Index: stable/10/contrib/libarchive/NEWS =================================================================== --- stable/10/contrib/libarchive/NEWS (revision 349524) +++ stable/10/contrib/libarchive/NEWS (revision 349525) @@ -1,709 +1,713 @@ +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 compression in zip archives +Jan 20, 2019: Support for xz, lzma, ppmd8 and bzip2 decompression in ZIP files Oct 06, 2018: RAR 5.0 reader Sep 03, 2018: libarchive 3.3.3 released Jul 19, 2018: Avoid super-linear slowdown on malformed mtree files Jan 27, 2018: Many fixes for building with Visual Studio Oct 19, 2017: NO_OVERWRITE doesn't change existing directory attributes Aug 12, 2017: New support for Zstandard read and write filters Jul 09, 2017: libarchive 3.3.2 released Mar 16, 2017: NFSv4 ACL support for Linux (librichacl) Feb 26, 2017: libarchive 3.3.1 released Security & Feature release Feb 19, 2017: libarchive 3.3.0 released Security & Feature release Jan 29, 2017: Limited NFSv4 ACL support for Mac OS (Darwin) Jan 10, 2017: POSIX.1e and NFSv4 ACL support for Solaris and derivates Dec 27, 2016: NFSv4 ACL read and write support for pax Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w() Nov, 2016: libarchive is now being tested by the OSS-Fuzz project Oct 26, 2016: Remove liblzmadec support Oct 23, 2016: libarchive 3.2.2 released Security release Jun 20, 2016: libarchive 3.2.1 released This fixes a handful of security and other critical issues with 3.2.0 May 01, 2016: libarchive 3.2.0 released Apr 09, 2016: libarchive 3.1.901a released Another test release in preparation for 3.2.0 Feb 13, 2016: libarchive 3.1.900a released This is a test release in preparation for 3.2.0 Oct 21, 2015: Preliminary port to OSF Apr 11, 2015: libarchive's issue tracker is now hosted at GitHub. https://github.com/libarchive/libarchive/issues Early 2015: Many fixes to crash and overflow bugs thanks to Hanno Boeck Oct 13, 2014: Zip encryption and decryption support Aug 13, 2014: Add support for lz4 compression. Jun 10, 2014: Add warc format support May 3, 2014: Add experimental Zip streaming extension Apr 6, 2014: Add bsdcat command-line tool Jan 12, 2014: Add Zip64 support Dec 1, 2013: Rewrite Zip write logic Jul 1, 2013: Add ability to detect encrypted entries for many formats (This does not add the ability to *decrypt* those entries, however) Feb 23, 2013: "raw" write support added Feb 09, 2013: libarchive 3.1.2 released Jan 28, 2013: libarchive's new website moved to http://www.libarchive.org. Jan 13, 2013: libarchive 3.1.1 released Jan 13, 2013: libarchive 3.1.0 released Dec 07, 2012: Implement functions to manually set the format and filters used. Nov 11, 2012: Add support for __MACOSX directory in Zip archives, which resource forks are stored in. Oct 20, 2012: Add support for writing v7 tar format. Oct 09, 2012: Add support for grzip compression. Oct 07, 2012: Introduce b64encode filter. Oct 07, 2012: Introduce uuencode filter. Oct 06, 2012: Add support for lzop. Sep 27, 2012: Implement function used to seek within data blocks. (Currently only supported for uncompressed RAR archives). Apr 22, 2012: Add basic archive read and write filter support for lrzip. Mar 27, 2012: libarchive 3.0.4 released Feb 05, 2012: libarchive development now hosted at GitHub. http://libarchive.github.com/ Feb 05, 2012: libarchive's issue tracker remains at Google Code. http://code.google.com/p/libarchive/issues/list Feb 05, 2012: libarchive's mailing lists remain at Google Groups. Dec 24, 2011: libarchive 3.0.2 released Dec 23, 2011: Various fixes merged from FreeBSD Dec 23, 2011: Symlink support in Zip reader and writer Dec 23, 2011: Robustness fixes to 7Zip reader Nov 27, 2011: libarchive 3.0.1b released Nov 26, 2011: 7Zip reader Nov 26, 2011: Small fixes to ISO and Zip to improve robustness with corrupted input Nov 24, 2011: Improve streaming Zip reader's support for uncompressed entries Nov 20, 2011: New seeking Zip reader supports SFX Zip archives Nov 20, 2011: Build fixes on Windows Nov 13, 2011: libarchive 3.0.0a released Nov 06, 2011: Update shared-library version calculations for libarchive 3.x Sep 04, 2011: Fix tar -s; follow GNU tar for controlling hardlink/symlink substitutions Aug 18, 2011: Fix reading ISO images built by NetBSD's mkisofs Aug 15, 2011: Old archive_read_support_compression_XXX functions are deprecated and will disappear in libarchive 4.0. Jun 26, 2011: RAR reader Jun 16, 2011: Add tar:compat-2x option to emulate broken libarchive 2.x handling of pax UTF-8 headers Apr 25, 2011: Refactor read_open() into a collection of single-item setters; support the old interfaces as wrappers Apr 12, 2011: Split disk writer into separate POSIX and Windows implementations Apr 10, 2011: Improvements to character translations on Windows. Mar 30, 2011: More work to return errors instead of calling abort() Mar 23, 2011: Add charset option to many writers to control MBCS filenames Mar 17, 2011: Overhauled support for per-format extension options Mar 17, 2011: Track character set used for mbcs strings, support translating to/from user-specified locale Mar 09, 2011: Recognize mtree files without requiring a signature Mar 06, 2011: Use iconv to convert to/from Unicode instead of making bad assumptions about the C90 character set translation functions Feb 17, 2011: Fixes for AIX, TRU64, and other platforms Dec 22, 2010: CAB reader Dec 20, 2010: LHA/LZH reader Jul 03, 2010: minitar example demonstrates archive_read_disk directory traversal Jun 29, 2010: Many improvements to ISO reader compatibility Jun 26, 2010: Use larger buffers when copy files into an archive Jun 18, 2010: Reimplement Mac OS extensions in libarchive Jun 09, 2010: archive_read_disk now supports traversals May 28, 2010: XAR writer May 16, 2010: Fix ^T handling; don't exit on interrupted reads and writes May 09, 2010: Improved detection of platform-specific crypto support May 04, 2010: lzip read and write filters May 01, 2010: New options: tar --gid --gname --uid --uname Apr 28, 2010: Use Red-black tree for ISO reader/writer to improve performance Apr 17, 2010: Minimal writer for legacy GNU tar format Mar 12, 2010: Don't dereference symlinks on Linux when reading ACLs. Mar 06, 2010: Fix build when an older libarchive is already installed Feb 28, 2010: Relax handling of state failures; misuse by clients now generally results in a sticky ARCHIVE_FATAL rather than a visit to abort() Feb 25, 2010: ISO writer Feb 21, 2010: Split many man pages into smaller chunks. Feb 21, 2010: Performance: Cheat on block sizes when reading archives from disk. Feb 21, 2010: Use int64_t instead of off_t, dev_t, ino_t, uid_t, and gid_t Feb 20, 2010: Document new ACL functions. Feb 19, 2010: Support multiple write filters Feb 07, 2010: Remove some legacy libarchive 1.x APIs Feb 04, 2010: Read afio headers Feb 02, 2010: Archive sparse files compatibly with GNU tar Feb 01, 2010: Integrate Apple extensions for Mac OS extended attributes into bsdtar Jan 31, 2010: Support cpio -V Feb 04, 2010: libarchive 2.8.0 released Jan 17, 2010: Fix error handling for 'echo nonexistent | cpio -o' Jan 17, 2010: Don't use futimes() on Cygwin Jan 02, 2010: libarchive 2.7.902a released (test release for 2.8) Jan 02, 2010: Fix tar/test/test_windows on MinGW Jan 02, 2010: Fix memory leaks in libarchive tests Jan 01, 2010: Fix memory leak when filter startup fails Dec 27, 2009: libarchive 2.7.901a released (test release for 2.8) Aug 04, 2009: libarchive 2.7.1 released Jul 20, 2009: Suppress bogus warning about unxz Jul 19, 2009: Support Cygwin 1.7 Jun 11, 2009: Support lzma/xz files compressed with larger buffer sizes. May 24, 2009: Handle gzip files signed with OpenBSD "gzsig" program. May 07, 2009: Avoid false failures when reading from pipe. Apr 16, 2009: libarchive 2.7.0 released Apr 10, 2009: libarchive 2.6.992a released Apr 09, 2009: Fix SIGPIPE issue building with MSVC. Apr 09, 2009: Fix several minor memory leaks in libarchive and libarchive_test Apr 08, 2009: libarchive 2.6.991a released Apr 07, 2009: Additional tests added to bsdcpio_test Apr 01, 2009: libarchive 2.6.990a released Apr 01, 2009: Use command-line gunzip, bunzip2, unxz, unlzma for decompression if the library is built without suitable libraries. The setup functions return ARCHIVE_WARN in this case so clients can adapt if necessary. Apr 01, 2009: Use getpw*_r and getgr*_r functions for thread-safety. Mar 24, 2009: Add archive_read_next_header2(), which is up to 25% more efficient for some clients; from Brian Harring. Mar 22, 2009: PDF versions of manpages are now included in the distribution. Mar, 2009: Major work to improve Cygwin build by Charles Wilson. Feb/Mar, 2009: Major work on cmake build support, mostly by Michihiro NAKAJIMA. Feb/Mar, 2009: Major work on Visual Studio support by Michihiro NAKAJIMA. All tests now pass. Feb 25, 2009: Fix Debian Bug #516577 Feb 21, 2009: Yacc is no longer needed to build; date parser rewritten in C. Jan/Feb, 2009: Mtree work by Michihiro. Feb, 2009: Joliet support by Andreas Henriksson. Jan/Feb, 2009: New options framework by Michihiro. Feb, 2009: High-res timestamps on Tru64, AIX, and GNU Hurd, by Björn Jacke. Jan 18, 2009: Extended attributes work on FreeBSD and Linux now with pax format. Jan 07, 2009: New archive_read_disk_entry_from_file() knows about ACLs, extended attributes, etc so that bsdtar and bsdcpio don't require such system-specific knowledge. Jan 03, 2009: Read filter system extensively refactored. In particular, read filter pipelines are now built out automatically and individual filters should be much easier to implement. Documentation on the Googlecode Wiki explains how to implement new filters. Dec 28, 2008: Many Windows/Visual Studio fixes from Michihiro NAKAJIMA. Dec 28, 2008: Main libarchive development moved from FreeBSD Perforce server to Google Code. This should make it easier for more people to participate in libarchive development. Dec 28, 2008: libarchive 2.6.0 released Dec 25, 2008: libarchive 2.5.905a released Dec 10, 2008: libarchive 2.5.904a released Dec 04, 2008: libarchive 2.5.903a released Nov 09, 2008: libarchive 2.5.902a released Nov 08, 2008: libarchive 2.5.901a released Nov 08, 2008: Start of pre-release testing for libarchive 2.6 Nov 07, 2008: Read filter refactor: The decompression routines just consume and produce arbitrarily-sized blocks. The reblocking from read_support_compression_none() has been pulled into the read core. Also, the decompression bid now makes multiple passes and stacks read filters. Oct 21, 2008: bsdcpio: New command-line parser. Oct 19, 2008: Internal read_ahead change: short reads are now an error Oct 06, 2008: bsdtar: option parser no longer uses getopt_long(), gives consistent option parsing on all platforms. Sep 19, 2008: Jaakko Heinonen: shar utility built on libarchive Sep 17, 2008: Pedro Giffuni: birthtime support Sep 17, 2008: Miklos Vajna: lzma reader and test. Note: I still have some concerns about the auto-detection (LZMA file format doesn't support auto-detection well), so this is not yet enabled under archive_read_support_compression_all(). For now, you must call archive_read_support_compression_lzma() if you want LZMA read support. Sep 11, 2008: Ivailo Petrov: Many fixes to Windows build, new solution files Jul 26, 2008: archive_entry now tracks which values have not been set. This helps zip extraction (file size is often "unknown") and time restores (tar usually doesn't know atime). Jul 26, 2008: Joerg Sonnenberger: Performance improvements to shar writer Jul 25, 2008: Joerg Sonnenberger: mtree write support Jul 02, 2008: libarchive 2.5.5 released Jul 02, 2008: libarchive 2.5.5b released Jul 01, 2008: bsdcpio is being used by enough people, we can call it 1.0.0 now Jun 20, 2008: bsdcpio: If a -l link fails with EXDEV, copy the file instead Jun 19, 2008: bsdcpio: additional long options for better GNU cpio compat Jun 15, 2008: Many small portability and bugfixes since 2.5.4b. May 25, 2008: libarchive 2.5.4b released May 21, 2008: Joerg Sonnenberger: fix bsdtar hardlink handling for newc format May 21, 2008: More progress on Windows building. Thanks to "Scott" for the Windows makefiles, thanks to Kees Zeelenberg for code contributions. May 21, 2008: Fix a number of non-exploitable integer and buffer overflows, thanks to David Remahl at Apple for pointing these out. May 21, 2008: Colin Percival: SIGINFO or SIGUSR1 to bsdtar prints progress info May 16, 2008: bsdtar's test harness no longer depends on file ordering. This was causing spurious test failures on a lot of systems. Thanks to Bernhard R. Link for the diagnosis. May 14, 2008: Joerg Sonnenberger: -s substitution support for bsdtar May 13, 2008: Joerg Sonnenberger: Many mtree improvements May 11, 2008: Joerg Sonnenberger: fix hardlink extraction when hardlinks have different permissions from original file April 30, 2008: Primary libarchive work has been moved into the FreeBSD project's Perforce repository: http://perforce.freebsd.org/ The libarchive project can be browsed at //depot/user/kientzle/libarchive-portable Direct link: http://preview.tinyurl.com/46mdgr May 04, 2008: libarchive 2.5.3b released * libarchive: Several fixes to link resolver to address bsdcpio crashes * bsdcpio: -p hardlink handling fixes * tar/pax: Ensure ustar dirnames end in '/'; be more careful about measuring filenames when deciding what pathname fields to use * libarchive: Mark which entry strings are set; be accurate about distinguishing empty strings ("") from unset ones (NULL) * tar: Don't crash reading entries with empty filenames * libarchive_test, bsdtar_test, bsdcpio_test: Better defaults: run all tests, delete temp dirs, summarize repeated failures * -no-undefined to libtool for Cygwin * libarchive_test: Skip large file tests on systems with 32-bit off_t * iso9660: Don't bother trying to find the body of an empty file; this works around strange behavior from some ISO9660 writers * tar: allow -r -T to be used together * tar: allow --format with -r or -u * libarchive: Don't build archive.h May 04, 2008: Simplified building: archive.h is no longer constructed This may require additional #if conditionals on some platforms. Mar 30, 2008: libarchive 2.5.1b released Mar 15, 2008: libarchive 2.5.0b released Mar 15, 2008: bsdcpio now seems to correctly write hardlinks into newc, ustar, and old cpio archives. Just a little more testing before bsdcpio 1.0 becomes a reality. Mar 15, 2008: I think the new linkify() interface is finally handling all known hardlink strategies. Mar 15, 2008: Mtree read fixes from Joerg Sonnenberger. Mar 15, 2008: Many new bsdtar and bsdcpio options from Joerg Sonnenberger. Mar 15, 2008: test harnesses no longer require uudecode; they now have built-in decoding logic that decodes the reference files as they are needed. Mar 14, 2008: libarchive 2.4.14 released; identical to 2.4.13 except for a point fix for gname/uname mixup in pax format that was introduced with the UTF-8 fixes. Feb 26, 2008: libarchive 2.4.13 released Feb 25, 2008: Handle path, linkname, gname, or uname that can't be converted to/from UTF-8. Implement "hdrcharset" attribute from SUS-2008. Feb 25, 2008: Fix name clash on NetBSD. Feb 18, 2008: Fix writing empty 'ar' archives, per Kai Wang Feb 18, 2008: [bsdtar] Permit appending on block devices. Feb 09, 2008: New "linkify" resolver to help with newc hardlink writing; bsdcpio still needs to be converted to use this. Feb 02, 2008: Windows compatibility fixes from Ivailo Petrov, Kees Zeelenberg Jan 30, 2008: Ignore hardlink size for non-POSIX tar archives. Jan 22, 2008: libarchive 2.4.12 released Jan 22, 2008: Fix bad padding when writing symlinks to newc cpio archives. Jan 22, 2008: Verify bsdcpio_test by getting it to work against GNU cpio 2.9. bsdcpio_test complains about missing options (-y and -z), format of informational messages (--version, --help), and a minor formatting issue in odc format output. After this update, bsdcpio_test uncovered several more cosmetic issues in bsdcpio, all now fixed. Jan 22, 2008: Experimental support for self-extracting Zip archives. Jan 22, 2008: Extend hardlink restore strategy to work correctly with hardlinks extracted from newc cpio files. (Which store the body only with the last occurrence of a link.) Dec 30, 2007: libarchive 2.4.11 released Dec 30, 2007: Fixed a compile error in bsdcpio on some systems. Dec 29, 2007: libarchive 2.4.10 released Dec 29, 2007: bsdcpio 0.9.0 is ready for wider use. Dec 29, 2007: Completed initial test harness for bsdcpio. Dec 22, 2007: libarchive 2.4.9 released Dec 22, 2007: Implement the remaining options for bsdcpio: -a, -q, -L, -f, pattern selection for -i and -it. Dec 13, 2007: libarchive 2.4.8 released Dec 13, 2007: gzip and bzip2 compression now handle zero-byte writes correctly, Thanks to Damien Golding for bringing this to my attention. Dec 12, 2007: libarchive 2.4.7 released Dec 10, 2007: libarchive 2.4.6 released Dec 09, 2007: tar/test/test_copy.c verifies "tar -c | tar -x" copy pipeline Dec 07, 2007: Fix a couple of minor memory leaks. Dec 04, 2007: libarchive 2.4.5 released Dec 04, 2007: Fix cpio/test/test_write_odc by setting the umask first. Dec 03, 2007: libarchive 2.4.4 released Dec 03, 2007: New configure options --disable-xattr and --disable-acl, thanks to Samuli Suominen. Dec 03, 2007: libarchive 2.4.3 released Dec 03, 2007: Thanks to Lapo Luchini for sending me a ZIP file that libarchive couldn't handle. Fixed a bug in handling of "length at end" flags in ZIP files. Dec 03, 2007: Fixed bsdcpio -help, bsdtar -help tests. Dec 02, 2007: First cut at real bsdtar test harness. Dec 02, 2007: libarchive 2.4.2 released Dec 02, 2007: libarchive 2.4.1 released Dec 02, 2007: Minor fixes, rough cut of mdoc-to-man conversion for man pages. Oct 30, 2007: libarchive 2.4.0 released Oct 30, 2007: Minor compile fix thanks to Joerg Schilling. Oct 30, 2007: Only run the format auction once at the beginning of the archive. This is simpler and supports better error recovery. Oct 29, 2007: Test support for very large entries in tar archives: libarchive_test now exercises entries from 2GB up to 1TB. Oct 27, 2007: libarchive 2.3.5 released Oct 27, 2007: Correct some unnecessary internal data copying in the "compression none" reader and writer; this reduces user time by up to 2/3 in some tests. (Thanks to Jan Psota for publishing his performance test results to GNU tar's bug-tar mailing list; those results pointed me towards this problem.) Oct 27, 2007: Fix for skipping archive entries that are exactly a multiple of 4G on 32-bit platforms. Oct 25, 2007: Fix for reading very large (>8G) tar archives; this was broken when I put in support for new GNU tar sparse formats. Oct 20, 2007: Initial work on new pattern-matching code for cpio; I hope this eventually replaces the code currently in bsdtar. Oct 08, 2007: libarchive 2.3.4 released Oct 05, 2007: Continuing work on bsdcpio test suite. Oct 05, 2007: New cpio.5 manpage, updates to "History" of bsdcpio.1 and bsdtar.1 manpages. Oct 05, 2007: Fix zip reader to immediately return EOF if you try to read body of non-regular file. In particular, this fixes bsdtar extraction of zip archives. Sep 30, 2007: libarchive 2.3.3 released Sep 26, 2007: Rework Makefile.am so that the enable/disable options actually do the right things. Sep 26, 2007: cpio-odc and cpio-newc archives no longer write bodies for non-regular files. Sep 26, 2007: Test harness for bsdcpio is in place, needs more tests written. This is much nicer than the ragtag collection of test scripts that bsdtar has. Sep 20, 2007: libarchive 2.3.2 released Sep 20, 2007: libarchive 2.3.1 broke bsdtar because the archive_write_data() fix was implemented incorrectly. Sep 16, 2007: libarchive 2.3.1 released Sep 16, 2007: Many fixes to bsdcpio 0.3: handle hardlinks with -p, recognize block size on writing, fix a couple of segfaults. Sep 16, 2007: Fixed return value from archive_write_data() when used with archive_write_disk() to match the documentation and other instances of this same function. Sep 15, 2007: Add archive_entry_link_resolver, archive_entry_strmode Sep 11, 2007: libarchive 2.2.8 released Sep 09, 2007: bsdcpio 0.2 supports most (not yet all) of the old POSIX spec. Sep 01, 2007: libarchive 2.2.7 released Aug 31, 2007: Support for reading mtree files, including an mtree.5 manpage (A little experimental still.) Aug 18, 2007: Read gtar 1.17 --posix --sparse entries. Aug 13, 2007: Refined suid/sgid restore handling; it is no longer an error if suid/sgid bits are dropped when you request perm restore but don't request owner restore. Aug 06, 2007: Use --enable-bsdcpio if you want to try bsdcpio Aug 05, 2007: libarchive 2.2.6 released Aug 05, 2007: New configure option --disable-bsdtar, thanks to Joerg Sonnenberger. Aug 05, 2007: Several bug fixes from FreeBSD CVS repo. Jul 13, 2007: libarchive 2.2.5 released Jul 12, 2007: libarchive 2.2.4 released Jul 12, 2007: Thanks to Colin Percival's help in diagnosing and fixing several critical security bugs. Details available at http://security.freebsd.org/advisories/FreeBSD-SA-07:05.libarchive.asc May 26, 2007: libarchive 2.2.3 released May 26, 2007: Fix memory leaks in ZIP reader and shar writer, add some missing system headers to archive_entry.h, dead code cleanup from Colin Percival, more tests for gzip/bzip2, fix an EOF anomaly in bzip2 decompression. May 12, 2007: libarchive 2.2.2 released May 12, 2007: Fix archive_write_disk permission restore by cloning entry passed into write_header so that permission info is still available at finish_entry time. (archive_read_extract() worked okay because it held onto the passed-in entry, but direct consumers of archive_write_disk would break). This required fixing archive_entry_clone(), which now works and has a reasonably complete test case. May 10, 2007: Skeletal cpio implementation. May 06, 2007: libarchive 2.2.1 released May 06, 2007: Flesh out a lot more of test_entry.c so as to catch problems such as the device node breakage before releasing . May 05, 2007: Fix a bad bug introduced in 2.1.9 that broke device node entries in tar archives. May 03, 2007: Move 'struct stat' out of archive_entry core as well. This removes some portability headaches and fixes a bunch of corner cases that arise when manipulating archives on dissimilar systems. Apr 30, 2007: libarchive 2.1.10 released Apr 31, 2007: Minor code cleanup. Apr 24, 2007: libarchive 2.1.9 released Apr 24, 2007: Fix some recently-introduced problems with libraries (Just let automake handle it and it all works much better.) Finish isolating major()/minor()/makedev() in archive_entry.c. Apr 23, 2007: libarchive 2.1.8 released Apr 23, 2007: Minor fixes found from building on MacOS X Apr 22, 2007: libarchive 2.1.7 released Apr 22, 2007: Eliminated all uses of 'struct stat' from the format readers/writers. This should improve portability; 'struct stat' is now only used in archive_entry and in code that actually touches the disk. Apr 17, 2007: libarchive 2.1.6 released Libarchive now compiles and passes all tests on Interix. Apr 16, 2007: libarchive 2.1.5 released Apr 15, 2007: libarchive 2.1b2 released Apr 15, 2007: New libarchive_internals.3 documentation of internal APIs. Not complete, but should prove helpful. Apr 15, 2007: Experimental "read_compress_program" and "write_compress_program" for using libarchive with external compression. Not yet well tested, and likely has portability issues. Feedback appreciated. Apr 14, 2007: libarchive 2.0.31 released Apr 14, 2007: More fixes for Interix, more 'ar' work Apr 14, 2007: libarchive 2.0.30 released Apr 13, 2007: libarchive now enforces trailing '/' on dirs written to tar archives Apr 11, 2007: libarchive 2.0.29 released Apr 11, 2007: Make it easier to statically configure for different platforms. Apr 11, 2007: Updated config.guess, config.sub, libtool Apr 06, 2007: libarchive 2.0.28 released Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang. Apr 01, 2007: libarchive 2.0.27 released Mar 31, 2007: Several minor fixes from Colin Percival and Joerg Sonnenberger. Mar 12, 2007: libarchive 2.0.25 released Mar 12, 2007: Fix broken --unlink flag. Mar 11, 2007: libarchive 2.0.24 released Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry that refers to a non-existent user or group to not be restored correctly. The fix both makes the parser more tolerant (so that archives created with the buggy ACLs can be read now) and corrects the ACL formatter. Mar 10, 2007: More work on test portability to Linux. Mar 10, 2007: libarchive 2.0.22 released Mar 10, 2007: Header cleanups; added linux/fs.h, removed some unnecessary headers, added #include guards in bsdtar. If you see any obvious compile failures from this, let me know. Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough to enable as part of "make check", but getting better. Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when a header write fails in a way that only affects this item. Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN. Mar 07, 2007: libarchive 2.0.21 released Mar 07, 2007: Add some ACL tests (only for the system-independent portion of the ACL support for now). Mar 07, 2007: tar's ability to read ACLs off disk got turned off for FreeBSD; re-enable it. (ACL restores and libarchive support for storing/reading ACLs from pax archives was unaffected.) Mar 02, 2007: libarchive 2.0.20 released Mar 2, 2007: It's not perfect, but it's pretty good. Libarchive 2.0 is officially out of beta. Feb 28, 2007: libarchive 2.0b17 released Feb 27, 2007: Make the GID restore checks more robust by checking whether the current user has too few or too many privileges. Feb 26, 2007: libarchive 2.0b15 released Feb 26, 2007: Don't lose symlinks when extracting from ISOs. Thanks to Diego "Flameeyes" Pettenò for telling me about the broken testcase on Gentoo that (finally!) led me to the cause of this long-standing bug. Feb 26, 2007: libarchive 2.0b14 released Feb 26, 2007: Fix a broken test on platforms that lack lchmod(). Feb 25, 2007: libarchive 2.0b13 released Feb 25, 2007: Empty archives were being written as empty files, without a proper end-of-archive marker. Fixed. Feb 23, 2007: libarchive 2.0b12 released Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT and _EXTRACT_SECURE_SYMLINK. These checks used to be in bsdtar, but they belong down in libarchive where they can be used by other tools and where they can be better optimized. Feb 11, 2007: libarchive 2.0b11 released Feb 10, 2007: Fixed a bunch of errors in libarchive's handling of EXTRACT_PERM and EXTRACT_OWNER, especially relating to SUID and SGID bits. Jan 31, 2007: libarchive 2.0b9 released Jan 31, 2007: Added read support for "empty" archives as a distinct archive format. Bsdtar uses this to handle, e.g., "touch foo.tar; tar -rf foo.tar" Jan 22, 2007: libarchive 2.0b6 released Jan 22, 2007: archive_write_disk API is now in place. It provides a finer-grained interface than archive_read_extract. In particular, you can use it to create objects on disk without having an archive around (just feed it archive_entry objects describing what you want to create), you can override the uname/gname-to-uid/gid lookups (minitar uses this to avoid getpwXXX() and getgrXXX() bloat). Jan 09, 2007: libarchive 2.0a3 released Jan 9, 2007: archive_extract is now much better; it handles the most common cases with a minimal number of system calls. Some features still need a lot of testing, especially corner cases involving objects that already exist on disk. I expect the next round of API overhaul will simplify building test cases. Jan 9, 2007: a number of fixes thanks to Colin Percival, especially corrections to the skip() framework and handling of large files. Jan 9, 2007: Fixes for large ISOs. The code should correctly handle very large ISOs with entries up to 4G. Thanks to Robert Sciuk for pointing out these issues. Sep 05, 2006: libarchive 1.3.1 released Sep 5, 2006: Bump version to 1.3 for new I/O wrappers. Sep 4, 2006: New memory and FILE read/write wrappers. Sep 4, 2006: libarchive test harness is now minimally functional; it's located a few minor bugs in error-handling logic Aug 17, 2006: libarchive 1.2.54 released Aug 17, 2006: Outline ABI changes for libarchive 2.0; these are protected behind #ifdef's until I think I've found everything that needs to change. Aug 17, 2006: Fix error-handling in archive_read/write_close() They weren't returning any errors before. Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set Fixes a bug adding files when writing archive to pipe or when using archive_write_open() directly. Jul 2006: New "skip" handling improves performance extracting single files from large uncompressed archives. Mar 21, 2006: 1.2.52 released Mar 21, 2006: Fix -p on platforms that don't have platform-specific extended attribute code. Mar 20, 2006: Add NEWS file; fill in some older history from other files. I'll try to keep this file up-to-date from now on. OLDER NEWS SUMMARIES Mar 19, 2006: libarchive 1.2.51 released Mar 18, 2006: Many fixes to extended attribute support, including a redesign of the storage format to simplify debugging. Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth spending much time on. Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support for extended attributes (Currently Linux-only.). Mar 11, 2006: Reorganized distribution package: There is now one tar.gz file that builds both libarchive and bsdtar. Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write Pax attribute entry names. Nov 7, 2005: Experimental 'tp' format support in libarchive. Feedback appreciated; this is not enabled by archive_read_support_format_all() yet as I'm not quite content with the format detection heuristics. Nov 7, 2005: Some more portability improvements thanks to Darin Broady, minor bugfixes. Oct 12, 2005: Use GNU libtool to build shared libraries on many systems. Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs. Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows: http://gnuwin32.sourceforge.net/ Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson. -L/-h fix from Jaakko Heinonen. Mar 12, 2005: archive_read_extract can now handle very long pathnames (I've tested with pathnames up to 1MB). Mar 12, 2005: Marcus Geiger has written an article about libarchive http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/ including examples of using it from Objective-C. His MoinX http://moinx.antbear.org/ desktop Wiki uses libarchive for archiving and restoring Wiki pages. Jan 22, 2005: Preliminary ZIP extraction support, new directory-walking code for bsdtar. Jan 16, 2005: ISO9660 extraction code added; manpage corrections. May 22, 2004: Many gtar-compatible long options have been added; almost all FreeBSD ports extract correctly with bsdtar. May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar, and pdtar archives. Index: stable/10/contrib/libarchive/README.md =================================================================== --- stable/10/contrib/libarchive/README.md (revision 349524) +++ stable/10/contrib/libarchive/README.md (revision 349525) @@ -1,223 +1,224 @@ # 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 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 * **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 fomats: * 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) * 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 * 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 * 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. * 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. Index: stable/10/contrib/libarchive/libarchive/archive.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive.h (revision 349524) +++ stable/10/contrib/libarchive/libarchive/archive.h (revision 349525) @@ -1,1195 +1,1195 @@ /*- * 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 3003003 +#define ARCHIVE_VERSION_NUMBER 3004000 #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__) # include #endif /* Get appropriate definitions of 64-bit integer */ #if !defined(__LA_INT64_T_DEFINED) /* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_INT64_T la_int64_t # endif #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; # else # include /* ssize_t */ # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; # else typedef int64_t la_int64_t; # endif # endif #endif /* The la_ssize_t should match the type used in 'struct stat' */ #if !defined(__LA_SSIZE_T_DEFINED) /* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_SSIZE_T la_ssize_t # endif #define __LA_SSIZE_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) typedef ssize_t la_ssize_t; # elif defined(_WIN64) typedef __int64 la_ssize_t; # else typedef long la_ssize_t; # endif # else # include /* ssize_t */ typedef ssize_t la_ssize_t; # endif #endif /* Large file support for Android */ #ifdef __ANDROID__ #include "android_lf.h" #endif /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ #if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries or non-Windows needs no special declaration. */ # define __LA_DECL #endif #if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__) #define __LA_PRINTF(fmtarg, firstvararg) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) #else #define __LA_PRINTF(fmtarg, firstvararg) /* nothing */ #endif #if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 # define __LA_DEPRECATED __attribute__((deprecated)) #else # define __LA_DEPRECATED #endif #ifdef __cplusplus extern "C" { #endif /* * The version number is provided as both a macro and a function. * The macro identifies the installed header; the function identifies * the library version (which may not be the same if you're using a * dynamically-linked version of the library). Of course, if the * header and library are very different, you should expect some * strangeness. Don't do that. */ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.3.3" +#define ARCHIVE_VERSION_ONLY_STRING "3.4.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); /* Switches from one client data object to the next/prev client data object. * This is useful for reading from different data blocks such as a set of files * that make up one large file. */ typedef int archive_switch_callback(struct archive *, void *_client_data1, void *_client_data2); /* * Returns a passphrase used for encryption or decryption, NULL on nothing * to do and give it up. */ typedef const char *archive_passphrase_callback(struct archive *, void *_client_data); /* * Codes to identify various stream filters. */ #define ARCHIVE_FILTER_NONE 0 #define ARCHIVE_FILTER_GZIP 1 #define ARCHIVE_FILTER_BZIP2 2 #define ARCHIVE_FILTER_COMPRESS 3 #define ARCHIVE_FILTER_PROGRAM 4 #define ARCHIVE_FILTER_LZMA 5 #define ARCHIVE_FILTER_XZ 6 #define ARCHIVE_FILTER_UU 7 #define ARCHIVE_FILTER_RPM 8 #define ARCHIVE_FILTER_LZIP 9 #define ARCHIVE_FILTER_LRZIP 10 #define ARCHIVE_FILTER_LZOP 11 #define ARCHIVE_FILTER_GRZIP 12 #define ARCHIVE_FILTER_LZ4 13 #define ARCHIVE_FILTER_ZSTD 14 #if ARCHIVE_VERSION_NUMBER < 4000000 #define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE #define ARCHIVE_COMPRESSION_GZIP ARCHIVE_FILTER_GZIP #define ARCHIVE_COMPRESSION_BZIP2 ARCHIVE_FILTER_BZIP2 #define ARCHIVE_COMPRESSION_COMPRESS ARCHIVE_FILTER_COMPRESS #define ARCHIVE_COMPRESSION_PROGRAM ARCHIVE_FILTER_PROGRAM #define ARCHIVE_COMPRESSION_LZMA ARCHIVE_FILTER_LZMA #define ARCHIVE_COMPRESSION_XZ ARCHIVE_FILTER_XZ #define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU #define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM #define ARCHIVE_COMPRESSION_LZIP ARCHIVE_FILTER_LZIP #define ARCHIVE_COMPRESSION_LRZIP ARCHIVE_FILTER_LRZIP #endif /* * Codes returned by archive_format. * * Top 16 bits identifies the format family (e.g., "tar"); lower * 16 bits indicate the variant. This is updated by read_next_header. * Note that the lower 16 bits will often vary from entry to entry. * In some cases, this variation occurs as libarchive learns more about * the archive (for example, later entries might utilize extensions that * weren't necessary earlier in the archive; in this case, libarchive * will change the format code to indicate the extended format that * was used). In other cases, it's because different tools have * modified the archive and so different parts of the archive * actually have slightly different formats. (Both tar and cpio store * format codes in each entry, so it is quite possible for each * entry to be in a different format.) */ #define ARCHIVE_FORMAT_BASE_MASK 0xff0000 #define ARCHIVE_FORMAT_CPIO 0x10000 #define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) #define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2) #define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) #define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) #define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) #define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6) #define ARCHIVE_FORMAT_SHAR 0x20000 #define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) #define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) #define ARCHIVE_FORMAT_TAR 0x30000 #define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1) #define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2) #define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3) #define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4) #define ARCHIVE_FORMAT_ISO9660 0x40000 #define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) #define ARCHIVE_FORMAT_ZIP 0x50000 #define ARCHIVE_FORMAT_EMPTY 0x60000 #define ARCHIVE_FORMAT_AR 0x70000 #define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) #define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) #define ARCHIVE_FORMAT_MTREE 0x80000 #define ARCHIVE_FORMAT_RAW 0x90000 #define ARCHIVE_FORMAT_XAR 0xA0000 #define ARCHIVE_FORMAT_LHA 0xB0000 #define ARCHIVE_FORMAT_CAB 0xC0000 #define ARCHIVE_FORMAT_RAR 0xD0000 #define ARCHIVE_FORMAT_7ZIP 0xE0000 #define ARCHIVE_FORMAT_WARC 0xF0000 #define ARCHIVE_FORMAT_RAR_V5 0x100000 /* * Codes returned by archive_read_format_capabilities(). * * This list can be extended with values between 0 and 0xffff. * The original purpose of this list was to let different archive * format readers expose their general capabilities in terms of * encryption. */ #define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */ #define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0) /* reader can detect encrypted data */ #define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1) /* reader can detect encryptable metadata (pathname, mtime, etc.) */ /* * Codes returned by archive_read_has_encrypted_entries(). * * In case the archive does not support encryption detection at all * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader * for some other reason (e.g. not enough bytes read) cannot say if * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW * is returned. */ #define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2 #define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1 /*- * Basic outline for reading an archive: * 1) Ask archive_read_new for an archive reader object. * 2) Update any global properties as appropriate. * In particular, you'll certainly want to call appropriate * archive_read_support_XXX functions. * 3) Call archive_read_open_XXX to open the archive * 4) Repeatedly call archive_read_next_header to get information about * successive archive entries. Call archive_read_data to extract * data for entries of interest. * 5) Call archive_read_free to end processing. */ __LA_DECL struct archive *archive_read_new(void); /* * The archive_read_support_XXX calls enable auto-detect for this * archive handle. They also link in the necessary support code. * For example, if you don't want bzlib linked in, don't invoke * support_compression_bzip2(). The "all" functions provide the * obvious shorthand. */ #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_read_support_compression_all(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_bzip2(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_compress(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_gzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_lzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_lzma(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_none(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program(struct archive *, const char *command) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program_signature (struct archive *, const char *, const void * /* match */, size_t) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_rpm(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_uu(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_xz(struct archive *) __LA_DEPRECATED; #endif __LA_DECL int archive_read_support_filter_all(struct archive *); __LA_DECL int archive_read_support_filter_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) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); __LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, struct archive * /* dest */); __LA_DECL void archive_read_extract_set_progress_callback(struct archive *, void (*_progress_func)(void *), void *_user_data); /* Record the dev/ino of a file that will not be written. This is * generally set to the dev/ino of the archive being read. */ __LA_DECL void archive_read_extract_set_skip_file(struct archive *, la_int64_t, la_int64_t); /* Close the file and release most resources. */ __LA_DECL int archive_read_close(struct archive *); /* Release all resources and destroy the object. */ /* Note that archive_read_free will call archive_read_close for you. */ __LA_DECL int archive_read_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_read_free() for backwards compatibility. */ __LA_DECL int archive_read_finish(struct archive *) __LA_DEPRECATED; #endif /*- * To create an archive: * 1) Ask archive_write_new for an archive writer object. * 2) Set any global properties. In particular, you should set * the compression and format to use. * 3) Call archive_write_open to open the file (most people * will use archive_write_open_file or archive_write_open_fd, * which provide convenient canned I/O callbacks for you). * 4) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to write the header * - archive_write_data to write the entry data * 5) archive_write_close to close the output * 6) archive_write_free to cleanup the writer and release resources */ __LA_DECL struct archive *archive_write_new(void); __LA_DECL int archive_write_set_bytes_per_block(struct archive *, int bytes_per_block); __LA_DECL int archive_write_get_bytes_per_block(struct archive *); /* XXX This is badly misnamed; suggestions appreciated. XXX */ __LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, int bytes_in_last_block); __LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); /* The dev/ino of a file that won't be archived. This is used * to avoid recursively adding an archive to itself. */ __LA_DECL int archive_write_set_skip_file(struct archive *, la_int64_t, la_int64_t); #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_write_set_compression_bzip2(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_compress(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_gzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_lzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_lzma(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_none(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_xz(struct archive *) __LA_DEPRECATED; #endif /* A convenience function to set the filter based on the code. */ __LA_DECL int archive_write_add_filter(struct archive *, int filter_code); __LA_DECL int archive_write_add_filter_by_name(struct archive *, const char *name); __LA_DECL int archive_write_add_filter_b64encode(struct archive *); __LA_DECL int archive_write_add_filter_bzip2(struct archive *); __LA_DECL int archive_write_add_filter_compress(struct archive *); __LA_DECL int archive_write_add_filter_grzip(struct archive *); __LA_DECL int archive_write_add_filter_gzip(struct archive *); __LA_DECL int archive_write_add_filter_lrzip(struct archive *); __LA_DECL int archive_write_add_filter_lz4(struct archive *); __LA_DECL int archive_write_add_filter_lzip(struct archive *); __LA_DECL int archive_write_add_filter_lzma(struct archive *); __LA_DECL int archive_write_add_filter_lzop(struct archive *); __LA_DECL int archive_write_add_filter_none(struct archive *); __LA_DECL int archive_write_add_filter_program(struct archive *, const char *cmd); __LA_DECL int archive_write_add_filter_uuencode(struct archive *); __LA_DECL int archive_write_add_filter_xz(struct archive *); __LA_DECL int archive_write_add_filter_zstd(struct archive *); /* A convenience function to set the format based on the code or name. */ __LA_DECL int archive_write_set_format(struct archive *, int format_code); __LA_DECL int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ __LA_DECL int archive_write_set_format_7zip(struct archive *); __LA_DECL int archive_write_set_format_ar_bsd(struct archive *); __LA_DECL int archive_write_set_format_ar_svr4(struct archive *); __LA_DECL int archive_write_set_format_cpio(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_gnutar(struct archive *); __LA_DECL int archive_write_set_format_iso9660(struct archive *); __LA_DECL int archive_write_set_format_mtree(struct archive *); __LA_DECL int archive_write_set_format_mtree_classic(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __LA_DECL int archive_write_set_format_pax(struct archive *); __LA_DECL int archive_write_set_format_pax_restricted(struct archive *); __LA_DECL int archive_write_set_format_raw(struct archive *); __LA_DECL int archive_write_set_format_shar(struct archive *); __LA_DECL int archive_write_set_format_shar_dump(struct archive *); __LA_DECL int archive_write_set_format_ustar(struct archive *); __LA_DECL int archive_write_set_format_v7tar(struct archive *); __LA_DECL int archive_write_set_format_warc(struct archive *); __LA_DECL int archive_write_set_format_xar(struct archive *); __LA_DECL int archive_write_set_format_zip(struct archive *); __LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename); __LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext); __LA_DECL int archive_write_zip_set_compression_deflate(struct archive *); __LA_DECL int archive_write_zip_set_compression_store(struct archive *); __LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); __LA_DECL int archive_write_open_fd(struct archive *, int _fd); __LA_DECL int archive_write_open_filename(struct archive *, const char *_file); __LA_DECL int archive_write_open_filename_w(struct archive *, const wchar_t *_file); /* A deprecated synonym for archive_write_open_filename() */ __LA_DECL int archive_write_open_file(struct archive *, const char *_file) __LA_DEPRECATED; __LA_DECL int archive_write_open_FILE(struct archive *, FILE *); /* _buffSize is the size of the buffer, _used refers to a variable that * will be updated after each write into the buffer. */ __LA_DECL int archive_write_open_memory(struct archive *, void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided * to archive_write_header or pad if the provided data is short. */ __LA_DECL int archive_write_header(struct archive *, struct archive_entry *); __LA_DECL la_ssize_t archive_write_data(struct archive *, const void *, size_t); /* This interface is currently only available for archive_write_disk handles. */ __LA_DECL la_ssize_t archive_write_data_block(struct archive *, const void *, size_t, la_int64_t); __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); /* Marks the archive as FATAL so that a subsequent free() operation * won't try to close() cleanly. Provides a fast abort capability * when the client discovers that things have gone wrong. */ __LA_DECL int archive_write_fail(struct archive *); /* This can fail if the archive wasn't already closed, in which case * archive_write_free() will implicitly call archive_write_close(). */ __LA_DECL int archive_write_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_write_free() for backwards compatibility. */ __LA_DECL int archive_write_finish(struct archive *) __LA_DEPRECATED; #endif /* * Set write options. */ /* Apply option to the format only. */ __LA_DECL int archive_write_set_format_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to the filter only. */ __LA_DECL int archive_write_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to both the format and the filter. */ __LA_DECL int archive_write_set_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_write_set_options(struct archive *_a, const char *opts); /* * Set a encryption passphrase. */ __LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p); __LA_DECL int archive_write_set_passphrase_callback(struct archive *, void *client_data, archive_passphrase_callback *); /*- * ARCHIVE_WRITE_DISK API * * To create objects on disk: * 1) Ask archive_write_disk_new for a new archive_write_disk object. * 2) Set any global properties. In particular, you probably * want to set the options. * 3) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to create the file/dir/etc on disk * - archive_write_data to write the entry data * 4) archive_write_free to cleanup the writer and release resources * * In particular, you can use this in conjunction with archive_read() * to pull entries out of an archive and create them on disk. */ __LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ __LA_DECL int archive_write_disk_set_skip_file(struct archive *, la_int64_t, la_int64_t); /* Set flags to control how the next item gets created. * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */ __LA_DECL int archive_write_disk_set_options(struct archive *, int flags); /* * The lookup functions are given uname/uid (or gname/gid) pairs and * return a uid (gid) suitable for this system. These are used for * restoring ownership and for setting ACLs. The default functions * are naive, they just return the uid/gid. These are small, so reasonable * for applications that don't need to preserve ownership; they * are probably also appropriate for applications that are doing * same-system backup and restore. */ /* * The "standard" lookup functions use common system calls to lookup * the uname/gname, falling back to the uid/gid if the names can't be * found. They cache lookups and are reasonably fast, but can be very * large, so they are not used unless you ask for them. In * particular, these match the specifications of POSIX "pax" and old * POSIX "tar". */ __LA_DECL int archive_write_disk_set_standard_lookup(struct archive *); /* * If neither the default (naive) nor the standard (big) functions suit * your needs, you can write your own and register them. Be sure to * include a cleanup function if you have allocated private data. */ __LA_DECL int archive_write_disk_set_group_lookup(struct archive *, void * /* private_data */, la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, void * /* private_data */, la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t); __LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t); /* * ARCHIVE_READ_DISK API * * This is still evolving and somewhat experimental. */ __LA_DECL struct archive *archive_read_disk_new(void); /* The names for symlink modes here correspond to an old BSD * command-line argument convention: -L, -P, -H */ /* Follow all symlinks. */ __LA_DECL int archive_read_disk_set_symlink_logical(struct archive *); /* Follow no symlinks. */ __LA_DECL int archive_read_disk_set_symlink_physical(struct archive *); /* Follow symlink initially, then not. */ __LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *); /* TODO: Handle Linux stat32/stat64 ugliness. */ __LA_DECL int archive_read_disk_entry_from_file(struct archive *, struct archive_entry *, int /* fd */, const struct stat *); /* Look up gname for gid or uname for uid. */ /* Default implementations are very, very stupid. */ __LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t); __LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t); /* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the * results for performance. */ __LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); /* You can install your own lookups if you like. */ __LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, la_int64_t), void (* /* cleanup_fn */)(void *)); __LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, la_int64_t), void (* /* cleanup_fn */)(void *)); /* Start traversal. */ __LA_DECL int archive_read_disk_open(struct archive *, const char *); __LA_DECL int archive_read_disk_open_w(struct archive *, const wchar_t *); /* * Request that current entry be visited. If you invoke it on every * directory, you'll get a physical traversal. This is ignored if the * current entry isn't a directory or a link to a directory. So, if * you invoke this on every returned path, you'll get a full logical * traversal. */ __LA_DECL int archive_read_disk_descend(struct archive *); __LA_DECL int archive_read_disk_can_descend(struct archive *); __LA_DECL int archive_read_disk_current_filesystem(struct archive *); __LA_DECL int archive_read_disk_current_filesystem_is_synthetic(struct archive *); __LA_DECL int archive_read_disk_current_filesystem_is_remote(struct archive *); /* Request that the access time of the entry visited by traversal be restored. */ __LA_DECL int archive_read_disk_set_atime_restored(struct archive *); /* * Set behavior. The "flags" argument selects optional behavior. */ /* Request that the access time of the entry visited by traversal be restored. * This is the same as archive_read_disk_set_atime_restored. */ #define ARCHIVE_READDISK_RESTORE_ATIME (0x0001) /* Default: Do not skip an entry which has nodump flags. */ #define ARCHIVE_READDISK_HONOR_NODUMP (0x0002) /* Default: Skip a mac resource fork file whose prefix is "._" because of * using copyfile. */ #define ARCHIVE_READDISK_MAC_COPYFILE (0x0004) /* Default: Traverse mount points. */ #define ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS (0x0008) /* Default: Xattrs are read from disk. */ #define ARCHIVE_READDISK_NO_XATTR (0x0010) /* Default: ACLs are read from disk. */ #define ARCHIVE_READDISK_NO_ACL (0x0020) /* Default: File flags are read from disk. */ #define ARCHIVE_READDISK_NO_FFLAGS (0x0040) __LA_DECL int archive_read_disk_set_behavior(struct archive *, int flags); /* * Set archive_match object that will be used in archive_read_disk to * know whether an entry should be skipped. The callback function * _excluded_func will be invoked when an entry is skipped by the result * of archive_match. */ __LA_DECL int archive_read_disk_set_matching(struct archive *, struct archive *_matching, void (*_excluded_func) (struct archive *, void *, struct archive_entry *), void *_client_data); __LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data); /* Simplified cleanup interface; * This calls archive_read_free() or archive_write_free() as needed. */ __LA_DECL int archive_free(struct archive *); /* * Accessor functions to read/set various information in * the struct archive object: */ /* Number of filters in the current filter pipeline. */ /* Filter #0 is the one closest to the format, -1 is a synonym for the * last filter, which is always the pseudo-filter that wraps the * client callbacks. */ __LA_DECL int archive_filter_count(struct archive *); __LA_DECL la_int64_t archive_filter_bytes(struct archive *, int); __LA_DECL int archive_filter_code(struct archive *, int); __LA_DECL const char * archive_filter_name(struct archive *, int); #if ARCHIVE_VERSION_NUMBER < 4000000 /* These don't properly handle multiple filters, so are deprecated and * will eventually be removed. */ /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */ __LA_DECL la_int64_t archive_position_compressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */ __LA_DECL la_int64_t archive_position_uncompressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */ __LA_DECL const char *archive_compression_name(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */ __LA_DECL int archive_compression(struct archive *) __LA_DEPRECATED; #endif __LA_DECL int archive_errno(struct archive *); __LA_DECL const char *archive_error_string(struct archive *); __LA_DECL const char *archive_format_name(struct archive *); __LA_DECL int archive_format(struct archive *); __LA_DECL void archive_clear_error(struct archive *); __LA_DECL void archive_set_error(struct archive *, int _err, const char *fmt, ...) __LA_PRINTF(3, 4); __LA_DECL void archive_copy_error(struct archive *dest, struct archive *src); __LA_DECL int archive_file_count(struct archive *); /* * ARCHIVE_MATCH API */ __LA_DECL struct archive *archive_match_new(void); __LA_DECL int archive_match_free(struct archive *); /* * Test if archive_entry is excluded. * This is a convenience function. This is the same as calling all * archive_match_path_excluded, archive_match_time_excluded * and archive_match_owner_excluded. */ __LA_DECL int archive_match_excluded(struct archive *, struct archive_entry *); /* * Test if pathname is excluded. The conditions are set by following functions. */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); /* Control recursive inclusion of directory content when directory is included. Default on. */ __LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, const wchar_t *); /* Add exclusion pathname pattern from file. */ __LA_DECL int archive_match_exclude_pattern_from_file(struct archive *, const char *, int _nullSeparator); __LA_DECL int archive_match_exclude_pattern_from_file_w(struct archive *, const wchar_t *, int _nullSeparator); /* Add inclusion pathname pattern. */ __LA_DECL int archive_match_include_pattern(struct archive *, const char *); __LA_DECL int archive_match_include_pattern_w(struct archive *, const wchar_t *); /* Add inclusion pathname pattern from file. */ __LA_DECL int archive_match_include_pattern_from_file(struct archive *, const char *, int _nullSeparator); __LA_DECL int archive_match_include_pattern_from_file_w(struct archive *, const wchar_t *, int _nullSeparator); /* * How to get statistic information for inclusion patterns. */ /* Return the amount number of unmatched inclusion patterns. */ __LA_DECL int archive_match_path_unmatched_inclusions(struct archive *); /* Return the pattern of unmatched inclusion with ARCHIVE_OK. * Return ARCHIVE_EOF if there is no inclusion pattern. */ __LA_DECL int archive_match_path_unmatched_inclusions_next( struct archive *, const char **); __LA_DECL int archive_match_path_unmatched_inclusions_next_w( struct archive *, const wchar_t **); /* * Test if a file is excluded by its time stamp. * The conditions are set by following functions. */ __LA_DECL int archive_match_time_excluded(struct archive *, struct archive_entry *); /* * Flags to tell a matching type of time stamps. These are used for * following functions. */ /* Time flag: mtime to be tested. */ #define ARCHIVE_MATCH_MTIME (0x0100) /* Time flag: ctime to be tested. */ #define ARCHIVE_MATCH_CTIME (0x0200) /* Comparison flag: Match the time if it is newer than. */ #define ARCHIVE_MATCH_NEWER (0x0001) /* Comparison flag: Match the time if it is older than. */ #define ARCHIVE_MATCH_OLDER (0x0002) /* Comparison flag: Match the time if it is equal to. */ #define ARCHIVE_MATCH_EQUAL (0x0010) /* Set inclusion time. */ __LA_DECL int archive_match_include_time(struct archive *, int _flag, time_t _sec, long _nsec); /* Set inclusion time by a date string. */ __LA_DECL int archive_match_include_date(struct archive *, int _flag, const char *_datestr); __LA_DECL int archive_match_include_date_w(struct archive *, int _flag, const wchar_t *_datestr); /* Set inclusion time by a particular file. */ __LA_DECL int archive_match_include_file_time(struct archive *, int _flag, const char *_pathname); __LA_DECL int archive_match_include_file_time_w(struct archive *, int _flag, const wchar_t *_pathname); /* Add exclusion entry. */ __LA_DECL int archive_match_exclude_entry(struct archive *, int _flag, struct archive_entry *); /* * Test if a file is excluded by its uid ,gid, uname or gname. * The conditions are set by following functions. */ __LA_DECL int archive_match_owner_excluded(struct archive *, struct archive_entry *); /* Add inclusion uid, gid, uname and gname. */ __LA_DECL int archive_match_include_uid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_gid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_uname(struct archive *, const char *); __LA_DECL int archive_match_include_uname_w(struct archive *, const wchar_t *); __LA_DECL int archive_match_include_gname(struct archive *, const char *); __LA_DECL int archive_match_include_gname_w(struct archive *, const wchar_t *); /* Utility functions */ /* Convenience function to sort a NULL terminated list of strings */ __LA_DECL int archive_utility_string_sort(char **); #ifdef __cplusplus } #endif /* These are meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_H_INCLUDED */ Index: stable/10/contrib/libarchive/libarchive/archive_entry.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_entry.h (revision 349524) +++ stable/10/contrib/libarchive/libarchive/archive_entry.h (revision 349525) @@ -1,711 +1,711 @@ /*- * 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 3003003 +#define ARCHIVE_VERSION_NUMBER 3004000 /* * Note: archive_entry.h is for use outside of libarchive; the * configuration headers (config.h, archive_platform.h, etc.) are * purely internal. Do NOT use HAVE_XXX configuration macros to * control the behavior of this header! If you must conditionalize, * use predefined compiler and/or platform macros. */ #include #include /* for wchar_t */ #include #include #if defined(_WIN32) && !defined(__CYGWIN__) #include #endif /* Get a suitable 64-bit integer type. */ #if !defined(__LA_INT64_T_DEFINED) # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_INT64_T la_int64_t # endif #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; # else #include # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; # else typedef int64_t la_int64_t; # endif # endif #endif /* The la_ssize_t should match the type used in 'struct stat' */ #if !defined(__LA_SSIZE_T_DEFINED) /* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_SSIZE_T la_ssize_t # endif #define __LA_SSIZE_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) typedef ssize_t la_ssize_t; # elif defined(_WIN64) typedef __int64 la_ssize_t; # else typedef long la_ssize_t; # endif # else # include /* ssize_t */ typedef ssize_t la_ssize_t; # endif #endif /* Get a suitable definition for mode_t */ #if ARCHIVE_VERSION_NUMBER >= 3999000 /* Switch to plain 'int' for libarchive 4.0. It's less broken than 'mode_t' */ # define __LA_MODE_T int #elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__) # define __LA_MODE_T unsigned short #else # define __LA_MODE_T mode_t #endif /* Large file support for Android */ #ifdef __ANDROID__ #include "android_lf.h" #endif /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ #if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries on all platforms and shared libraries on non-Windows. */ # define __LA_DECL #endif #if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 # define __LA_DEPRECATED __attribute__((deprecated)) #else # define __LA_DEPRECATED #endif #ifdef __cplusplus extern "C" { #endif /* * Description of an archive entry. * * You can think of this as "struct stat" with some text fields added in. * * TODO: Add "comment", "charset", and possibly other entries that are * supported by "pax interchange" format. However, GNU, ustar, cpio, * and other variants don't support these features, so they're not an * excruciatingly high priority right now. * * TODO: "pax interchange" format allows essentially arbitrary * key/value attributes to be attached to any entry. Supporting * such extensions may make this library useful for special * applications (e.g., a package manager could attach special * package-management attributes to each entry). */ struct archive; struct archive_entry; /* * File-type constants. These are returned from archive_entry_filetype() * and passed to archive_entry_set_filetype(). * * These values match S_XXX defines on every platform I've checked, * including Windows, AIX, Linux, Solaris, and BSD. They're * (re)defined here because platforms generally don't define the ones * they don't support. For example, Windows doesn't define S_IFLNK or * S_IFBLK. Instead of having a mass of conditional logic and system * checks to define any S_XXX values that aren't supported locally, * I've just defined a new set of such constants so that * libarchive-based applications can manipulate and identify archive * entries properly even if the hosting platform can't store them on * disk. * * These values are also used directly within some portable formats, * such as cpio. If you find a platform that varies from these, the * correct solution is to leave these alone and translate from these * portable values to platform-native values when entries are read from * or written to disk. */ /* * In libarchive 4.0, we can drop the casts here. * They're needed to work around Borland C's broken mode_t. */ #define AE_IFMT ((__LA_MODE_T)0170000) #define AE_IFREG ((__LA_MODE_T)0100000) #define AE_IFLNK ((__LA_MODE_T)0120000) #define AE_IFSOCK ((__LA_MODE_T)0140000) #define AE_IFCHR ((__LA_MODE_T)0020000) #define AE_IFBLK ((__LA_MODE_T)0060000) #define AE_IFDIR ((__LA_MODE_T)0040000) #define AE_IFIFO ((__LA_MODE_T)0010000) /* * Symlink types */ #define AE_SYMLINK_TYPE_UNDEFINED 0 #define AE_SYMLINK_TYPE_FILE 1 #define AE_SYMLINK_TYPE_DIRECTORY 2 /* * Basic object manipulation */ __LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *); /* The 'clone' function does a deep copy; all of the strings are copied too. */ __LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *); __LA_DECL void archive_entry_free(struct archive_entry *); __LA_DECL struct archive_entry *archive_entry_new(void); /* * This form of archive_entry_new2() will pull character-set * conversion information from the specified archive handle. The * older archive_entry_new(void) form is equivalent to calling * archive_entry_new2(NULL) and will result in the use of an internal * default character-set conversion. */ __LA_DECL struct archive_entry *archive_entry_new2(struct archive *); /* * Retrieve fields from an archive_entry. * * There are a number of implicit conversions among these fields. For * example, if a regular string field is set and you read the _w wide * character field, the entry will implicitly convert narrow-to-wide * using the current locale. Similarly, dev values are automatically * updated when you write devmajor or devminor and vice versa. * * In addition, fields can be "set" or "unset." Unset string fields * return NULL, non-string fields have _is_set() functions to test * whether they've been set. You can "unset" a string field by * assigning NULL; non-string fields have _unset() functions to * unset them. * * Note: There is one ambiguity in the above; string fields will * also return NULL when implicit character set conversions fail. * This is usually what you want. */ __LA_DECL time_t archive_entry_atime(struct archive_entry *); __LA_DECL long archive_entry_atime_nsec(struct archive_entry *); __LA_DECL int archive_entry_atime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_birthtime(struct archive_entry *); __LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_ctime(struct archive_entry *); __LA_DECL long archive_entry_ctime_nsec(struct archive_entry *); __LA_DECL int archive_entry_ctime_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_dev(struct archive_entry *); __LA_DECL int archive_entry_dev_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_devminor(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); __LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); __LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); __LA_DECL la_int64_t archive_entry_gid(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); __LA_DECL const char *archive_entry_gname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *); __LA_DECL int archive_entry_ino_is_set(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); __LA_DECL time_t archive_entry_mtime(struct archive_entry *); __LA_DECL long archive_entry_mtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_mtime_is_set(struct archive_entry *); __LA_DECL unsigned int archive_entry_nlink(struct archive_entry *); __LA_DECL const char *archive_entry_pathname(struct archive_entry *); __LA_DECL const char *archive_entry_pathname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_perm(struct archive_entry *); __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); __LA_DECL const char *archive_entry_sourcepath(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_sourcepath_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_size(struct archive_entry *); __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *); __LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); __LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); __LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *); __LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *); __LA_DECL int archive_entry_is_encrypted(struct archive_entry *); /* * Set fields in an archive_entry. * * Note: Before libarchive 2.4, there were 'set' and 'copy' versions * of the string setters. 'copy' copied the actual string, 'set' just * stored the pointer. In libarchive 2.4 and later, strings are * always copied. */ __LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_atime(struct archive_entry *); #if defined(_WIN32) && !defined(__CYGWIN__) __LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *); #endif __LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_birthtime(struct archive_entry *); __LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_ctime(struct archive_entry *); __LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_fflags(struct archive_entry *, unsigned long /* set */, unsigned long /* clear */); /* Returns pointer to start of first invalid token, or NULL if none. */ /* Note that all recognized tokens are processed, regardless. */ __LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *, const char *); __LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_gid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_gname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_hardlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_mtime(struct archive_entry *); __LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_pathname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_size(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int); __LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_uid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_uname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted); __LA_DECL void archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted); /* * Routines to bulk copy fields to/from a platform-native "struct * stat." Libarchive used to just store a struct stat inside of each * archive_entry object, but this created issues when trying to * manipulate archives on systems different than the ones they were * created on. * * TODO: On Linux and other LFS systems, provide both stat32 and * stat64 versions of these functions and all of the macro glue so * that archive_entry_stat is magically defined to * archive_entry_stat32 or archive_entry_stat64 as appropriate. */ __LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); /* * Storage for Mac OS-specific AppleDouble metadata information. * Apple-format tar files store a separate binary blob containing * encoded metadata with ACL, extended attributes, etc. * This provides a place to store that blob. */ __LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *); __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t); /* * 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 */); __LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const wchar_t ** /* name */); /* * Construct a text-format ACL. The flags argument is a bitmask that * can include any of the following: * * Flags only for archive entries with POSIX.1e ACL: * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries. * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries. * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each * default ACL entry. * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and * "mask" entries. * * Flags only for archive entries with NFSv4 ACL: * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for * unset permissions and flags in NFSv4 ACL permission and flag fields * * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL: * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in * each ACL entry. * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma * instead of newline. */ #define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 0x00000001 #define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 0x00000002 #define ARCHIVE_ENTRY_ACL_STYLE_SOLARIS 0x00000004 #define ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 0x00000008 #define ARCHIVE_ENTRY_ACL_STYLE_COMPACT 0x00000010 __LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *, la_ssize_t * /* len */, int /* flags */); __LA_DECL char *archive_entry_acl_to_text(struct archive_entry *, la_ssize_t * /* len */, int /* flags */); __LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *, const wchar_t * /* wtext */, int /* type */); __LA_DECL int archive_entry_acl_from_text(struct archive_entry *, const char * /* text */, int /* type */); /* Deprecated constants */ #define OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024 #define OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 /* Deprecated functions */ __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int /* flags */) __LA_DEPRECATED; __LA_DECL const char *archive_entry_acl_text(struct archive_entry *, int /* flags */) __LA_DEPRECATED; /* Return bitmask of ACL types in an archive entry */ __LA_DECL int archive_entry_acl_types(struct archive_entry *); /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); /* Return an opaque ACL object. */ /* There's not yet anything clients can actually do with this... */ struct archive_acl; __LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *); /* * extended attributes */ __LA_DECL void archive_entry_xattr_clear(struct archive_entry *); __LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *, const char * /* name */, const void * /* value */, size_t /* size */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_xattr_count(struct archive_entry *); __LA_DECL int archive_entry_xattr_reset(struct archive_entry *); __LA_DECL int archive_entry_xattr_next(struct archive_entry *, const char ** /* name */, const void ** /* value */, size_t *); /* * sparse */ __LA_DECL void archive_entry_sparse_clear(struct archive_entry *); __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *, la_int64_t /* offset */, la_int64_t /* length */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_sparse_count(struct archive_entry *); __LA_DECL int archive_entry_sparse_reset(struct archive_entry *); __LA_DECL int archive_entry_sparse_next(struct archive_entry *, la_int64_t * /* offset */, la_int64_t * /* length */); /* * Utility to match up hardlinks. * * The 'struct archive_entry_linkresolver' is a cache of archive entries * for files with multiple links. Here's how to use it: * 1. Create a lookup object with archive_entry_linkresolver_new() * 2. Tell it the archive format you're using. * 3. Hand each archive_entry to archive_entry_linkify(). * That function will return 0, 1, or 2 entries that should * be written. * 4. Call archive_entry_linkify(resolver, NULL) until * no more entries are returned. * 5. Call archive_entry_linkresolver_free(resolver) to free resources. * * The entries returned have their hardlink and size fields updated * appropriately. If an entry is passed in that does not refer to * a file with multiple links, it is returned unchanged. The intention * is that you should be able to simply filter all entries through * this machine. * * To make things more efficient, be sure that each entry has a valid * nlinks value. The hardlink cache uses this to track when all links * have been found. If the nlinks value is zero, it will keep every * name in the cache indefinitely, which can use a lot of memory. * * Note that archive_entry_size() is reset to zero if the file * body should not be written to the archive. Pay attention! */ struct archive_entry_linkresolver; /* * There are three different strategies for marking hardlinks. * The descriptions below name them after the best-known * formats that rely on each strategy: * * "Old cpio" is the simplest, it always returns any entry unmodified. * As far as I know, only cpio formats use this. Old cpio archives * store every link with the full body; the onus is on the dearchiver * to detect and properly link the files as they are restored. * "tar" is also pretty simple; it caches a copy the first time it sees * any link. Subsequent appearances are modified to be hardlink * references to the first one without any body. Used by all tar * formats, although the newest tar formats permit the "old cpio" strategy * as well. This strategy is very simple for the dearchiver, * and reasonably straightforward for the archiver. * "new cpio" is trickier. It stores the body only with the last * occurrence. The complication is that we might not * see every link to a particular file in a single session, so * there's no easy way to know when we've seen the last occurrence. * The solution here is to queue one link until we see the next. * At the end of the session, you can enumerate any remaining * entries by calling archive_entry_linkify(NULL) and store those * bodies. If you have a file with three links l1, l2, and l3, * you'll get the following behavior if you see all three links: * linkify(l1) => NULL (the resolver stores l1 internally) * linkify(l2) => l1 (resolver stores l2, you write l1) * linkify(l3) => l2, l3 (all links seen, you can write both). * If you only see l1 and l2, you'll get this behavior: * linkify(l1) => NULL * linkify(l2) => l1 * linkify(NULL) => l2 (at end, you retrieve remaining links) * As the name suggests, this strategy is used by newer cpio variants. * It's noticeably more complex for the archiver, slightly more complex * for the dearchiver than the tar strategy, but makes it straightforward * to restore a file using any link by simply continuing to scan until * you see a link that is stored with a body. In contrast, the tar * strategy requires you to rescan the archive from the beginning to * correctly extract an arbitrary link. */ __LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void); __LA_DECL void archive_entry_linkresolver_set_strategy( struct archive_entry_linkresolver *, int /* format_code */); __LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *); __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); #ifdef __cplusplus } #endif /* This is meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar.c (revision 349524) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar.c (revision 349525) @@ -1,2945 +1,2946 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Andres Mejia * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #include #include #ifdef HAVE_ZLIB_H #include /* crc32 */ #endif #include "archive.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" /* RAR signature, also known as the mark header */ #define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00" /* Header types */ #define MARK_HEAD 0x72 #define MAIN_HEAD 0x73 #define FILE_HEAD 0x74 #define COMM_HEAD 0x75 #define AV_HEAD 0x76 #define SUB_HEAD 0x77 #define PROTECT_HEAD 0x78 #define SIGN_HEAD 0x79 #define NEWSUB_HEAD 0x7a #define ENDARC_HEAD 0x7b /* Main Header Flags */ #define MHD_VOLUME 0x0001 #define MHD_COMMENT 0x0002 #define MHD_LOCK 0x0004 #define MHD_SOLID 0x0008 #define MHD_NEWNUMBERING 0x0010 #define MHD_AV 0x0020 #define MHD_PROTECT 0x0040 #define MHD_PASSWORD 0x0080 #define MHD_FIRSTVOLUME 0x0100 #define MHD_ENCRYPTVER 0x0200 /* Flags common to all headers */ #define HD_MARKDELETION 0x4000 #define HD_ADD_SIZE_PRESENT 0x8000 /* File Header Flags */ #define FHD_SPLIT_BEFORE 0x0001 #define FHD_SPLIT_AFTER 0x0002 #define FHD_PASSWORD 0x0004 #define FHD_COMMENT 0x0008 #define FHD_SOLID 0x0010 #define FHD_LARGE 0x0100 #define FHD_UNICODE 0x0200 #define FHD_SALT 0x0400 #define FHD_VERSION 0x0800 #define FHD_EXTTIME 0x1000 #define FHD_EXTFLAGS 0x2000 /* File dictionary sizes */ #define DICTIONARY_SIZE_64 0x00 #define DICTIONARY_SIZE_128 0x20 #define DICTIONARY_SIZE_256 0x40 #define DICTIONARY_SIZE_512 0x60 #define DICTIONARY_SIZE_1024 0x80 #define DICTIONARY_SIZE_2048 0xA0 #define DICTIONARY_SIZE_4096 0xC0 #define FILE_IS_DIRECTORY 0xE0 #define DICTIONARY_MASK FILE_IS_DIRECTORY /* OS Flags */ #define OS_MSDOS 0 #define OS_OS2 1 #define OS_WIN32 2 #define OS_UNIX 3 #define OS_MAC_OS 4 #define OS_BEOS 5 /* Compression Methods */ #define COMPRESS_METHOD_STORE 0x30 /* LZSS */ #define COMPRESS_METHOD_FASTEST 0x31 #define COMPRESS_METHOD_FAST 0x32 #define COMPRESS_METHOD_NORMAL 0x33 /* PPMd Variant H */ #define COMPRESS_METHOD_GOOD 0x34 #define COMPRESS_METHOD_BEST 0x35 #define CRC_POLYNOMIAL 0xEDB88320 #define NS_UNIT 10000000 #define DICTIONARY_MAX_SIZE 0x400000 #define MAINCODE_SIZE 299 #define OFFSETCODE_SIZE 60 #define LOWOFFSETCODE_SIZE 17 #define LENGTHCODE_SIZE 28 #define HUFFMAN_TABLE_SIZE \ MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE #define MAX_SYMBOL_LENGTH 0xF #define MAX_SYMBOLS 20 /* * Considering L1,L2 cache miss and a calling of write system-call, * the best size of the output buffer(uncompressed buffer) is 128K. * If the structure of extracting process is changed, this value * might be researched again. */ #define UNP_BUFFER_SIZE (128 * 1024) /* Define this here for non-Windows platforms */ #if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)) #define FILE_ATTRIBUTE_DIRECTORY 0x10 #endif /* Fields common to all headers */ struct rar_header { char crc[2]; char type; char flags[2]; char size[2]; }; /* Fields common to all file headers */ struct rar_file_header { char pack_size[4]; char unp_size[4]; char host_os; char file_crc[4]; char file_time[4]; char unp_ver; char method; char name_size[2]; char file_attr[4]; }; struct huffman_tree_node { int branches[2]; }; struct huffman_table_entry { unsigned int length; int value; }; struct huffman_code { struct huffman_tree_node *tree; int numentries; int numallocatedentries; int minlength; int maxlength; int tablesize; struct huffman_table_entry *table; }; struct lzss { unsigned char *window; int mask; int64_t position; }; struct data_block_offsets { int64_t header_size; int64_t start_offset; int64_t end_offset; }; struct rar { /* Entries from main RAR header */ unsigned main_flags; unsigned long file_crc; char reserved1[2]; char reserved2[4]; char encryptver; /* File header entries */ char compression_method; unsigned file_flags; int64_t packed_size; int64_t unp_size; time_t mtime; long mnsec; mode_t mode; char *filename; char *filename_save; size_t filename_save_size; size_t filename_allocated; /* File header optional entries */ char salt[8]; time_t atime; long ansec; time_t ctime; long cnsec; time_t arctime; long arcnsec; /* Fields to help with tracking decompression of files. */ int64_t bytes_unconsumed; int64_t bytes_remaining; int64_t bytes_uncopied; int64_t offset; int64_t offset_outgoing; int64_t offset_seek; char valid; unsigned int unp_offset; unsigned int unp_buffer_size; unsigned char *unp_buffer; unsigned int dictionary_size; char start_new_block; char entry_eof; unsigned long crc_calculated; int found_first_header; char has_endarc_header; struct data_block_offsets *dbo; unsigned int cursor; unsigned int nodes; /* LZSS members */ struct huffman_code maincode; struct huffman_code offsetcode; struct huffman_code lowoffsetcode; struct huffman_code lengthcode; unsigned char lengthtable[HUFFMAN_TABLE_SIZE]; struct lzss lzss; char output_last_match; unsigned int lastlength; unsigned int lastoffset; unsigned int oldoffset[4]; unsigned int lastlowoffset; unsigned int numlowoffsetrepeats; int64_t filterstart; char start_new_table; /* PPMd Variant H members */ char ppmd_valid; char ppmd_eod; char is_ppmd_block; int ppmd_escape; CPpmd7 ppmd7_context; CPpmd7z_RangeDec range_dec; IByteIn bytein; /* * String conversion object. */ int init_default_conversion; struct archive_string_conv *sconv_default; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_utf8; struct archive_string_conv *sconv_utf16be; /* * Bit stream reader. */ struct rar_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; ssize_t avail_in; const unsigned char *next_in; } br; /* * Custom field to denote that this archive contains encrypted entries */ int has_encrypted_entries; }; static int archive_read_support_format_rar_capabilities(struct archive_read *); static int archive_read_format_rar_has_encrypted_entries(struct archive_read *); static int archive_read_format_rar_bid(struct archive_read *, int); static int archive_read_format_rar_options(struct archive_read *, const char *, const char *); static int archive_read_format_rar_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_rar_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_rar_read_data_skip(struct archive_read *a); static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t, int); static int archive_read_format_rar_cleanup(struct archive_read *); /* Support functions */ static int read_header(struct archive_read *, struct archive_entry *, char); static time_t get_time(int); static int read_exttime(const char *, struct rar *, const char *); static int read_symlink_stored(struct archive_read *, struct archive_entry *, struct archive_string_conv *); static int read_data_stored(struct archive_read *, const void **, size_t *, int64_t *); static int read_data_compressed(struct archive_read *, const void **, size_t *, int64_t *); static int rar_br_preparation(struct archive_read *, struct rar_br *); static int parse_codes(struct archive_read *); static void free_codes(struct archive_read *); static int read_next_symbol(struct archive_read *, struct huffman_code *); static int create_code(struct archive_read *, struct huffman_code *, unsigned char *, int, char); static int add_value(struct archive_read *, struct huffman_code *, int, int, int); static int new_node(struct huffman_code *); static int make_table(struct archive_read *, struct huffman_code *); static int make_table_recurse(struct archive_read *, struct huffman_code *, int, struct huffman_table_entry *, int, int); static int64_t expand(struct archive_read *, int64_t); static int copy_from_lzss_window(struct archive_read *, const void **, int64_t, int); static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *); /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define rar_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define rar_br_bits(br, n) \ (((uint32_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define rar_br_bits_forced(br, n) \ (((uint32_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : there is no data in the stream. */ #define rar_br_read_ahead(a, br, n) \ ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n))) /* Notify how many bits we consumed. */ #define rar_br_consume(br, n) ((br)->cache_avail -= (n)) #define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7) static const uint32_t cache_masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int rar_br_fillup(struct archive_read *a, struct rar_br *br) { struct rar *rar = (struct rar *)(a->format->data); int n = CACHE_BITS - br->cache_avail; for (;;) { switch (n >> 3) { case 8: if (br->avail_in >= 8) { br->cache_buffer = ((uint64_t)br->next_in[0]) << 56 | ((uint64_t)br->next_in[1]) << 48 | ((uint64_t)br->next_in[2]) << 40 | ((uint64_t)br->next_in[3]) << 32 | ((uint32_t)br->next_in[4]) << 24 | ((uint32_t)br->next_in[5]) << 16 | ((uint32_t)br->next_in[6]) << 8 | (uint32_t)br->next_in[7]; br->next_in += 8; br->avail_in -= 8; br->cache_avail += 8 * 8; rar->bytes_unconsumed += 8; rar->bytes_remaining -= 8; return (1); } break; case 7: if (br->avail_in >= 7) { br->cache_buffer = (br->cache_buffer << 56) | ((uint64_t)br->next_in[0]) << 48 | ((uint64_t)br->next_in[1]) << 40 | ((uint64_t)br->next_in[2]) << 32 | ((uint32_t)br->next_in[3]) << 24 | ((uint32_t)br->next_in[4]) << 16 | ((uint32_t)br->next_in[5]) << 8 | (uint32_t)br->next_in[6]; br->next_in += 7; br->avail_in -= 7; br->cache_avail += 7 * 8; rar->bytes_unconsumed += 7; rar->bytes_remaining -= 7; return (1); } break; case 6: if (br->avail_in >= 6) { br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)br->next_in[0]) << 40 | ((uint64_t)br->next_in[1]) << 32 | ((uint32_t)br->next_in[2]) << 24 | ((uint32_t)br->next_in[3]) << 16 | ((uint32_t)br->next_in[4]) << 8 | (uint32_t)br->next_in[5]; br->next_in += 6; br->avail_in -= 6; br->cache_avail += 6 * 8; rar->bytes_unconsumed += 6; rar->bytes_remaining -= 6; return (1); } break; case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } if (br->avail_in <= 0) { if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor * actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); if (br->next_in == NULL) return (0); if (br->avail_in == 0) return (0); } br->cache_buffer = (br->cache_buffer << 8) | *br->next_in++; br->avail_in--; br->cache_avail += 8; n -= 8; rar->bytes_unconsumed++; rar->bytes_remaining--; } } static int rar_br_preparation(struct archive_read *a, struct rar_br *br) { struct rar *rar = (struct rar *)(a->format->data); if (rar->bytes_remaining > 0) { br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); if (br->next_in == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } if (br->cache_avail == 0) (void)rar_br_fillup(a, br); } return (ARCHIVE_OK); } /* Find last bit set */ static inline int rar_fls(unsigned int word) { word |= (word >> 1); word |= (word >> 2); word |= (word >> 4); word |= (word >> 8); word |= (word >> 16); return word - (word >> 1); } /* LZSS functions */ static inline int64_t lzss_position(struct lzss *lzss) { return lzss->position; } static inline int lzss_mask(struct lzss *lzss) { return lzss->mask; } static inline int lzss_size(struct lzss *lzss) { return lzss->mask + 1; } static inline int lzss_offset_for_position(struct lzss *lzss, int64_t pos) { return (int)(pos & lzss->mask); } static inline unsigned char * lzss_pointer_for_position(struct lzss *lzss, int64_t pos) { return &lzss->window[lzss_offset_for_position(lzss, pos)]; } static inline int lzss_current_offset(struct lzss *lzss) { return lzss_offset_for_position(lzss, lzss->position); } static inline uint8_t * lzss_current_pointer(struct lzss *lzss) { return lzss_pointer_for_position(lzss, lzss->position); } static inline void lzss_emit_literal(struct rar *rar, uint8_t literal) { *lzss_current_pointer(&rar->lzss) = literal; rar->lzss.position++; } static inline void lzss_emit_match(struct rar *rar, int offset, int length) { int dstoffs = lzss_current_offset(&rar->lzss); int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss); int l, li, remaining; unsigned char *d, *s; remaining = length; while (remaining > 0) { l = remaining; if (dstoffs > srcoffs) { if (l > lzss_size(&rar->lzss) - dstoffs) l = lzss_size(&rar->lzss) - dstoffs; } else { if (l > lzss_size(&rar->lzss) - srcoffs) l = lzss_size(&rar->lzss) - srcoffs; } d = &(rar->lzss.window[dstoffs]); s = &(rar->lzss.window[srcoffs]); if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs)) memcpy(d, s, l); else { for (li = 0; li < l; li++) d[li] = s[li]; } remaining -= l; dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss)); srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss)); } rar->lzss.position += length; } static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); Byte b; if (!rar_br_read_ahead(a, br, 8)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return 0; } b = rar_br_bits(br, 8); rar_br_consume(br, 8); return b; } int archive_read_support_format_rar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct rar *rar; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar"); rar = (struct rar *)calloc(sizeof(*rar), 1); if (rar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data"); return (ARCHIVE_FATAL); } /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; r = __archive_read_register_format(a, rar, "rar", archive_read_format_rar_bid, archive_read_format_rar_options, archive_read_format_rar_read_header, archive_read_format_rar_read_data, archive_read_format_rar_read_data_skip, archive_read_format_rar_seek_data, archive_read_format_rar_cleanup, archive_read_support_format_rar_capabilities, archive_read_format_rar_has_encrypted_entries); if (r != ARCHIVE_OK) free(rar); return (r); } static int archive_read_support_format_rar_capabilities(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_rar_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct rar * rar = (struct rar *)_a->format->data; if (rar) { return rar->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_rar_bid(struct archive_read *a, int best_bid) { const char *p; /* If there's already a bid > 30, we'll never win. */ if (best_bid > 30) return (-1); if ((p = __archive_read_ahead(a, 7, NULL)) == NULL) return (-1); if (memcmp(p, RAR_SIGNATURE, 7) == 0) return (30); if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { /* This is a PE file */ ssize_t offset = 0x10000; ssize_t window = 4096; ssize_t bytes_avail; while (offset + window <= (1024 * 128)) { const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) return (0); continue; } p = buff + offset; while (p + 7 < buff + bytes_avail) { if (memcmp(p, RAR_SIGNATURE, 7) == 0) return (30); p += 0x10; } offset = p - buff; } } return (0); } static int skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, total; ssize_t bytes, window; total = 0; window = 4096; while (total + window <= (1024 * 128)) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) goto fatal; continue; } if (bytes < 0x40) goto fatal; p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the RAR header. */ while (p + 7 < q) { if (memcmp(p, RAR_SIGNATURE, 7) == 0) { skip = p - (const char *)h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += 0x10; } skip = p - (const char *)h; __archive_read_consume(a, skip); total += skip; } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out RAR header"); return (ARCHIVE_FATAL); } static int archive_read_format_rar_options(struct archive_read *a, const char *key, const char *val) { struct rar *rar; int ret = ARCHIVE_FAILED; rar = (struct rar *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "rar: hdrcharset option needs a character-set name"); else { rar->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (rar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_read_format_rar_read_header(struct archive_read *a, struct archive_entry *entry) { const void *h; const char *p; struct rar *rar; size_t skip; char head_type; int ret; unsigned flags; unsigned long crc32_expected; a->archive.archive_format = ARCHIVE_FORMAT_RAR; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "RAR"; rar = (struct rar *)(a->format->data); /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { rar->has_encrypted_entries = 0; } /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if * this fails. */ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (ARCHIVE_EOF); p = h; if (rar->found_first_header == 0 && ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) { /* This is an executable ? Must be self-extracting... */ ret = skip_sfx(a); if (ret < ARCHIVE_WARN) return (ret); } rar->found_first_header = 1; while (1) { unsigned long crc32_val; if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; head_type = p[2]; switch(head_type) { case MARK_HEAD: if (memcmp(p, RAR_SIGNATURE, 7) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid marker header"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 7); break; case MAIN_HEAD: rar->main_flags = archive_le16dec(p + 3); skip = archive_le16dec(p + 5); if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, skip, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1)); memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1), sizeof(rar->reserved2)); if (rar->main_flags & MHD_ENCRYPTVER) { if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } rar->encryptver = *(p + 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)); } /* Main header is password encrypted, so we cannot read any file names or any other info about files from the header. */ if (rar->main_flags & MHD_PASSWORD) { archive_entry_set_is_metadata_encrypted(entry, 1); archive_entry_set_is_data_encrypted(entry, 1); rar->has_encrypted_entries = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR encryption support unavailable."); return (ARCHIVE_FATAL); } crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); if ((crc32_val & 0xffff) != archive_le16dec(p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } __archive_read_consume(a, skip); break; case FILE_HEAD: return read_header(a, entry, head_type); case COMM_HEAD: case AV_HEAD: case SUB_HEAD: case PROTECT_HEAD: case SIGN_HEAD: case ENDARC_HEAD: flags = archive_le16dec(p + 3); skip = archive_le16dec(p + 5); if (skip < 7) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size too small"); return (ARCHIVE_FATAL); } if (flags & HD_ADD_SIZE_PRESENT) { if (skip < 7 + 4) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size too small"); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, skip, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; skip += archive_le32dec(p + 7); } /* Skip over the 2-byte CRC at the beginning of the header. */ crc32_expected = archive_le16dec(p); __archive_read_consume(a, 2); skip -= 2; /* Skim the entire header and compute the CRC. */ crc32_val = 0; while (skip > 0) { size_t to_read = skip; ssize_t did_read; if (to_read > 32 * 1024) { to_read = 32 * 1024; } if ((h = __archive_read_ahead(a, to_read, &did_read)) == NULL) { return (ARCHIVE_FATAL); } p = h; crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned)did_read); __archive_read_consume(a, did_read); skip -= did_read; } if ((crc32_val & 0xffff) != crc32_expected) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } if (head_type == ENDARC_HEAD) return (ARCHIVE_EOF); break; case NEWSUB_HEAD: if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN) return ret; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file"); return (ARCHIVE_FATAL); } } } static int archive_read_format_rar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct rar *rar = (struct rar *)(a->format->data); int ret; if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { rar->has_encrypted_entries = 0; } if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } *buff = NULL; if (rar->entry_eof || rar->offset_seek >= rar->unp_size) { *size = 0; *offset = rar->offset; if (*offset < rar->unp_size) *offset = rar->unp_size; return (ARCHIVE_EOF); } switch (rar->compression_method) { case COMPRESS_METHOD_STORE: ret = read_data_stored(a, buff, size, offset); break; case COMPRESS_METHOD_FASTEST: case COMPRESS_METHOD_FAST: case COMPRESS_METHOD_NORMAL: case COMPRESS_METHOD_GOOD: case COMPRESS_METHOD_BEST: ret = read_data_compressed(a, buff, size, offset); if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) { __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->start_new_table = 1; + rar->ppmd_valid = 0; } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported compression method for RAR file."); ret = ARCHIVE_FATAL; break; } return (ret); } static int archive_read_format_rar_read_data_skip(struct archive_read *a) { struct rar *rar; int64_t bytes_skipped; int ret; rar = (struct rar *)(a->format->data); if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } if (rar->bytes_remaining > 0) { bytes_skipped = __archive_read_consume(a, rar->bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); } /* Compressed data to skip must be read from each header in a multivolume * archive. */ if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER) { ret = archive_read_format_rar_read_header(a, a->entry); if (ret == (ARCHIVE_EOF)) ret = archive_read_format_rar_read_header(a, a->entry); if (ret != (ARCHIVE_OK)) return ret; return archive_read_format_rar_read_data_skip(a); } return (ARCHIVE_OK); } static int64_t archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, int whence) { int64_t client_offset, ret; unsigned int i; struct rar *rar = (struct rar *)(a->format->data); if (rar->compression_method == COMPRESS_METHOD_STORE) { /* Modify the offset for use with SEEK_SET */ switch (whence) { case SEEK_CUR: client_offset = rar->offset_seek; break; case SEEK_END: client_offset = rar->unp_size; break; case SEEK_SET: default: client_offset = 0; } client_offset += offset; if (client_offset < 0) { /* Can't seek past beginning of data block */ return -1; } else if (client_offset > rar->unp_size) { /* * Set the returned offset but only seek to the end of * the data block. */ rar->offset_seek = client_offset; client_offset = rar->unp_size; } client_offset += rar->dbo[0].start_offset; i = 0; while (i < rar->cursor) { i++; client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset; } if (rar->main_flags & MHD_VOLUME) { /* Find the appropriate offset among the multivolume archive */ while (1) { if (client_offset < rar->dbo[rar->cursor].start_offset && rar->file_flags & FHD_SPLIT_BEFORE) { /* Search backwards for the correct data block */ if (rar->cursor == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Attempt to seek past beginning of RAR data block"); return (ARCHIVE_FAILED); } rar->cursor--; client_offset -= rar->dbo[rar->cursor+1].start_offset - rar->dbo[rar->cursor].end_offset; if (client_offset < rar->dbo[rar->cursor].start_offset) continue; ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor].header_size, SEEK_SET); if (ret < (ARCHIVE_OK)) return ret; ret = archive_read_format_rar_read_header(a, a->entry); if (ret != (ARCHIVE_OK)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error during seek of RAR file"); return (ARCHIVE_FAILED); } rar->cursor--; break; } else if (client_offset > rar->dbo[rar->cursor].end_offset && rar->file_flags & FHD_SPLIT_AFTER) { /* Search forward for the correct data block */ rar->cursor++; if (rar->cursor < rar->nodes && client_offset > rar->dbo[rar->cursor].end_offset) { client_offset += rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor-1].end_offset; continue; } rar->cursor--; ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset, SEEK_SET); if (ret < (ARCHIVE_OK)) return ret; ret = archive_read_format_rar_read_header(a, a->entry); if (ret == (ARCHIVE_EOF)) { rar->has_endarc_header = 1; ret = archive_read_format_rar_read_header(a, a->entry); } if (ret != (ARCHIVE_OK)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error during seek of RAR file"); return (ARCHIVE_FAILED); } client_offset += rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor-1].end_offset; continue; } break; } } ret = __archive_read_seek(a, client_offset, SEEK_SET); if (ret < (ARCHIVE_OK)) return ret; rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret; i = rar->cursor; while (i > 0) { i--; ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset; } ret -= rar->dbo[0].start_offset; /* Always restart reading the file after a seek */ __archive_reset_read_data(&a->archive); rar->bytes_unconsumed = 0; rar->offset = 0; /* * If a seek past the end of file was requested, return the requested * offset. */ if (ret == rar->unp_size && rar->offset_seek > rar->unp_size) return rar->offset_seek; /* Return the new offset */ rar->offset_seek = ret; return rar->offset_seek; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seeking of compressed RAR files is unsupported"); } return (ARCHIVE_FAILED); } static int archive_read_format_rar_cleanup(struct archive_read *a) { struct rar *rar; rar = (struct rar *)(a->format->data); free_codes(a); free(rar->filename); free(rar->filename_save); free(rar->dbo); free(rar->unp_buffer); free(rar->lzss.window); __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); free(rar); (a->format->data) = NULL; return (ARCHIVE_OK); } static int read_header(struct archive_read *a, struct archive_entry *entry, char head_type) { const void *h; const char *p, *endp; struct rar *rar; struct rar_header rar_header; struct rar_file_header file_header; int64_t header_size; unsigned filename_size, end; char *filename; char *strp; char packed_size[8]; char unp_size[8]; int ttime; struct archive_string_conv *sconv, *fn_sconv; unsigned long crc32_val; int ret = (ARCHIVE_OK), ret2; rar = (struct rar *)(a->format->data); /* Setup a string conversion object for non-rar-unicode filenames. */ sconv = rar->opt_sconv; if (sconv == NULL) { if (!rar->init_default_conversion) { rar->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); rar->init_default_conversion = 1; } sconv = rar->sconv_default; } if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; memcpy(&rar_header, p, sizeof(rar_header)); rar->file_flags = archive_le16dec(rar_header.flags); header_size = archive_le16dec(rar_header.size); if (header_size < (int64_t)sizeof(file_header) + 7) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2); __archive_read_consume(a, 7); if (!(rar->file_flags & FHD_SOLID)) { rar->compression_method = 0; rar->packed_size = 0; rar->unp_size = 0; rar->mtime = 0; rar->ctime = 0; rar->atime = 0; rar->arctime = 0; rar->mode = 0; memset(&rar->salt, 0, sizeof(rar->salt)); rar->atime = 0; rar->ansec = 0; rar->ctime = 0; rar->cnsec = 0; rar->mtime = 0; rar->mnsec = 0; rar->arctime = 0; rar->arcnsec = 0; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR solid archive support unavailable."); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) return (ARCHIVE_FATAL); /* File Header CRC check. */ crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7)); if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } /* If no CRC error, Go on parsing File Header. */ p = h; endp = p + header_size - 7; memcpy(&file_header, p, sizeof(file_header)); p += sizeof(file_header); rar->compression_method = file_header.method; ttime = archive_le32dec(file_header.file_time); rar->mtime = get_time(ttime); rar->file_crc = archive_le32dec(file_header.file_crc); if (rar->file_flags & FHD_PASSWORD) { archive_entry_set_is_data_encrypted(entry, 1); rar->has_encrypted_entries = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR encryption support unavailable."); /* Since it is only the data part itself that is encrypted we can at least extract information about the currently processed entry and don't need to return ARCHIVE_FATAL here. */ /*return (ARCHIVE_FATAL);*/ } if (rar->file_flags & FHD_LARGE) { memcpy(packed_size, file_header.pack_size, 4); memcpy(packed_size + 4, p, 4); /* High pack size */ p += 4; memcpy(unp_size, file_header.unp_size, 4); memcpy(unp_size + 4, p, 4); /* High unpack size */ p += 4; rar->packed_size = archive_le64dec(&packed_size); rar->unp_size = archive_le64dec(&unp_size); } else { rar->packed_size = archive_le32dec(file_header.pack_size); rar->unp_size = archive_le32dec(file_header.unp_size); } if (rar->packed_size < 0 || rar->unp_size < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid sizes specified."); return (ARCHIVE_FATAL); } rar->bytes_remaining = rar->packed_size; /* TODO: RARv3 subblocks contain comments. For now the complete block is * consumed at the end. */ if (head_type == NEWSUB_HEAD) { size_t distance = p - (const char *)h; header_size += rar->packed_size; /* Make sure we have the extended data. */ if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; endp = p + header_size - 7; p += distance; } filename_size = archive_le16dec(file_header.name_size); if (p + filename_size > endp) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filename size"); return (ARCHIVE_FATAL); } if (rar->filename_allocated < filename_size * 2 + 2) { char *newptr; size_t newsize = filename_size * 2 + 2; newptr = realloc(rar->filename, newsize); if (newptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } rar->filename = newptr; rar->filename_allocated = newsize; } filename = rar->filename; memcpy(filename, p, filename_size); filename[filename_size] = '\0'; if (rar->file_flags & FHD_UNICODE) { if (filename_size != strlen(filename)) { unsigned char highbyte, flagbits, flagbyte; unsigned fn_end, offset; end = filename_size; fn_end = filename_size * 2; filename_size = 0; offset = (unsigned)strlen(filename) + 1; highbyte = *(p + offset++); flagbits = 0; flagbyte = 0; while (offset < end && filename_size < fn_end) { if (!flagbits) { flagbyte = *(p + offset++); flagbits = 8; } flagbits -= 2; switch((flagbyte >> flagbits) & 3) { case 0: filename[filename_size++] = '\0'; filename[filename_size++] = *(p + offset++); break; case 1: filename[filename_size++] = highbyte; filename[filename_size++] = *(p + offset++); break; case 2: filename[filename_size++] = *(p + offset + 1); filename[filename_size++] = *(p + offset); offset += 2; break; case 3: { char extra, high; uint8_t length = *(p + offset++); if (length & 0x80) { extra = *(p + offset++); high = (char)highbyte; } else extra = high = 0; length = (length & 0x7f) + 2; while (length && filename_size < fn_end) { unsigned cp = filename_size >> 1; filename[filename_size++] = high; filename[filename_size++] = p[cp] + extra; length--; } } break; } } if (filename_size > fn_end) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filename"); return (ARCHIVE_FATAL); } filename[filename_size++] = '\0'; /* * Do not increment filename_size here as the computations below * add the space for the terminating NUL explicitly. */ filename[filename_size] = '\0'; /* Decoded unicode form is UTF-16BE, so we have to update a string * conversion object for it. */ if (rar->sconv_utf16be == NULL) { rar->sconv_utf16be = archive_string_conversion_from_charset( &a->archive, "UTF-16BE", 1); if (rar->sconv_utf16be == NULL) return (ARCHIVE_FATAL); } fn_sconv = rar->sconv_utf16be; strp = filename; while (memcmp(strp, "\x00\x00", 2)) { if (!memcmp(strp, "\x00\\", 2)) *(strp + 1) = '/'; strp += 2; } p += offset; } else { /* * If FHD_UNICODE is set but no unicode data, this file name form * is UTF-8, so we have to update a string conversion object for * it accordingly. */ if (rar->sconv_utf8 == NULL) { rar->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); if (rar->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } fn_sconv = rar->sconv_utf8; while ((strp = strchr(filename, '\\')) != NULL) *strp = '/'; p += filename_size; } } else { fn_sconv = sconv; while ((strp = strchr(filename, '\\')) != NULL) *strp = '/'; p += filename_size; } /* Split file in multivolume RAR. No more need to process header. */ if (rar->filename_save && filename_size == rar->filename_save_size && !memcmp(rar->filename, rar->filename_save, filename_size + 1)) { __archive_read_consume(a, header_size - 7); rar->cursor++; if (rar->cursor >= rar->nodes) { rar->nodes++; if ((rar->dbo = realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } rar->dbo[rar->cursor].header_size = header_size; rar->dbo[rar->cursor].start_offset = -1; rar->dbo[rar->cursor].end_offset = -1; } if (rar->dbo[rar->cursor].start_offset < 0) { rar->dbo[rar->cursor].start_offset = a->filter->position; rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset + rar->packed_size; } return ret; } rar->filename_save = (char*)realloc(rar->filename_save, filename_size + 1); memcpy(rar->filename_save, rar->filename, filename_size + 1); rar->filename_save_size = filename_size; /* Set info for seeking */ free(rar->dbo); if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } rar->dbo[0].header_size = header_size; rar->dbo[0].start_offset = -1; rar->dbo[0].end_offset = -1; rar->cursor = 0; rar->nodes = 1; if (rar->file_flags & FHD_SALT) { if (p + 8 > endp) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } memcpy(rar->salt, p, 8); p += 8; } if (rar->file_flags & FHD_EXTTIME) { if (read_exttime(p, rar, endp) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } } __archive_read_consume(a, header_size - 7); rar->dbo[0].start_offset = a->filter->position; rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size; switch(file_header.host_os) { case OS_MSDOS: case OS_OS2: case OS_WIN32: rar->mode = archive_le32dec(file_header.file_attr); if (rar->mode & FILE_ATTRIBUTE_DIRECTORY) rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else rar->mode = AE_IFREG; rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; break; case OS_UNIX: case OS_MAC_OS: case OS_BEOS: rar->mode = archive_le32dec(file_header.file_attr); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown file attributes from RAR file's host OS"); return (ARCHIVE_FATAL); } rar->bytes_uncopied = rar->bytes_unconsumed = 0; rar->lzss.position = rar->offset = 0; rar->offset_seek = 0; rar->dictionary_size = 0; rar->offset_outgoing = 0; rar->br.cache_avail = 0; rar->br.avail_in = 0; rar->crc_calculated = 0; rar->entry_eof = 0; rar->valid = 1; rar->is_ppmd_block = 0; rar->start_new_table = 1; free(rar->unp_buffer); rar->unp_buffer = NULL; rar->unp_offset = 0; rar->unp_buffer_size = UNP_BUFFER_SIZE; memset(rar->lengthtable, 0, sizeof(rar->lengthtable)); __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->ppmd_valid = rar->ppmd_eod = 0; /* Don't set any archive entries for non-file header types */ if (head_type == NEWSUB_HEAD) return ret; archive_entry_set_mtime(entry, rar->mtime, rar->mnsec); archive_entry_set_ctime(entry, rar->ctime, rar->cnsec); archive_entry_set_atime(entry, rar->atime, rar->ansec); archive_entry_set_size(entry, rar->unp_size); archive_entry_set_mode(entry, rar->mode); if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(fn_sconv)); ret = (ARCHIVE_WARN); } if (((rar->mode) & AE_IFMT) == AE_IFLNK) { /* Make sure a symbolic-link file does not have its body. */ rar->bytes_remaining = 0; archive_entry_set_size(entry, 0); /* Read a symbolic-link name. */ if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN)) return ret2; if (ret > ret2) ret = ret2; } if (rar->bytes_remaining == 0) rar->entry_eof = 1; return ret; } static time_t get_time(int ttime) { struct tm tm; tm.tm_sec = 2 * (ttime & 0x1f); tm.tm_min = (ttime >> 5) & 0x3f; tm.tm_hour = (ttime >> 11) & 0x1f; tm.tm_mday = (ttime >> 16) & 0x1f; tm.tm_mon = ((ttime >> 21) & 0x0f) - 1; tm.tm_year = ((ttime >> 25) & 0x7f) + 80; tm.tm_isdst = -1; return mktime(&tm); } static int read_exttime(const char *p, struct rar *rar, const char *endp) { unsigned rmode, flags, rem, j, count; int ttime, i; struct tm *tm; time_t t; long nsec; if (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++; } tm = localtime(&t); nsec = tm->tm_sec + rem / NS_UNIT; if (rmode & 4) { tm->tm_sec++; t = mktime(tm); } if (i == 3) { rar->mtime = t; rar->mnsec = nsec; } else if (i == 2) { rar->ctime = t; rar->cnsec = nsec; } else if (i == 1) { rar->atime = t; rar->ansec = nsec; } else { rar->arctime = t; rar->arcnsec = nsec; } } } return (0); } static int read_symlink_stored(struct archive_read *a, struct archive_entry *entry, struct archive_string_conv *sconv) { const void *h; const char *p; struct rar *rar; int ret = (ARCHIVE_OK); rar = (struct rar *)(a->format->data); if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; if (archive_entry_copy_symlink_l(entry, p, (size_t)rar->packed_size, sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for link"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "link cannot be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); ret = (ARCHIVE_WARN); } __archive_read_consume(a, rar->packed_size); return ret; } static int read_data_stored(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct rar *rar; ssize_t bytes_avail; rar = (struct rar *)(a->format->data); if (rar->bytes_remaining == 0 && !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)) { *buff = NULL; *size = 0; *offset = rar->offset; if (rar->file_crc != rar->crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); return (ARCHIVE_FATAL); } rar->entry_eof = 1; return (ARCHIVE_EOF); } *buff = rar_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } *size = bytes_avail; *offset = rar->offset; rar->offset += bytes_avail; rar->offset_seek += bytes_avail; rar->bytes_remaining -= bytes_avail; rar->bytes_unconsumed = bytes_avail; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)bytes_avail); return (ARCHIVE_OK); } static int read_data_compressed(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct rar *rar; int64_t start, end, actualend; size_t bs; int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i; rar = (struct rar *)(a->format->data); do { if (!rar->valid) return (ARCHIVE_FATAL); if (rar->ppmd_eod || (rar->dictionary_size && rar->offset >= rar->unp_size)) { if (rar->unp_offset > 0) { /* * We have unprocessed extracted data. write it out. */ *buff = rar->unp_buffer; *size = rar->unp_offset; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); rar->unp_offset = 0; return (ARCHIVE_OK); } *buff = NULL; *size = 0; *offset = rar->offset; if (rar->file_crc != rar->crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); return (ARCHIVE_FATAL); } rar->entry_eof = 1; return (ARCHIVE_EOF); } if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0) { if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset)) bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; rar->bytes_uncopied -= bs; if (*buff != NULL) { rar->unp_offset = 0; *size = rar->unp_buffer_size; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); return (ret); } continue; } if (!rar->br.next_in && (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN) return (ret); if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN))) return (ret); if (rar->is_ppmd_block) { if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } if(sym != rar->ppmd_escape) { lzss_emit_literal(rar, sym); rar->bytes_uncopied++; } else { if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } switch(code) { case 0: rar->start_new_table = 1; return read_data_compressed(a, buff, size, offset); case 2: rar->ppmd_eod = 1;/* End Of ppmd Data. */ continue; case 3: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Parsing filters is unsupported."); return (ARCHIVE_FAILED); case 4: lzss_offset = 0; for (i = 2; i >= 0; i--) { if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } lzss_offset |= code << (i * 8); } if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } lzss_emit_match(rar, lzss_offset + 2, length + 32); rar->bytes_uncopied += length + 32; break; case 5: if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } lzss_emit_match(rar, 1, length + 4); rar->bytes_uncopied += length + 4; break; default: lzss_emit_literal(rar, sym); rar->bytes_uncopied++; } } } else { start = rar->offset; end = start + rar->dictionary_size; rar->filterstart = INT64_MAX; if ((actualend = expand(a, end)) < 0) return ((int)actualend); rar->bytes_uncopied = actualend - start; if (rar->bytes_uncopied == 0) { /* Broken RAR files cause this case. * NOTE: If this case were possible on a normal RAR file * we would find out where it was actually bad and * what we would do to solve it. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file"); return (ARCHIVE_FATAL); } } if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset)) bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; rar->bytes_uncopied -= bs; /* * If *buff is NULL, it means unp_buffer is not full. * So we have to continue extracting a RAR file. */ } while (*buff == NULL); rar->unp_offset = 0; *size = rar->unp_buffer_size; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); return ret; } static int parse_codes(struct archive_read *a) { int i, j, val, n, r; unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags; unsigned int maxorder; struct huffman_code precode; struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); free_codes(a); /* Skip to the next byte */ rar_br_consume_unalined_bits(br); /* PPMd block flag */ if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0) { rar_br_consume(br, 1); if (!rar_br_read_ahead(a, br, 7)) goto truncated_data; ppmd_flags = rar_br_bits(br, 7); rar_br_consume(br, 7); /* Memory is allocated in MB */ if (ppmd_flags & 0x20) { if (!rar_br_read_ahead(a, br, 8)) goto truncated_data; rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20; rar_br_consume(br, 8); } if (ppmd_flags & 0x40) { if (!rar_br_read_ahead(a, br, 8)) goto truncated_data; rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8); rar_br_consume(br, 8); } else rar->ppmd_escape = 2; if (ppmd_flags & 0x20) { maxorder = (ppmd_flags & 0x1F) + 1; if(maxorder > 16) maxorder = 16 + (maxorder - 16) * 3; if (maxorder == 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } /* Make sure ppmd7_contest is freed before Ppmd7_Construct * because reading a broken file cause this abnormal sequence. */ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->bytein.a = a; rar->bytein.Read = &ppmd_read; __archive_ppmd7_functions.PpmdRAR_RangeDec_CreateVTable(&rar->range_dec); rar->range_dec.Stream = &rar->bytein; __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context); if (rar->dictionary_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid zero dictionary size"); return (ARCHIVE_FATAL); } if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context, rar->dictionary_size)) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); return (ARCHIVE_FATAL); } __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder); rar->ppmd_valid = 1; } else { if (!rar->ppmd_valid) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid PPMd sequence"); return (ARCHIVE_FATAL); } if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); return (ARCHIVE_FATAL); } } } else { rar_br_consume(br, 1); /* Keep existing table flag */ if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; if (!rar_br_bits(br, 1)) memset(rar->lengthtable, 0, sizeof(rar->lengthtable)); rar_br_consume(br, 1); memset(&bitlengths, 0, sizeof(bitlengths)); for (i = 0; i < MAX_SYMBOLS;) { if (!rar_br_read_ahead(a, br, 4)) goto truncated_data; bitlengths[i++] = rar_br_bits(br, 4); rar_br_consume(br, 4); if (bitlengths[i-1] == 0xF) { if (!rar_br_read_ahead(a, br, 4)) goto truncated_data; zerocount = rar_br_bits(br, 4); rar_br_consume(br, 4); if (zerocount) { i--; for (j = 0; j < zerocount + 2 && i < MAX_SYMBOLS; j++) bitlengths[i++] = 0; } } } memset(&precode, 0, sizeof(precode)); r = create_code(a, &precode, bitlengths, MAX_SYMBOLS, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) { free(precode.tree); free(precode.table); return (r); } for (i = 0; i < HUFFMAN_TABLE_SIZE;) { if ((val = read_next_symbol(a, &precode)) < 0) { free(precode.tree); free(precode.table); return (ARCHIVE_FATAL); } if (val < 16) { rar->lengthtable[i] = (rar->lengthtable[i] + val) & 0xF; i++; } else if (val < 18) { if (i == 0) { free(precode.tree); free(precode.table); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file."); return (ARCHIVE_FATAL); } if(val == 16) { if (!rar_br_read_ahead(a, br, 3)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 3) + 3; rar_br_consume(br, 3); } else { if (!rar_br_read_ahead(a, br, 7)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 7) + 11; rar_br_consume(br, 7); } for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++) { rar->lengthtable[i] = rar->lengthtable[i-1]; i++; } } else { if(val == 18) { if (!rar_br_read_ahead(a, br, 3)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 3) + 3; rar_br_consume(br, 3); } else { if (!rar_br_read_ahead(a, br, 7)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 7) + 11; rar_br_consume(br, 7); } for(j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++) rar->lengthtable[i++] = 0; } } free(precode.tree); free(precode.table); r = create_code(a, &rar->maincode, &rar->lengthtable[0], MAINCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); r = create_code(a, &rar->offsetcode, &rar->lengthtable[MAINCODE_SIZE], OFFSETCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); r = create_code(a, &rar->lowoffsetcode, &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE], LOWOFFSETCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); r = create_code(a, &rar->lengthcode, &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE], LENGTHCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); } if (!rar->dictionary_size || !rar->lzss.window) { /* Seems as though dictionary sizes are not used. Even so, minimize * memory usage as much as possible. */ void *new_window; unsigned int new_size; if (rar->unp_size >= DICTIONARY_MAX_SIZE) new_size = DICTIONARY_MAX_SIZE; else new_size = rar_fls((unsigned int)rar->unp_size) << 1; 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; 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 repeatpos, lastnode, bitpos, bit, repeatnode, nextnode; free(code->table); code->table = NULL; if(length > code->maxlength) code->maxlength = length; if(length < code->minlength) code->minlength = length; 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); } 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]; } } else if (node < 0) { for(i = 0; i < currtablesize; i++) table[i].length = -1; } else { if(depth == maxdepth) { table[0].length = maxdepth + 1; table[0].value = node; } else { ret |= make_table_recurse(a, code, code->tree[node].branches[0], table, depth + 1, maxdepth); ret |= make_table_recurse(a, code, code->tree[node].branches[1], table + currtablesize / 2, depth + 1, maxdepth); } } return ret; } static int64_t expand(struct archive_read *a, int64_t end) { static const unsigned char lengthbases[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224 }; static const unsigned char lengthbits[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; static const 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 unsigned char shortbases[] = { 0, 4, 8, 16, 32, 64, 128, 192 }; static const unsigned char shortbits[] = { 2, 2, 3, 4, 5, 6, 6, 6 }; int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol; unsigned char newfile; struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); if (rar->filterstart < end) end = rar->filterstart; while (1) { if (rar->output_last_match && lzss_position(&rar->lzss) + rar->lastlength <= end) { lzss_emit_match(rar, rar->lastoffset, rar->lastlength); rar->output_last_match = 0; } if(rar->is_ppmd_block || rar->output_last_match || lzss_position(&rar->lzss) >= end) return lzss_position(&rar->lzss); if ((symbol = read_next_symbol(a, &rar->maincode)) < 0) return (ARCHIVE_FATAL); rar->output_last_match = 0; if (symbol < 256) { lzss_emit_literal(rar, symbol); continue; } else if (symbol == 256) { if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; newfile = !rar_br_bits(br, 1); rar_br_consume(br, 1); if(newfile) { rar->start_new_block = 1; if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; rar->start_new_table = rar_br_bits(br, 1); rar_br_consume(br, 1); return lzss_position(&rar->lzss); } else { if (parse_codes(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); continue; } } else if(symbol==257) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Parsing filters is unsupported."); return (ARCHIVE_FAILED); } else if(symbol==258) { if(rar->lastlength == 0) continue; offs = rar->lastoffset; len = rar->lastlength; } else if (symbol <= 262) { offsindex = symbol - 259; offs = rar->oldoffset[offsindex]; if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0) goto bad_data; if (lensymbol > (int)(sizeof(lengthbases)/sizeof(lengthbases[0]))) goto bad_data; if (lensymbol > (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))) 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 > (int)(sizeof(lengthbases)/sizeof(lengthbases[0]))) goto bad_data; if (symbol-271 > (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))) 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 > (int)(sizeof(offsetbases)/sizeof(offsetbases[0]))) goto bad_data; if (offssymbol > (int)(sizeof(offsetbits)/sizeof(offsetbits[0]))) goto bad_data; offs = offsetbases[offssymbol]+1; if(offsetbits[offssymbol] > 0) { if(offssymbol > 9) { if(offsetbits[offssymbol] > 4) { if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4)) goto truncated_data; offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4; rar_br_consume(br, offsetbits[offssymbol] - 4); } if(rar->numlowoffsetrepeats > 0) { rar->numlowoffsetrepeats--; offs += rar->lastlowoffset; } else { if ((lowoffsetsymbol = read_next_symbol(a, &rar->lowoffsetcode)) < 0) return (ARCHIVE_FATAL); if(lowoffsetsymbol == 16) { rar->numlowoffsetrepeats = 15; offs += rar->lastlowoffset; } else { offs += lowoffsetsymbol; rar->lastlowoffset = lowoffsetsymbol; } } } else { if (!rar_br_read_ahead(a, br, offsetbits[offssymbol])) goto truncated_data; offs += rar_br_bits(br, offsetbits[offssymbol]); rar_br_consume(br, offsetbits[offssymbol]); } } if (offs >= 0x40000) len++; if (offs >= 0x2000) len++; for(i = 3; i > 0; i--) rar->oldoffset[i] = rar->oldoffset[i-1]; rar->oldoffset[0] = offs; } rar->lastoffset = offs; rar->lastlength = len; rar->output_last_match = 1; } truncated_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return (ARCHIVE_FATAL); bad_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } static int copy_from_lzss_window(struct archive_read *a, const void **buffer, int64_t startpos, int length) { int windowoffs, firstpart; struct rar *rar = (struct rar *)(a->format->data); if (!rar->unp_buffer) { if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for uncompressed data."); return (ARCHIVE_FATAL); } } windowoffs = lzss_offset_for_position(&rar->lzss, startpos); if(windowoffs + length <= lzss_size(&rar->lzss)) { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], length); } else if (length <= lzss_size(&rar->lzss)) { firstpart = lzss_size(&rar->lzss) - windowoffs; if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } if (firstpart < length) { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], firstpart); memcpy(&rar->unp_buffer[rar->unp_offset + firstpart], &rar->lzss.window[0], length - firstpart); } else { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], length); } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } rar->unp_offset += length; if (rar->unp_offset >= rar->unp_buffer_size) *buffer = rar->unp_buffer; else *buffer = NULL; return (ARCHIVE_OK); } static const void * rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) { struct rar *rar = (struct rar *)(a->format->data); const void *h = __archive_read_ahead(a, min, avail); int ret; if (avail) { if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested) *avail = a->archive.read_data_requested; if (*avail > rar->bytes_remaining) *avail = (ssize_t)rar->bytes_remaining; if (*avail < 0) return NULL; else if (*avail == 0 && rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER) { 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)) return NULL; return rar_read_ahead(a, min, avail); } } return h; } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar5.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar5.c (revision 349524) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar5.c (revision 349525) @@ -1,4030 +1,4035 @@ /*- * Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #include "archive_endian.h" #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_ZLIB_H #include /* crc32 */ #endif #ifdef HAVE_LIMITS_H #include #endif #include "archive.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_entry_private.h" #ifdef HAVE_BLAKE2_H #include #else #include "archive_blake2.h" #endif /*#define CHECK_CRC_ON_SOLID_SKIP*/ /*#define DONT_FAIL_ON_CRC_ERROR*/ /*#define DEBUG*/ #define rar5_min(a, b) (((a) > (b)) ? (b) : (a)) #define rar5_max(a, b) (((a) > (b)) ? (a) : (b)) #define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X))) #if defined DEBUG #define DEBUG_CODE if(1) #else #define DEBUG_CODE if(0) #endif /* Real RAR5 magic number is: * * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 * "Rar!→•☺·\x00" * * It's stored in `rar5_signature` after XOR'ing it with 0xA1, because I don't * want to put this magic sequence in each binary that uses libarchive, so * applications that scan through the file for this marker won't trigger on * this "false" one. * * The array itself is decrypted in `rar5_init` function. */ static unsigned char rar5_signature[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; static const ssize_t rar5_signature_size = sizeof(rar5_signature); static const size_t g_unpack_window_size = 0x20000; /* These could have been static const's, but they aren't, because of * Visual Studio. */ #define MAX_NAME_IN_CHARS 2048 #define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS) struct file_header { ssize_t bytes_remaining; ssize_t unpacked_size; int64_t last_offset; /* Used in sanity checks. */ int64_t last_size; /* Used in sanity checks. */ uint8_t solid : 1; /* Is this a solid stream? */ uint8_t service : 1; /* Is this file a service data? */ uint8_t eof : 1; /* Did we finish unpacking the file? */ uint8_t dir : 1; /* Is this file entry a directory? */ /* Optional time fields. */ uint64_t e_mtime; uint64_t e_ctime; uint64_t e_atime; uint32_t e_unix_ns; /* Optional hash fields. */ uint32_t stored_crc32; uint32_t calculated_crc32; uint8_t blake2sp[32]; blake2sp_state b2state; char has_blake2; /* Optional redir fields */ uint64_t redir_type; uint64_t redir_flags; }; enum EXTRA { EX_CRYPT = 0x01, EX_HASH = 0x02, EX_HTIME = 0x03, EX_VERSION = 0x04, EX_REDIR = 0x05, EX_UOWNER = 0x06, EX_SUBDATA = 0x07 }; #define REDIR_SYMLINK_IS_DIR 1 enum REDIR_TYPE { REDIR_TYPE_NONE = 0, REDIR_TYPE_UNIXSYMLINK = 1, REDIR_TYPE_WINSYMLINK = 2, REDIR_TYPE_JUNCTION = 3, REDIR_TYPE_HARDLINK = 4, REDIR_TYPE_FILECOPY = 5, }; #define OWNER_USER_NAME 0x01 #define OWNER_GROUP_NAME 0x02 #define OWNER_USER_UID 0x04 #define OWNER_GROUP_GID 0x08 #define OWNER_MAXNAMELEN 256 enum FILTER_TYPE { FILTER_DELTA = 0, /* Generic pattern. */ FILTER_E8 = 1, /* Intel x86 code. */ FILTER_E8E9 = 2, /* Intel x86 code. */ FILTER_ARM = 3, /* ARM code. */ FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */ FILTER_RGB = 5, /* Color palette, not used in RARv5. */ FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */ FILTER_PPM = 7, /* Predictive pattern matching, not used in RARv5. */ FILTER_NONE = 8, }; struct filter_info { int type; int channels; int pos_r; int64_t block_start; ssize_t block_length; uint16_t width; }; struct data_ready { char used; const uint8_t* buf; size_t size; int64_t offset; }; struct cdeque { uint16_t beg_pos; uint16_t end_pos; uint16_t cap_mask; uint16_t size; size_t* arr; }; struct decode_table { uint32_t size; int32_t decode_len[16]; uint32_t decode_pos[16]; uint32_t quick_bits; uint8_t quick_len[1 << 10]; uint16_t quick_num[1 << 10]; uint16_t decode_num[306]; }; struct comp_state { /* Flag used to specify if unpacker needs to reinitialize the uncompression context. */ uint8_t initialized : 1; /* Flag used when applying filters. */ uint8_t all_filters_applied : 1; /* Flag used to skip file context reinitialization, used when unpacker is skipping through different multivolume archives. */ uint8_t switch_multivolume : 1; /* Flag used to specify if unpacker has processed the whole data block or just a part of it. */ uint8_t block_parsing_finished : 1; int notused : 4; int flags; /* Uncompression flags. */ int method; /* Uncompression algorithm method. */ int version; /* Uncompression algorithm version. */ ssize_t window_size; /* Size of window_buf. */ uint8_t* window_buf; /* Circular buffer used during decompression. */ uint8_t* filtered_buf; /* Buffer used when applying filters. */ const uint8_t* block_buf; /* Buffer used when merging blocks. */ size_t window_mask; /* Convenience field; window_size - 1. */ int64_t write_ptr; /* This amount of data has been unpacked in the window buffer. */ int64_t last_write_ptr; /* This amount of data has been stored in the output file. */ int64_t last_unstore_ptr; /* Counter of bytes extracted during unstoring. This is separate from last_write_ptr because of how SERVICE base blocks are handled during skipping in solid multiarchive archives. */ int64_t solid_offset; /* Additional offset inside the window buffer, used in unpacking solid archives. */ ssize_t cur_block_size; /* Size of current data block. */ int last_len; /* Flag used in lzss decompression. */ /* Decode tables used during lzss uncompression. */ #define HUFF_BC 20 struct decode_table bd; /* huffman bit lengths */ #define HUFF_NC 306 struct decode_table ld; /* literals */ #define HUFF_DC 64 struct decode_table dd; /* distances */ #define HUFF_LDC 16 struct decode_table ldd; /* lower bits of distances */ #define HUFF_RC 44 struct decode_table rd; /* repeating distances */ #define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC) /* Circular deque for storing filters. */ struct cdeque filters; int64_t last_block_start; /* Used for sanity checking. */ ssize_t last_block_length; /* Used for sanity checking. */ /* Distance cache used during lzss uncompression. */ int dist_cache[4]; /* Data buffer stack. */ struct data_ready dready[2]; }; /* Bit reader state. */ struct bit_reader { int8_t bit_addr; /* Current bit pointer inside current byte. */ int in_addr; /* Current byte pointer. */ }; /* RARv5 block header structure. Use bf_* functions to get values from * block_flags_u8 field. I.e. bf_byte_count, etc. */ struct compressed_block_header { /* block_flags_u8 contain fields encoded in little-endian bitfield: * * - table present flag (shr 7, and 1), * - last block flag (shr 6, and 1), * - byte_count (shr 3, and 7), * - bit_size (shr 0, and 7). */ uint8_t block_flags_u8; uint8_t block_cksum; }; /* RARv5 main header structure. */ struct main_header { /* Does the archive contain solid streams? */ uint8_t solid : 1; /* If this a multi-file archive? */ uint8_t volume : 1; uint8_t endarc : 1; uint8_t notused : 5; unsigned int vol_no; }; struct generic_header { uint8_t split_after : 1; uint8_t split_before : 1; uint8_t padding : 6; int size; int last_header_id; }; struct multivolume { unsigned int expected_vol_no; uint8_t* push_buf; }; /* Main context structure. */ struct rar5 { int header_initialized; /* Set to 1 if current file is positioned AFTER the magic value * of the archive file. This is used in header reading functions. */ int skipped_magic; /* Set to not zero if we're in skip mode (either by calling * rar5_data_skip function or when skipping over solid streams). * Set to 0 when in * extraction mode. This is used during checksum * calculation functions. */ int skip_mode; /* Set to not zero if we're in block merging mode (i.e. when switching * to another file in multivolume archive, last block from 1st archive * needs to be merged with 1st block from 2nd archive). This flag * guards against recursive use of the merging function, which doesn't * support recursive calls. */ int merge_mode; /* An offset to QuickOpen list. This is not supported by this unpacker, * because we're focusing on streaming interface. QuickOpen is designed * to make things quicker for non-stream interfaces, so it's not our * use case. */ uint64_t qlist_offset; /* An offset to additional Recovery data. This is not supported by this * unpacker. Recovery data are additional Reed-Solomon codes that could * be used to calculate bytes that are missing in archive or are * corrupted. */ uint64_t rr_offset; /* Various context variables grouped to different structures. */ struct generic_header generic; struct main_header main; struct comp_state cstate; struct file_header file; struct bit_reader bits; struct multivolume vol; /* The header of currently processed RARv5 block. Used in main * decompression logic loop. */ struct compressed_block_header last_block_hdr; }; /* Forward function declarations. */ static int verify_global_checksums(struct archive_read* a); static int rar5_read_data_skip(struct archive_read *a); static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS, }; /* Clears the contents of this circular deque. */ static void cdeque_clear(struct cdeque* d) { d->size = 0; d->beg_pos = 0; d->end_pos = 0; } /* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32, * 64, 256, etc. When the user will add another item above current capacity, * the circular deque will overwrite the oldest entry. */ static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) { if(d == NULL || max_capacity_power_of_2 == 0) return CDE_PARAM; d->cap_mask = max_capacity_power_of_2 - 1; d->arr = NULL; if((max_capacity_power_of_2 & d->cap_mask) > 0) return CDE_PARAM; cdeque_clear(d); d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); return d->arr ? CDE_OK : CDE_ALLOC; } /* Return the current size (not capacity) of circular deque `d`. */ static size_t cdeque_size(struct cdeque* d) { return d->size; } /* Returns the first element of current circular deque. Note that this function * doesn't perform any bounds checking. If you need bounds checking, use * `cdeque_front()` function instead. */ static void cdeque_front_fast(struct cdeque* d, void** value) { *value = (void*) d->arr[d->beg_pos]; } /* Returns the first element of current circular deque. This function * performs bounds checking. */ static int cdeque_front(struct cdeque* d, void** value) { if(d->size > 0) { cdeque_front_fast(d, value); return CDE_OK; } else return CDE_OUT_OF_BOUNDS; } /* Pushes a new element into the end of this circular deque object. If current * size will exceed capacity, the oldest element will be overwritten. */ static int cdeque_push_back(struct cdeque* d, void* item) { if(d == NULL) return CDE_PARAM; if(d->size == d->cap_mask + 1) return CDE_OUT_OF_BOUNDS; d->arr[d->end_pos] = (size_t) item; d->end_pos = (d->end_pos + 1) & d->cap_mask; d->size++; return CDE_OK; } /* Pops a front element of this circular deque object and returns its value. * This function doesn't perform any bounds checking. */ static void cdeque_pop_front_fast(struct cdeque* d, void** value) { *value = (void*) d->arr[d->beg_pos]; d->beg_pos = (d->beg_pos + 1) & d->cap_mask; d->size--; } /* Pops a front element of this circular deque object and returns its value. * This function performs bounds checking. */ static int cdeque_pop_front(struct cdeque* d, void** value) { if(!d || !value) return CDE_PARAM; if(d->size == 0) return CDE_OUT_OF_BOUNDS; cdeque_pop_front_fast(d, value); return CDE_OK; } /* Convenience function to cast filter_info** to void **. */ static void** cdeque_filter_p(struct filter_info** f) { return (void**) (size_t) f; } /* Convenience function to cast filter_info* to void *. */ static void* cdeque_filter(struct filter_info* f) { return (void**) (size_t) f; } /* Destroys this circular deque object. Deallocates the memory of the * collection buffer, but doesn't deallocate the memory of any pointer passed * to this deque as a value. */ static void cdeque_free(struct cdeque* d) { if(!d) return; if(!d->arr) return; free(d->arr); d->arr = NULL; d->beg_pos = -1; d->end_pos = -1; d->cap_mask = 0; } static inline uint8_t bf_bit_size(const struct compressed_block_header* hdr) { return hdr->block_flags_u8 & 7; } static inline uint8_t bf_byte_count(const struct compressed_block_header* hdr) { return (hdr->block_flags_u8 >> 3) & 7; } static inline uint8_t bf_is_table_present(const struct compressed_block_header* hdr) { return (hdr->block_flags_u8 >> 7) & 1; } static inline struct rar5* get_context(struct archive_read* a) { return (struct rar5*) a->format->data; } /* Convenience functions used by filter implementations. */ static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask, int64_t start, int64_t end) { if((start & mask) > (end & mask)) { ssize_t len1 = mask + 1 - (start & mask); ssize_t len2 = end & mask; memcpy(dst, &window[start & mask], len1); memcpy(dst + len1, window, len2); } else { memcpy(dst, &window[start & mask], (size_t) (end - start)); } } static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { uint8_t linear_buf[4]; circular_memcpy(linear_buf, rar->cstate.window_buf, rar->cstate.window_mask, offset, offset + 4); return archive_le32dec(linear_buf); } static void write_filter_data(struct rar5* rar, uint32_t offset, uint32_t value) { archive_le32enc(&rar->cstate.filtered_buf[offset], value); } /* Allocates a new filter descriptor and adds it to the filter array. */ static struct filter_info* add_new_filter(struct rar5* rar) { struct filter_info* f = (struct filter_info*) calloc(1, sizeof(struct filter_info)); if(!f) { return NULL; } cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); return f; } static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { int i; ssize_t dest_pos, src_pos = 0; for(i = 0; i < flt->channels; i++) { uint8_t prev_byte = 0; for(dest_pos = i; dest_pos < flt->block_length; dest_pos += flt->channels) { uint8_t byte; byte = rar->cstate.window_buf[ (rar->cstate.solid_offset + flt->block_start + src_pos) & rar->cstate.window_mask]; prev_byte -= byte; rar->cstate.filtered_buf[dest_pos] = prev_byte; src_pos++; } } return ARCHIVE_OK; } static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, int extended) { const uint32_t file_size = 0x1000000; ssize_t i; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, rar->cstate.window_mask, rar->cstate.solid_offset + flt->block_start, rar->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 4;) { uint8_t b = rar->cstate.window_buf[ (rar->cstate.solid_offset + flt->block_start + i++) & rar->cstate.window_mask]; /* * 0xE8 = x86's call (function call) * 0xE9 = x86's jmp (unconditional jump) */ if(b == 0xE8 || (extended && b == 0xE9)) { uint32_t addr; uint32_t offset = (i + flt->block_start) % file_size; addr = read_filter_data(rar, (uint32_t)(rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask); if(addr & 0x80000000) { if(((addr + offset) & 0x80000000) == 0) { write_filter_data(rar, (uint32_t)i, addr + file_size); } } else { if((addr - file_size) & 0x80000000) { uint32_t naddr = addr - offset; write_filter_data(rar, (uint32_t)i, naddr); } } i += 4; } } return ARCHIVE_OK; } static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { ssize_t i = 0; uint32_t offset; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, rar->cstate.window_mask, rar->cstate.solid_offset + flt->block_start, rar->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 3; i += 4) { uint8_t* b = &rar->cstate.window_buf[ (rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask]; if(b[3] == 0xEB) { /* 0xEB = ARM's BL (branch + link) instruction. */ offset = read_filter_data(rar, (rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask) & 0x00ffffff; offset -= (uint32_t) ((i + flt->block_start) / 4); offset = (offset & 0x00ffffff) | 0xeb000000; write_filter_data(rar, (uint32_t)i, offset); } } return ARCHIVE_OK; } static int run_filter(struct archive_read* a, struct filter_info* flt) { int ret; struct rar5* rar = get_context(a); free(rar->cstate.filtered_buf); rar->cstate.filtered_buf = malloc(flt->block_length); if(!rar->cstate.filtered_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for filter data."); return ARCHIVE_FATAL; } switch(flt->type) { case FILTER_DELTA: ret = run_delta_filter(rar, flt); break; case FILTER_E8: /* fallthrough */ case FILTER_E8E9: ret = run_e8e9_filter(rar, flt, flt->type == FILTER_E8E9); break; case FILTER_ARM: ret = run_arm_filter(rar, flt); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported filter type: 0x%x", flt->type); return ARCHIVE_FATAL; } if(ret != ARCHIVE_OK) { /* Filter has failed. */ return ret; } if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, flt->block_length, rar->cstate.last_write_ptr)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Stack overflow when submitting unpacked data"); return ARCHIVE_FATAL; } rar->cstate.last_write_ptr += flt->block_length; return ARCHIVE_OK; } /* The `push_data` function submits the selected data range to the user. * Next call of `use_data` will use the pointer, size and offset arguments * that are specified here. These arguments are pushed to the FIFO stack here, * and popped from the stack by the `use_data` function. */ static void push_data(struct archive_read* a, struct rar5* rar, const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { const uint64_t wmask = rar->cstate.window_mask; const ssize_t solid_write_ptr = (rar->cstate.solid_offset + rar->cstate.last_write_ptr) & wmask; idx_begin += rar->cstate.solid_offset; idx_end += rar->cstate.solid_offset; /* Check if our unpacked data is wrapped inside the window circular * buffer. If it's not wrapped, it can be copied out by using * a single memcpy, but when it's wrapped, we need to copy the first * part with one memcpy, and the second part with another memcpy. */ if((idx_begin & wmask) > (idx_end & wmask)) { /* The data is wrapped (begin offset sis bigger than end * offset). */ const ssize_t frag1_size = rar->cstate.window_size - (idx_begin & wmask); const ssize_t frag2_size = idx_end & wmask; /* Copy the first part of the buffer first. */ push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, rar->cstate.last_write_ptr); /* Copy the second part of the buffer. */ push_data_ready(a, rar, buf, frag2_size, rar->cstate.last_write_ptr + frag1_size); rar->cstate.last_write_ptr += frag1_size + frag2_size; } else { /* Data is not wrapped, so we can just use one call to copy the * data. */ push_data_ready(a, rar, buf + solid_write_ptr, (idx_end - idx_begin) & wmask, rar->cstate.last_write_ptr); rar->cstate.last_write_ptr += idx_end - idx_begin; } } /* Convenience function that submits the data to the user. It uses the * unpack window buffer as a source location. */ static void push_window_data(struct archive_read* a, struct rar5* rar, int64_t idx_begin, int64_t idx_end) { push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); } static int apply_filters(struct archive_read* a) { struct filter_info* flt; struct rar5* rar = get_context(a); int ret; rar->cstate.all_filters_applied = 0; /* Get the first filter that can be applied to our data. The data * needs to be fully unpacked before the filter can be run. */ if(CDE_OK == cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) { /* Check if our unpacked data fully covers this filter's * range. */ if(rar->cstate.write_ptr > flt->block_start && rar->cstate.write_ptr >= flt->block_start + flt->block_length) { /* Check if we have some data pending to be written * right before the filter's start offset. */ if(rar->cstate.last_write_ptr == flt->block_start) { /* Run the filter specified by descriptor * `flt`. */ ret = run_filter(a, flt); if(ret != ARCHIVE_OK) { /* Filter failure, return error. */ return ret; } /* Filter descriptor won't be needed anymore * after it's used, * so remove it from the * filter list and free its memory. */ (void) cdeque_pop_front(&rar->cstate.filters, cdeque_filter_p(&flt)); free(flt); } else { /* We can't run filters yet, dump the memory * right before the filter. */ push_window_data(a, rar, rar->cstate.last_write_ptr, flt->block_start); } /* Return 'filter applied or not needed' state to the * caller. */ return ARCHIVE_RETRY; } } rar->cstate.all_filters_applied = 1; return ARCHIVE_OK; } static void dist_cache_push(struct rar5* rar, int value) { int* q = rar->cstate.dist_cache; q[3] = q[2]; q[2] = q[1]; q[1] = q[0]; q[0] = value; } static int dist_cache_touch(struct rar5* rar, int idx) { int* q = rar->cstate.dist_cache; int i, dist = q[idx]; for(i = idx; i > 0; i--) q[i] = q[i - 1]; q[0] = dist; return dist; } static void free_filters(struct rar5* rar) { struct cdeque* d = &rar->cstate.filters; /* Free any remaining filters. All filters should be naturally * consumed by the unpacking function, so remaining filters after * unpacking normally mean that unpacking wasn't successful. * But still of course we shouldn't leak memory in such case. */ /* cdeque_size() is a fast operation, so we can use it as a loop * expression. */ while(cdeque_size(d) > 0) { struct filter_info* f = NULL; /* Pop_front will also decrease the collection's size. */ if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) free(f); } cdeque_clear(d); /* Also clear out the variables needed for sanity checking. */ rar->cstate.last_block_start = 0; rar->cstate.last_block_length = 0; } static void reset_file_context(struct rar5* rar) { memset(&rar->file, 0, sizeof(rar->file)); blake2sp_init(&rar->file.b2state, 32); if(rar->main.solid) { rar->cstate.solid_offset += rar->cstate.write_ptr; } else { rar->cstate.solid_offset = 0; } rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; rar->cstate.last_unstore_ptr = 0; rar->file.redir_type = REDIR_TYPE_NONE; rar->file.redir_flags = 0; free_filters(rar); } static inline int get_archive_read(struct archive* a, struct archive_read** ar) { *ar = (struct archive_read*) a; archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar5"); return ARCHIVE_OK; } static int read_ahead(struct archive_read* a, size_t how_many, const uint8_t** ptr) { if(!ptr) return 0; ssize_t avail = -1; *ptr = __archive_read_ahead(a, how_many, &avail); if(*ptr == NULL) { return 0; } return 1; } static int consume(struct archive_read* a, int64_t how_many) { int ret; ret = how_many == __archive_read_consume(a, how_many) ? ARCHIVE_OK : ARCHIVE_FATAL; return ret; } /** * Read a RAR5 variable sized numeric value. This value will be stored in * `pvalue`. The `pvalue_len` argument points to a variable that will receive * the byte count that was consumed in order to decode the `pvalue` value, plus * one. * * pvalue_len is optional and can be NULL. * * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume * the number of bytes that `pvalue_len` value contains. If the `pvalue_len` * is NULL, this consuming operation is done automatically. * * Returns 1 if *pvalue was successfully read. * Returns 0 if there was an error. In this case, *pvalue contains an * invalid value. */ static int read_var(struct archive_read* a, uint64_t* pvalue, uint64_t* pvalue_len) { uint64_t result = 0; size_t shift, i; const uint8_t* p; uint8_t b; /* We will read maximum of 8 bytes. We don't have to handle the * situation to read the RAR5 variable-sized value stored at the end of * the file, because such situation will never happen. */ if(!read_ahead(a, 8, &p)) return 0; for(shift = 0, i = 0; i < 8; i++, shift += 7) { b = p[i]; /* Strip the MSB from the input byte and add the resulting * number to the `result`. */ result += (b & (uint64_t)0x7F) << shift; /* MSB set to 1 means we need to continue decoding process. * MSB set to 0 means we're done. * * This conditional checks for the second case. */ if((b & 0x80) == 0) { if(pvalue) { *pvalue = result; } /* If the caller has passed the `pvalue_len` pointer, * store the number of consumed bytes in it and do NOT * consume those bytes, since the caller has all the * information it needs to perform */ if(pvalue_len) { *pvalue_len = 1 + i; } else { /* If the caller did not provide the * `pvalue_len` pointer, it will not have the * possibility to advance the file pointer, * because it will not know how many bytes it * needs to consume. This is why we handle * such situation here automatically. */ if(ARCHIVE_OK != consume(a, 1 + i)) { return 0; } } /* End of decoding process, return success. */ return 1; } } /* The decoded value takes the maximum number of 8 bytes. * It's a maximum number of bytes, so end decoding process here * even if the first bit of last byte is 1. */ if(pvalue) { *pvalue = result; } if(pvalue_len) { *pvalue_len = 9; } else { if(ARCHIVE_OK != consume(a, 9)) { return 0; } } return 1; } static int read_var_sized(struct archive_read* a, size_t* pvalue, size_t* pvalue_len) { uint64_t v; uint64_t v_size = 0; const int ret = pvalue_len ? read_var(a, &v, &v_size) : read_var(a, &v, NULL); if(ret == 1 && pvalue) { *pvalue = (size_t) v; } if(pvalue_len) { /* Possible data truncation should be safe. */ *pvalue_len = (size_t) v_size; } return ret; } static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; bits |= p[rar->bits.in_addr + 1] << 16; bits |= p[rar->bits.in_addr + 2] << 8; bits |= p[rar->bits.in_addr + 3]; bits <<= rar->bits.bit_addr; bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); *value = bits; return ARCHIVE_OK; } static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; bits |= (int) p[rar->bits.in_addr + 1] << 8; bits |= (int) p[rar->bits.in_addr + 2]; bits >>= (8 - rar->bits.bit_addr); *value = bits & 0xffff; return ARCHIVE_OK; } static void skip_bits(struct rar5* rar, int bits) { const int new_bits = rar->bits.bit_addr + bits; rar->bits.in_addr += new_bits >> 3; rar->bits.bit_addr = new_bits & 7; } /* n = up to 16 */ static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n, int* value) { uint16_t v; int ret, num; if(n == 0 || n > 16) { /* This is a programmer error and should never happen * in runtime. */ return ARCHIVE_FATAL; } ret = read_bits_16(rar, p, &v); if(ret != ARCHIVE_OK) return ret; num = (int) v; num >>= 16 - n; skip_bits(rar, n); if(value) *value = num; return ARCHIVE_OK; } static int read_u32(struct archive_read* a, uint32_t* pvalue) { const uint8_t* p; if(!read_ahead(a, 4, &p)) return 0; *pvalue = archive_le32dec(p); return ARCHIVE_OK == consume(a, 4) ? 1 : 0; } static int read_u64(struct archive_read* a, uint64_t* pvalue) { const uint8_t* p; if(!read_ahead(a, 8, &p)) return 0; *pvalue = archive_le64dec(p); return ARCHIVE_OK == consume(a, 8) ? 1 : 0; } static int bid_standard(struct archive_read* a) { const uint8_t* p; if(!read_ahead(a, rar5_signature_size, &p)) return -1; if(!memcmp(rar5_signature, p, rar5_signature_size)) return 30; return -1; } static int rar5_bid(struct archive_read* a, int best_bid) { int my_bid; if(best_bid > 30) return -1; my_bid = bid_standard(a); if(my_bid > -1) { return my_bid; } return -1; } static int rar5_options(struct archive_read *a, const char *key, const char *val) { (void) a; (void) key; (void) val; /* No options supported in this version. Return the ARCHIVE_WARN code * to signal the options supervisor that the unpacker didn't handle * setting this option. */ return ARCHIVE_WARN; } static void init_header(struct archive_read* a) { a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5; a->archive.archive_format_name = "RAR5"; } +static void init_window_mask(struct rar5* rar) { + if (rar->cstate.window_size) + rar->cstate.window_mask = rar->cstate.window_size - 1; + else + rar->cstate.window_mask = 0; +} + enum HEADER_FLAGS { HFL_EXTRA_DATA = 0x0001, HFL_DATA = 0x0002, HFL_SKIP_IF_UNKNOWN = 0x0004, HFL_SPLIT_BEFORE = 0x0008, HFL_SPLIT_AFTER = 0x0010, HFL_CHILD = 0x0020, HFL_INHERITED = 0x0040 }; static int process_main_locator_extra_block(struct archive_read* a, struct rar5* rar) { uint64_t locator_flags; if(!read_var(a, &locator_flags, NULL)) { return ARCHIVE_EOF; } enum LOCATOR_FLAGS { QLIST = 0x01, RECOVERY = 0x02, }; if(locator_flags & QLIST) { if(!read_var(a, &rar->qlist_offset, NULL)) { return ARCHIVE_EOF; } /* qlist is not used */ } if(locator_flags & RECOVERY) { if(!read_var(a, &rar->rr_offset, NULL)) { return ARCHIVE_EOF; } /* rr is not used */ } return ARCHIVE_OK; } static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, ssize_t* extra_data_size) { size_t hash_type; size_t value_len; if(!read_var_sized(a, &hash_type, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { return ARCHIVE_EOF; } enum HASH_TYPE { BLAKE2sp = 0x00 }; /* The file uses BLAKE2sp checksum algorithm instead of plain old * CRC32. */ if(hash_type == BLAKE2sp) { const uint8_t* p; const int hash_size = sizeof(rar->file.blake2sp); if(!read_ahead(a, hash_size, &p)) return ARCHIVE_EOF; rar->file.has_blake2 = 1; memcpy(&rar->file.blake2sp, p, hash_size); if(ARCHIVE_OK != consume(a, hash_size)) { return ARCHIVE_EOF; } *extra_data_size -= hash_size; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported hash type (0x%x)", (int) hash_type); return ARCHIVE_FATAL; } return ARCHIVE_OK; } static uint64_t time_win_to_unix(uint64_t win_time) { const size_t ns_in_sec = 10000000; const uint64_t sec_to_unix = 11644473600LL; return win_time / ns_in_sec - sec_to_unix; } static int parse_htime_item(struct archive_read* a, char unix_time, uint64_t* where, ssize_t* extra_data_size) { if(unix_time) { uint32_t time_val; if(!read_u32(a, &time_val)) return ARCHIVE_EOF; *extra_data_size -= 4; *where = (uint64_t) time_val; } else { uint64_t windows_time; if(!read_u64(a, &windows_time)) return ARCHIVE_EOF; *where = time_win_to_unix(windows_time); *extra_data_size -= 8; } return ARCHIVE_OK; } static int parse_file_extra_version(struct archive_read* a, struct archive_entry* e, ssize_t* extra_data_size) { size_t flags = 0; size_t version = 0; size_t value_len = 0; struct archive_string version_string; struct archive_string name_utf8_string; /* Flags are ignored. */ if(!read_var_sized(a, &flags, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) return ARCHIVE_EOF; if(!read_var_sized(a, &version, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) return ARCHIVE_EOF; /* extra_data_size should be zero here. */ const char* cur_filename = archive_entry_pathname_utf8(e); if(cur_filename == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Version entry without file name"); return ARCHIVE_FATAL; } archive_string_init(&version_string); archive_string_init(&name_utf8_string); /* Prepare a ;123 suffix for the filename, where '123' is the version * value of this file. */ archive_string_sprintf(&version_string, ";%zu", version); /* Build the new filename. */ archive_strcat(&name_utf8_string, cur_filename); archive_strcat(&name_utf8_string, version_string.s); /* Apply the new filename into this file's context. */ archive_entry_update_pathname_utf8(e, name_utf8_string.s); /* Free buffers. */ archive_string_free(&version_string); archive_string_free(&name_utf8_string); return ARCHIVE_OK; } static int parse_file_extra_htime(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) { char unix_time = 0; size_t flags; size_t value_len; enum HTIME_FLAGS { IS_UNIX = 0x01, HAS_MTIME = 0x02, HAS_CTIME = 0x04, HAS_ATIME = 0x08, HAS_UNIX_NS = 0x10, }; if(!read_var_sized(a, &flags, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { return ARCHIVE_EOF; } unix_time = flags & IS_UNIX; if(flags & HAS_MTIME) { parse_htime_item(a, unix_time, &rar->file.e_mtime, extra_data_size); archive_entry_set_mtime(e, rar->file.e_mtime, 0); } if(flags & HAS_CTIME) { parse_htime_item(a, unix_time, &rar->file.e_ctime, extra_data_size); archive_entry_set_ctime(e, rar->file.e_ctime, 0); } if(flags & HAS_ATIME) { parse_htime_item(a, unix_time, &rar->file.e_atime, extra_data_size); archive_entry_set_atime(e, rar->file.e_atime, 0); } if(flags & HAS_UNIX_NS) { if(!read_u32(a, &rar->file.e_unix_ns)) return ARCHIVE_EOF; *extra_data_size -= 4; } return ARCHIVE_OK; } static int parse_file_extra_redir(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) { uint64_t value_size = 0; size_t target_size = 0; char target_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; if(!read_var(a, &rar->file.redir_type, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; if(!read_var(a, &rar->file.redir_flags, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; if(!read_var_sized(a, &target_size, NULL)) return ARCHIVE_EOF; *extra_data_size -= target_size + 1; if(!read_ahead(a, target_size, &p)) return ARCHIVE_EOF; if(target_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Link target is too long"); return ARCHIVE_FATAL; } if(target_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No link target specified"); return ARCHIVE_FATAL; } memcpy(target_utf8_buf, p, target_size); target_utf8_buf[target_size] = 0; if(ARCHIVE_OK != consume(a, (int64_t)target_size)) return ARCHIVE_EOF; switch(rar->file.redir_type) { case REDIR_TYPE_UNIXSYMLINK: case REDIR_TYPE_WINSYMLINK: archive_entry_set_filetype(e, AE_IFLNK); archive_entry_update_symlink_utf8(e, target_utf8_buf); if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { archive_entry_set_symlink_type(e, AE_SYMLINK_TYPE_DIRECTORY); } else { archive_entry_set_symlink_type(e, AE_SYMLINK_TYPE_FILE); } break; case REDIR_TYPE_HARDLINK: archive_entry_set_filetype(e, AE_IFREG); archive_entry_update_hardlink_utf8(e, target_utf8_buf); break; default: /* Unknown redir type, skip it. */ break; } return ARCHIVE_OK; } static int parse_file_extra_owner(struct archive_read* a, struct archive_entry* e, ssize_t* extra_data_size) { uint64_t flags = 0; uint64_t value_size = 0; uint64_t id = 0; size_t name_len = 0; size_t name_size = 0; char namebuf[OWNER_MAXNAMELEN]; const uint8_t* p; if(!read_var(a, &flags, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; if ((flags & OWNER_USER_NAME) != 0) { if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; *extra_data_size -= name_size + 1; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if (name_size >= OWNER_MAXNAMELEN) { name_len = OWNER_MAXNAMELEN - 1; } else { name_len = name_size; } memcpy(namebuf, p, name_len); namebuf[name_len] = 0; if(ARCHIVE_OK != consume(a, (int64_t)name_size)) return ARCHIVE_EOF; archive_entry_set_uname(e, namebuf); } if ((flags & OWNER_GROUP_NAME) != 0) { if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; *extra_data_size -= name_size + 1; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if (name_size >= OWNER_MAXNAMELEN) { name_len = OWNER_MAXNAMELEN - 1; } else { name_len = name_size; } memcpy(namebuf, p, name_len); namebuf[name_len] = 0; if(ARCHIVE_OK != consume(a, (int64_t)name_size)) return ARCHIVE_EOF; archive_entry_set_gname(e, namebuf); } if ((flags & OWNER_USER_UID) != 0) { if(!read_var(a, &id, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; archive_entry_set_uid(e, (la_int64_t)id); } if ((flags & OWNER_GROUP_GID) != 0) { if(!read_var(a, &id, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; archive_entry_set_gid(e, (la_int64_t)id); } return ARCHIVE_OK; } static int process_head_file_extra(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) { size_t extra_field_size; size_t extra_field_id = 0; int ret = ARCHIVE_FATAL; size_t var_size; while(extra_data_size > 0) { if(!read_var_sized(a, &extra_field_size, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &extra_field_id, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } switch(extra_field_id) { case EX_HASH: ret = parse_file_extra_hash(a, rar, &extra_data_size); break; case EX_HTIME: ret = parse_file_extra_htime(a, e, rar, &extra_data_size); break; case EX_REDIR: ret = parse_file_extra_redir(a, e, rar, &extra_data_size); break; case EX_UOWNER: ret = parse_file_extra_owner(a, e, &extra_data_size); break; case EX_VERSION: ret = parse_file_extra_version(a, e, &extra_data_size); break; case EX_CRYPT: /* fallthrough */ case EX_SUBDATA: /* fallthrough */ default: /* Skip unsupported entry. */ return consume(a, extra_data_size); } } if(ret != ARCHIVE_OK) { /* Attribute not implemented. */ return ret; } return ARCHIVE_OK; } static int process_head_file(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { ssize_t extra_data_size = 0; size_t data_size = 0; size_t file_flags = 0; size_t file_attr = 0; size_t compression_info = 0; size_t host_os = 0; size_t name_size = 0; uint64_t unpacked_size, window_size; uint32_t mtime = 0, crc = 0; int c_method = 0, c_version = 0; char name_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; archive_entry_clear(entry); /* Do not reset file context if we're switching archives. */ if(!rar->cstate.switch_multivolume) { reset_file_context(rar); } if(block_flags & HFL_EXTRA_DATA) { size_t edata_size = 0; if(!read_var_sized(a, &edata_size, NULL)) return ARCHIVE_EOF; /* Intentional type cast from unsigned to signed. */ extra_data_size = (ssize_t) edata_size; } if(block_flags & HFL_DATA) { if(!read_var_sized(a, &data_size, NULL)) return ARCHIVE_EOF; rar->file.bytes_remaining = data_size; } else { rar->file.bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "no data found in file/service block"); return ARCHIVE_FATAL; } enum FILE_FLAGS { DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, UNKNOWN_UNPACKED_SIZE = 0x0008, }; enum FILE_ATTRS { ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4, ATTR_DIRECTORY = 0x10, }; enum COMP_INFO_FLAGS { SOLID = 0x0040, }; if(!read_var_sized(a, &file_flags, NULL)) return ARCHIVE_EOF; if(!read_var(a, &unpacked_size, NULL)) return ARCHIVE_EOF; if(file_flags & UNKNOWN_UNPACKED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Files with unknown unpacked size are not supported"); return ARCHIVE_FATAL; } rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); if(!read_var_sized(a, &file_attr, NULL)) return ARCHIVE_EOF; if(file_flags & UTIME) { if(!read_u32(a, &mtime)) return ARCHIVE_EOF; } if(file_flags & CRC32) { if(!read_u32(a, &crc)) return ARCHIVE_EOF; } if(!read_var_sized(a, &compression_info, NULL)) return ARCHIVE_EOF; c_method = (int) (compression_info >> 7) & 0x7; c_version = (int) (compression_info & 0x3f); /* RAR5 seems to limit the dictionary size to 64MB. */ window_size = (rar->file.dir > 0) ? 0 : g_unpack_window_size << ((compression_info >> 10) & 15); rar->cstate.method = c_method; rar->cstate.version = c_version + 50; /* Check if window_size is a sane value. Also, if the file is not * declared as a directory, disallow window_size == 0. */ if(window_size > (64 * 1024 * 1024) || (rar->file.dir == 0 && window_size == 0)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Declared dictionary size is not supported."); return ARCHIVE_FATAL; } /* Values up to 64M should fit into ssize_t on every * architecture. */ rar->cstate.window_size = (ssize_t) window_size; + init_window_mask(rar); rar->file.solid = (compression_info & SOLID) > 0; rar->file.service = 0; if(!read_var_sized(a, &host_os, NULL)) return ARCHIVE_EOF; enum HOST_OS { HOST_WINDOWS = 0, HOST_UNIX = 1, }; if(host_os == HOST_WINDOWS) { /* Host OS is Windows */ __LA_MODE_T mode; if(file_attr & ATTR_DIRECTORY) { if (file_attr & ATTR_READONLY) { mode = 0555 | AE_IFDIR; } else { mode = 0755 | AE_IFDIR; } } else { if (file_attr & ATTR_READONLY) { mode = 0444 | AE_IFREG; } else { mode = 0644 | AE_IFREG; } } archive_entry_set_mode(entry, mode); if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) { char *fflags_text, *ptr; /* allocate for "rdonly,hidden,system," */ fflags_text = malloc(22 * sizeof(char)); if (fflags_text != NULL) { ptr = fflags_text; if (file_attr & ATTR_READONLY) { strcpy(ptr, "rdonly,"); ptr = ptr + 7; } if (file_attr & ATTR_HIDDEN) { strcpy(ptr, "hidden,"); ptr = ptr + 7; } if (file_attr & ATTR_SYSTEM) { strcpy(ptr, "system,"); ptr = ptr + 7; } if (ptr > fflags_text) { /* Delete trailing comma */ *(ptr - 1) = '\0'; archive_entry_copy_fflags_text(entry, fflags_text); } free(fflags_text); } } } else if(host_os == HOST_UNIX) { /* Host OS is Unix */ archive_entry_set_mode(entry, (__LA_MODE_T) file_attr); } else { /* Unknown host OS */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported Host OS: 0x%x", (int) host_os); return ARCHIVE_FATAL; } if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if(name_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Filename is too long"); return ARCHIVE_FATAL; } if(name_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No filename specified"); return ARCHIVE_FATAL; } memcpy(name_utf8_buf, p, name_size); name_utf8_buf[name_size] = 0; if(ARCHIVE_OK != consume(a, name_size)) { return ARCHIVE_EOF; } archive_entry_update_pathname_utf8(entry, name_utf8_buf); if(extra_data_size > 0) { int ret = process_head_file_extra(a, entry, rar, extra_data_size); /* Sanity check. */ if(extra_data_size < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "File extra data size is not zero"); return ARCHIVE_FATAL; } if(ret != ARCHIVE_OK) return ret; } if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { rar->file.unpacked_size = (ssize_t) unpacked_size; if(rar->file.redir_type == REDIR_TYPE_NONE) archive_entry_set_size(entry, unpacked_size); } if(file_flags & UTIME) { archive_entry_set_mtime(entry, (time_t) mtime, 0); } if(file_flags & CRC32) { rar->file.stored_crc32 = crc; } if(!rar->cstate.switch_multivolume) { /* Do not reinitialize unpacking state if we're switching * archives. */ rar->cstate.block_parsing_finished = 1; rar->cstate.all_filters_applied = 1; rar->cstate.initialized = 0; } if(rar->generic.split_before > 0) { /* If now we're standing on a header that has a 'split before' * mark, it means we're standing on a 'continuation' file * header. Signal the caller that if it wants to move to * another file, it must call rar5_read_header() function * again. */ return ARCHIVE_RETRY; } else { return ARCHIVE_OK; } } static int process_head_service(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { /* Process this SERVICE block the same way as FILE blocks. */ int ret = process_head_file(a, rar, entry, block_flags); if(ret != ARCHIVE_OK) return ret; rar->file.service = 1; /* But skip the data part automatically. It's no use for the user * anyway. It contains only service data, not even needed to * properly unpack the file. */ ret = rar5_read_data_skip(a); if(ret != ARCHIVE_OK) return ret; /* After skipping, try parsing another block automatically. */ return ARCHIVE_RETRY; } static int process_head_main(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { (void) entry; int ret; size_t extra_data_size = 0; size_t extra_field_size = 0; size_t extra_field_id = 0; size_t archive_flags = 0; if(block_flags & HFL_EXTRA_DATA) { if(!read_var_sized(a, &extra_data_size, NULL)) return ARCHIVE_EOF; } else { extra_data_size = 0; } if(!read_var_sized(a, &archive_flags, NULL)) { return ARCHIVE_EOF; } enum MAIN_FLAGS { VOLUME = 0x0001, /* multi-volume archive */ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't * have it */ SOLID = 0x0004, /* solid archive */ PROTECT = 0x0008, /* contains Recovery info */ LOCK = 0x0010, /* readonly flag, not used */ }; rar->main.volume = (archive_flags & VOLUME) > 0; rar->main.solid = (archive_flags & SOLID) > 0; if(archive_flags & VOLUME_NUMBER) { size_t v = 0; if(!read_var_sized(a, &v, NULL)) { return ARCHIVE_EOF; } if (v > UINT_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid volume number"); return ARCHIVE_FATAL; } rar->main.vol_no = (unsigned int) v; } else { rar->main.vol_no = 0; } if(rar->vol.expected_vol_no > 0 && rar->main.vol_no != rar->vol.expected_vol_no) { /* Returning EOF instead of FATAL because of strange * libarchive behavior. When opening multiple files via * archive_read_open_filenames(), after reading up the whole * last file, the __archive_read_ahead function wraps up to * the first archive instead of returning EOF. */ return ARCHIVE_EOF; } if(extra_data_size == 0) { /* Early return. */ return ARCHIVE_OK; } if(!read_var_sized(a, &extra_field_size, NULL)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &extra_field_id, NULL)) { return ARCHIVE_EOF; } if(extra_field_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extra field size"); return ARCHIVE_FATAL; } enum MAIN_EXTRA { // Just one attribute here. LOCATOR = 0x01, }; switch(extra_field_id) { case LOCATOR: ret = process_main_locator_extra_block(a, rar); if(ret != ARCHIVE_OK) { /* Error while parsing main locator extra * block. */ return ret; } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported extra type (0x%x)", (int) extra_field_id); return ARCHIVE_FATAL; } return ARCHIVE_OK; } static int skip_unprocessed_bytes(struct archive_read* a) { struct rar5* rar = get_context(a); int ret; if(rar->file.bytes_remaining) { /* Use different skipping method in block merging mode than in * normal mode. If merge mode is active, rar5_read_data_skip * can't be used, because it could allow recursive use of * merge_block() * function, and this function doesn't support * recursive use. */ if(rar->merge_mode) { /* Discard whole merged block. This is valid in solid * mode as well, because the code will discard blocks * only if those blocks are safe to discard (i.e. * they're not FILE blocks). */ ret = consume(a, rar->file.bytes_remaining); if(ret != ARCHIVE_OK) { return ret; } rar->file.bytes_remaining = 0; } else { /* If we're not in merge mode, use safe skipping code. * This will ensure we'll handle solid archives * properly. */ ret = rar5_read_data_skip(a); if(ret != ARCHIVE_OK) { return ret; } } } return ARCHIVE_OK; } static int scan_for_signature(struct archive_read* a); /* Base block processing function. A 'base block' is a RARv5 header block * that tells the reader what kind of data is stored inside the block. * * From the birds-eye view a RAR file looks file this: * * ... * * There are a few types of base blocks. Those types are specified inside * the 'switch' statement in this function. For example purposes, I'll write * how a standard RARv5 file could look like here: * *
* * The structure above could describe an archive file with 3 files in it, * one service "QuickOpen" block (that is ignored by this parser), and an * end of file base block marker. * * If the file is stored in multiple archive files ("multiarchive"), it might * look like this: * * .part01.rar:
* .part02.rar:
* .part03.rar:
* * This example could describe 3 RAR files that contain ONE archived file. * Or it could describe 3 RAR files that contain 3 different files. Or 3 * RAR files than contain 2 files. It all depends what metadata is stored in * the headers of blocks. * * Each block contains info about its size, the name of the file it's * storing inside, and whether this FILE block is a continuation block of * previous archive ('split before'), and is this FILE block should be * continued in another archive ('split after'). By parsing the 'split before' * and 'split after' flags, we're able to tell if multiple base blocks * are describing one file, or multiple files (with the same filename, for * example). * * One thing to note is that if we're parsing the first block, and * we see 'split after' flag, then we need to jump over to another * block to be able to decompress rest of the data. To do this, we need * to skip the block, then switch to another file, then skip the * block,
block, and then we're standing on the proper * block. */ static int process_base_block(struct archive_read* a, struct archive_entry* entry) { struct rar5* rar = get_context(a); uint32_t hdr_crc, computed_crc; size_t raw_hdr_size = 0, hdr_size_len, hdr_size; size_t header_id = 0; size_t header_flags = 0; const uint8_t* p; int ret; /* Skip any unprocessed data for this file. */ ret = skip_unprocessed_bytes(a); if(ret != ARCHIVE_OK) return ret; /* Read the expected CRC32 checksum. */ if(!read_u32(a, &hdr_crc)) { return ARCHIVE_EOF; } /* Read header size. */ if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { return ARCHIVE_EOF; } /* Sanity check, maximum header size for RAR5 is 2MB. */ if(raw_hdr_size > (2 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Base block header is too large"); return ARCHIVE_FATAL; } hdr_size = raw_hdr_size + hdr_size_len; /* Read the whole header data into memory, maximum memory use here is * 2MB. */ if(!read_ahead(a, hdr_size, &p)) { return ARCHIVE_EOF; } /* Verify the CRC32 of the header data. */ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); if(computed_crc != hdr_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return ARCHIVE_FATAL; } /* If the checksum is OK, we proceed with parsing. */ if(ARCHIVE_OK != consume(a, hdr_size_len)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &header_id, NULL)) return ARCHIVE_EOF; if(!read_var_sized(a, &header_flags, NULL)) return ARCHIVE_EOF; rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; rar->generic.size = (int)hdr_size; rar->generic.last_header_id = (int)header_id; rar->main.endarc = 0; /* Those are possible header ids in RARv5. */ enum HEADER_TYPE { HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02, HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05, HEAD_UNKNOWN = 0xff, }; switch(header_id) { case HEAD_MAIN: ret = process_head_main(a, rar, entry, header_flags); /* Main header doesn't have any files in it, so it's * pointless to return to the caller. Retry to next * header, which should be HEAD_FILE/HEAD_SERVICE. */ if(ret == ARCHIVE_OK) return ARCHIVE_RETRY; return ret; case HEAD_SERVICE: ret = process_head_service(a, rar, entry, header_flags); return ret; case HEAD_FILE: ret = process_head_file(a, rar, entry, header_flags); return ret; case HEAD_CRYPT: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encryption is not supported"); return ARCHIVE_FATAL; case HEAD_ENDARC: rar->main.endarc = 1; /* After encountering an end of file marker, we need * to take into consideration if this archive is * continued in another file (i.e. is it part01.rar: * is there a part02.rar?) */ if(rar->main.volume) { /* In case there is part02.rar, position the * read pointer in a proper place, so we can * resume parsing. */ ret = scan_for_signature(a); if(ret == ARCHIVE_FATAL) { return ARCHIVE_EOF; } else { if(rar->vol.expected_vol_no == UINT_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header error"); return ARCHIVE_FATAL; } rar->vol.expected_vol_no = rar->main.vol_no + 1; return ARCHIVE_OK; } } else { return ARCHIVE_EOF; } case HEAD_MARK: return ARCHIVE_EOF; default: if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header type error"); return ARCHIVE_FATAL; } else { /* If the block is marked as 'skip if unknown', * do as the flag says: skip the block * instead on failing on it. */ return ARCHIVE_RETRY; } } #if !defined WIN32 // Not reached. archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal unpacker error"); return ARCHIVE_FATAL; #endif } static int skip_base_block(struct archive_read* a) { int ret; struct rar5* rar = get_context(a); /* Create a new local archive_entry structure that will be operated on * by header reader; operations on this archive_entry will be discarded. */ struct archive_entry* entry = archive_entry_new(); ret = process_base_block(a, entry); /* Discard operations on this archive_entry structure. */ archive_entry_free(entry); if(ret == ARCHIVE_FATAL) return ret; if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) return ARCHIVE_OK; if(ret == ARCHIVE_OK) return ARCHIVE_RETRY; else return ret; } static int rar5_read_header(struct archive_read *a, struct archive_entry *entry) { struct rar5* rar = get_context(a); int ret; if(rar->header_initialized == 0) { init_header(a); rar->header_initialized = 1; } if(rar->skipped_magic == 0) { if(ARCHIVE_OK != consume(a, rar5_signature_size)) { return ARCHIVE_EOF; } rar->skipped_magic = 1; } do { ret = process_base_block(a, entry); } while(ret == ARCHIVE_RETRY || (rar->main.endarc > 0 && ret == ARCHIVE_OK)); return ret; } static void init_unpack(struct rar5* rar) { rar->file.calculated_crc32 = 0; - if (rar->cstate.window_size) - rar->cstate.window_mask = rar->cstate.window_size - 1; - else - rar->cstate.window_mask = 0; + init_window_mask(rar); free(rar->cstate.window_buf); free(rar->cstate.filtered_buf); if(rar->cstate.window_size > 0) { rar->cstate.window_buf = calloc(1, rar->cstate.window_size); rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); } else { rar->cstate.window_buf = NULL; rar->cstate.filtered_buf = NULL; } rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); } static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { int verify_crc; if(rar->skip_mode) { #if defined CHECK_CRC_ON_SOLID_SKIP verify_crc = 1; #else verify_crc = 0; #endif } else verify_crc = 1; if(verify_crc) { /* Don't update CRC32 if the file doesn't have the * `stored_crc32` info filled in. */ if(rar->file.stored_crc32 > 0) { rar->file.calculated_crc32 = crc32(rar->file.calculated_crc32, p, to_read); } /* Check if the file uses an optional BLAKE2sp checksum * algorithm. */ if(rar->file.has_blake2 > 0) { /* Return value of the `update` function is always 0, * so we can explicitly ignore it here. */ (void) blake2sp_update(&rar->file.b2state, p, to_read); } } } static int create_decode_tables(uint8_t* bit_length, struct decode_table* table, int size) { int code, upper_limit = 0, i, lc[16]; uint32_t decode_pos_clone[rar5_countof(table->decode_pos)]; ssize_t cur_len, quick_data_size; memset(&lc, 0, sizeof(lc)); memset(table->decode_num, 0, sizeof(table->decode_num)); table->size = size; table->quick_bits = size == HUFF_NC ? 10 : 7; for(i = 0; i < size; i++) { lc[bit_length[i] & 15]++; } lc[0] = 0; table->decode_pos[0] = 0; table->decode_len[0] = 0; for(i = 1; i < 16; i++) { upper_limit += lc[i]; table->decode_len[i] = upper_limit << (16 - i); table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1]; upper_limit <<= 1; } memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); for(i = 0; i < size; i++) { uint8_t clen = bit_length[i] & 15; if(clen > 0) { int last_pos = decode_pos_clone[clen]; table->decode_num[last_pos] = i; decode_pos_clone[clen]++; } } quick_data_size = (int64_t)1 << table->quick_bits; cur_len = 1; for(code = 0; code < quick_data_size; code++) { int bit_field = code << (16 - table->quick_bits); int dist, pos; while(cur_len < rar5_countof(table->decode_len) && bit_field >= table->decode_len[cur_len]) { cur_len++; } table->quick_len[code] = (uint8_t) cur_len; dist = bit_field - table->decode_len[cur_len - 1]; dist >>= (16 - cur_len); pos = table->decode_pos[cur_len & 15] + dist; if(cur_len < rar5_countof(table->decode_pos) && pos < size) { table->quick_num[code] = table->decode_num[pos]; } else { table->quick_num[code] = 0; } } return ARCHIVE_OK; } static int decode_number(struct archive_read* a, struct decode_table* table, const uint8_t* p, uint16_t* num) { int i, bits, dist; uint16_t bitfield; uint32_t pos; struct rar5* rar = get_context(a); if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { return ARCHIVE_EOF; } bitfield &= 0xfffe; if(bitfield < table->decode_len[table->quick_bits]) { int code = bitfield >> (16 - table->quick_bits); skip_bits(rar, table->quick_len[code]); *num = table->quick_num[code]; return ARCHIVE_OK; } bits = 15; for(i = table->quick_bits + 1; i < 15; i++) { if(bitfield < table->decode_len[i]) { bits = i; break; } } skip_bits(rar, bits); dist = bitfield - table->decode_len[bits - 1]; dist >>= (16 - bits); pos = table->decode_pos[bits] + dist; if(pos >= table->size) pos = 0; *num = table->decode_num[pos]; return ARCHIVE_OK; } /* Reads and parses Huffman tables from the beginning of the block. */ static int parse_tables(struct archive_read* a, struct rar5* rar, const uint8_t* p) { int ret, value, i, w, idx = 0; uint8_t bit_length[HUFF_BC], table[HUFF_TABLE_SIZE], nibble_mask = 0xF0, nibble_shift = 4; enum { ESCAPE = 15 }; /* The data for table generation is compressed using a simple RLE-like * algorithm when storing zeroes, so we need to unpack it first. */ for(w = 0, i = 0; w < HUFF_BC;) { if(i >= rar->cstate.cur_block_size) { /* Truncated data, can't continue. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated data in huffman tables"); return ARCHIVE_FATAL; } value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) ++i; nibble_mask ^= 0xFF; nibble_shift ^= 4; /* Values smaller than 15 is data, so we write it directly. * Value 15 is a flag telling us that we need to unpack more * bytes. */ if(value == ESCAPE) { value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) ++i; nibble_mask ^= 0xFF; nibble_shift ^= 4; if(value == 0) { /* We sometimes need to write the actual value * of 15, so this case handles that. */ bit_length[w++] = ESCAPE; } else { int k; /* Fill zeroes. */ for(k = 0; (k < value + 2) && (w < HUFF_BC); k++) { bit_length[w++] = 0; } } } else { bit_length[w++] = value; } } rar->bits.in_addr = i; rar->bits.bit_addr = nibble_shift ^ 4; ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); return ARCHIVE_FATAL; } for(i = 0; i < HUFF_TABLE_SIZE;) { uint16_t num; if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) { /* Truncated data, can't continue. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated data in huffman tables (#2)"); return ARCHIVE_FATAL; } ret = decode_number(a, &rar->cstate.bd, p, &num); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); return ARCHIVE_FATAL; } if(num < 16) { /* 0..15: store directly */ table[i] = (uint8_t) num; i++; continue; } if(num < 18) { /* 16..17: repeat previous code */ uint16_t n; if(ARCHIVE_OK != read_bits_16(rar, p, &n)) return ARCHIVE_EOF; if(num == 16) { n >>= 13; n += 3; skip_bits(rar, 3); } else { n >>= 9; n += 11; skip_bits(rar, 7); } if(i > 0) { while(n-- > 0 && i < HUFF_TABLE_SIZE) { table[i] = table[i - 1]; i++; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unexpected error when decoding " "huffman tables"); return ARCHIVE_FATAL; } continue; } /* other codes: fill with zeroes `n` times */ uint16_t n; if(ARCHIVE_OK != read_bits_16(rar, p, &n)) return ARCHIVE_EOF; if(num == 18) { n >>= 13; n += 3; skip_bits(rar, 3); } else { n >>= 9; n += 11; skip_bits(rar, 7); } while(n-- > 0 && i < HUFF_TABLE_SIZE) table[i++] = 0; } ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create literal table"); return ARCHIVE_FATAL; } idx += HUFF_NC; ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create distance table"); return ARCHIVE_FATAL; } idx += HUFF_DC; ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create lower bits of distances table"); return ARCHIVE_FATAL; } idx += HUFF_LDC; ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create repeating distances table"); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Parses the block header, verifies its CRC byte, and saves the header * fields inside the `hdr` pointer. */ static int parse_block_header(struct archive_read* a, const uint8_t* p, ssize_t* block_size, struct compressed_block_header* hdr) { memcpy(hdr, p, sizeof(struct compressed_block_header)); if(bf_byte_count(hdr) > 2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block header size (was %d, max is 2)", bf_byte_count(hdr)); return ARCHIVE_FATAL; } /* This should probably use bit reader interface in order to be more * future-proof. */ *block_size = 0; switch(bf_byte_count(hdr)) { /* 1-byte block size */ case 0: *block_size = *(const uint8_t*) &p[2]; break; /* 2-byte block size */ case 1: *block_size = archive_le16dec(&p[2]); break; /* 3-byte block size */ case 2: *block_size = archive_le32dec(&p[2]); *block_size &= 0x00FFFFFF; break; /* Other block sizes are not supported. This case is not * reached, because we have an 'if' guard before the switch * that makes sure of it. */ default: return ARCHIVE_FATAL; } /* Verify the block header checksum. 0x5A is a magic value and is * always * constant. */ uint8_t calculated_cksum = 0x5A ^ (uint8_t) hdr->block_flags_u8 ^ (uint8_t) *block_size ^ (uint8_t) (*block_size >> 8) ^ (uint8_t) (*block_size >> 16); if(calculated_cksum != hdr->block_cksum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Block checksum error: got 0x%x, expected 0x%x", hdr->block_cksum, calculated_cksum); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Convenience function used during filter processing. */ static int parse_filter_data(struct rar5* rar, const uint8_t* p, uint32_t* filter_data) { int i, bytes; uint32_t data = 0; if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) return ARCHIVE_EOF; bytes++; for(i = 0; i < bytes; i++) { uint16_t byte; if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { return ARCHIVE_EOF; } /* Cast to uint32_t will ensure the shift operation will not * produce undefined result. */ data += ((uint32_t) byte >> 8) << (i * 8); skip_bits(rar, 8); } *filter_data = data; return ARCHIVE_OK; } /* Function is used during sanity checking. */ static int is_valid_filter_block_start(struct rar5* rar, uint32_t start) { const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; const int64_t last_bs = rar->cstate.last_block_start; const ssize_t last_bl = rar->cstate.last_block_length; if(last_bs == 0 || last_bl == 0) { /* We didn't have any filters yet, so accept this offset. */ return 1; } if(block_start >= last_bs + last_bl) { /* Current offset is bigger than last block's end offset, so * accept current offset. */ return 1; } /* Any other case is not a normal situation and we should fail. */ return 0; } /* The function will create a new filter, read its parameters from the input * stream and add it to the filter collection. */ static int parse_filter(struct archive_read* ar, const uint8_t* p) { uint32_t block_start, block_length; uint16_t filter_type; struct rar5* rar = get_context(ar); /* Read the parameters from the input stream. */ if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) return ARCHIVE_EOF; if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) return ARCHIVE_EOF; if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) return ARCHIVE_EOF; filter_type >>= 13; skip_bits(rar, 3); /* Perform some sanity checks on this filter parameters. Note that we * allow only DELTA, E8/E9 and ARM filters here, because rest of * filters are not used in RARv5. */ if(block_length < 4 || block_length > 0x400000 || filter_type > FILTER_ARM || !is_valid_filter_block_start(rar, block_start)) { archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filter encountered"); return ARCHIVE_FATAL; } /* Allocate a new filter. */ struct filter_info* filt = add_new_filter(rar); if(filt == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate memory for a filter descriptor."); return ARCHIVE_FATAL; } filt->type = filter_type; filt->block_start = rar->cstate.write_ptr + block_start; filt->block_length = block_length; rar->cstate.last_block_start = filt->block_start; rar->cstate.last_block_length = filt->block_length; /* Read some more data in case this is a DELTA filter. Other filter * types don't require any additional data over what was already * read. */ if(filter_type == FILTER_DELTA) { int channels; if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) return ARCHIVE_EOF; filt->channels = channels + 1; } return ARCHIVE_OK; } static int decode_code_length(struct rar5* rar, const uint8_t* p, uint16_t code) { int lbits, length = 2; if(code < 8) { lbits = 0; length += code; } else { lbits = code / 4 - 1; length += (4 | (code & 3)) << lbits; } if(lbits > 0) { int add; if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) return -1; length += add; } return length; } static int copy_string(struct archive_read* a, int len, int dist) { struct rar5* rar = get_context(a); const uint64_t cmask = rar->cstate.window_mask; const uint64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; int i; if (rar->cstate.window_buf == NULL) return ARCHIVE_FATAL; /* The unpacker spends most of the time in this function. It would be * a good idea to introduce some optimizations here. * * Just remember that this loop treats buffers that overlap differently * than buffers that do not overlap. This is why a simple memcpy(3) * call will not be enough. */ for(i = 0; i < len; i++) { const ssize_t write_idx = (write_ptr + i) & cmask; const ssize_t read_idx = (write_ptr + i - dist) & cmask; rar->cstate.window_buf[write_idx] = rar->cstate.window_buf[read_idx]; } rar->cstate.write_ptr += len; return ARCHIVE_OK; } static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { struct rar5* rar = get_context(a); uint16_t num; int ret; const uint64_t cmask = rar->cstate.window_mask; const struct compressed_block_header* hdr = &rar->last_block_hdr; const uint8_t bit_size = 1 + bf_bit_size(hdr); while(1) { if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > (rar->cstate.window_size >> 1)) { /* Don't allow growing data by more than half of the * window size at a time. In such case, break the loop; * next call to this function will continue processing * from this moment. */ break; } if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && rar->bits.bit_addr >= bit_size)) { /* If the program counter is here, it means the * function has finished processing the block. */ rar->cstate.block_parsing_finished = 1; break; } /* Decode the next literal. */ if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { return ARCHIVE_EOF; } /* Num holds a decompression literal, or 'command code'. * * - Values lower than 256 are just bytes. Those codes * can be stored in the output buffer directly. * - * - Code 256 defines a new filter, which is later used to + * - Code 256 defines a new filter, which is later used to * ransform the data block accordingly to the filter type. * The data block needs to be fully uncompressed first. * * - Code bigger than 257 and smaller than 262 define * a repetition pattern that should be copied from * an already uncompressed chunk of data. */ if(num < 256) { /* Directly store the byte. */ int64_t write_idx = rar->cstate.solid_offset + rar->cstate.write_ptr++; rar->cstate.window_buf[write_idx & cmask] = (uint8_t) num; continue; } else if(num >= 262) { uint16_t dist_slot; int len = decode_code_length(rar, p, num - 262), dbits, dist = 1; if(len == -1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the code length"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, &dist_slot)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the distance slot"); return ARCHIVE_FATAL; } if(dist_slot < 4) { dbits = 0; dist += dist_slot; } else { dbits = dist_slot / 2 - 1; /* Cast to uint32_t will make sure the shift * left operation won't produce undefined * result. Then, the uint32_t type will * be implicitly casted to int. */ dist += (uint32_t) (2 | (dist_slot & 1)) << dbits; } if(dbits > 0) { if(dbits >= 4) { uint32_t add = 0; uint16_t low_dist; if(dbits > 4) { if(ARCHIVE_OK != read_bits_32( rar, p, &add)) { /* Return EOF if we * can't read more * data. */ return ARCHIVE_EOF; } skip_bits(rar, dbits - 4); add = (add >> ( 36 - dbits)) << 4; dist += add; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.ldd, p, &low_dist)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the " "distance slot"); return ARCHIVE_FATAL; } if(dist >= INT_MAX - low_dist - 1) { /* This only happens in * invalid archives. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Distance pointer " "overflow"); return ARCHIVE_FATAL; } dist += low_dist; } else { /* dbits is one of [0,1,2,3] */ int add; if(ARCHIVE_OK != read_consume_bits(rar, p, dbits, &add)) { /* Return EOF if we can't read * more data. */ return ARCHIVE_EOF; } dist += add; } } if(dist > 0x100) { len++; if(dist > 0x2000) { len++; if(dist > 0x40000) { len++; } } } dist_cache_push(rar, dist); rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) return ARCHIVE_FATAL; continue; } else if(num == 256) { /* Create a filter. */ ret = parse_filter(a, p); if(ret != ARCHIVE_OK) return ret; continue; } else if(num == 257) { if(rar->cstate.last_len != 0) { if(ARCHIVE_OK != copy_string(a, rar->cstate.last_len, rar->cstate.dist_cache[0])) { return ARCHIVE_FATAL; } } continue; } else if(num < 262) { const int idx = num - 258; const int dist = dist_cache_touch(rar, idx); uint16_t len_slot; int len; if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, &len_slot)) { return ARCHIVE_FATAL; } len = decode_code_length(rar, p, len_slot); rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) return ARCHIVE_FATAL; continue; } /* The program counter shouldn't reach here. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block code: 0x%x", num); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Binary search for the RARv5 signature. */ static int scan_for_signature(struct archive_read* a) { const uint8_t* p; const int chunk_size = 512; ssize_t i; /* If we're here, it means we're on an 'unknown territory' data. * There's no indication what kind of data we're reading here. * It could be some text comment, any kind of binary data, * digital sign, dragons, etc. * * We want to find a valid RARv5 magic header inside this unknown * data. */ /* Is it possible in libarchive to just skip everything until the * end of the file? If so, it would be a better approach than the * current implementation of this function. */ while(1) { if(!read_ahead(a, chunk_size, &p)) return ARCHIVE_EOF; for(i = 0; i < chunk_size - rar5_signature_size; i++) { if(memcmp(&p[i], rar5_signature, rar5_signature_size) == 0) { /* Consume the number of bytes we've used to * search for the signature, as well as the * number of bytes used by the signature * itself. After this we should be standing * on a valid base block header. */ (void) consume(a, i + rar5_signature_size); return ARCHIVE_OK; } } consume(a, chunk_size); } return ARCHIVE_FATAL; } /* This function will switch the multivolume archive file to another file, * i.e. from part03 to part 04. */ static int advance_multivolume(struct archive_read* a) { int lret; struct rar5* rar = get_context(a); /* A small state machine that will skip unnecessary data, needed to * switch from one multivolume to another. Such skipping is needed if * we want to be an stream-oriented (instead of file-oriented) * unpacker. * * The state machine starts with `rar->main.endarc` == 0. It also * assumes that current stream pointer points to some base block * header. * * The `endarc` field is being set when the base block parsing * function encounters the 'end of archive' marker. */ while(1) { if(rar->main.endarc == 1) { int looping = 1; rar->main.endarc = 0; while(looping) { lret = skip_base_block(a); switch(lret) { case ARCHIVE_RETRY: /* Continue looping. */ break; case ARCHIVE_OK: /* Break loop. */ looping = 0; break; default: /* Forward any errors to the * caller. */ return lret; } } break; } else { /* Skip current base block. In order to properly skip * it, we really need to simply parse it and discard * the results. */ lret = skip_base_block(a); if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED) return lret; /* The `skip_base_block` function tells us if we * should continue with skipping, or we should stop * skipping. We're trying to skip everything up to * a base FILE block. */ if(lret != ARCHIVE_RETRY) { /* If there was an error during skipping, or we * have just skipped a FILE base block... */ if(rar->main.endarc == 0) { return lret; } else { continue; } } } } return ARCHIVE_OK; } /* Merges the partial block from the first multivolume archive file, and * partial block from the second multivolume archive file. The result is * a chunk of memory containing the whole block, and the stream pointer * is advanced to the next block in the second multivolume archive file. */ static int merge_block(struct archive_read* a, ssize_t block_size, const uint8_t** p) { struct rar5* rar = get_context(a); ssize_t cur_block_size, partial_offset = 0; const uint8_t* lp; int ret; if(rar->merge_mode) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Recursive merge is not allowed"); return ARCHIVE_FATAL; } /* Set a flag that we're in the switching mode. */ rar->cstate.switch_multivolume = 1; /* Reallocate the memory which will hold the whole block. */ if(rar->vol.push_buf) free((void*) rar->vol.push_buf); /* Increasing the allocation block by 8 is due to bit reading functions, * which are using additional 2 or 4 bytes. Allocating the block size * by exact value would make bit reader perform reads from invalid * memory block when reading the last byte from the buffer. */ rar->vol.push_buf = malloc(block_size + 8); if(!rar->vol.push_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a merge block buffer."); return ARCHIVE_FATAL; } /* Valgrind complains if the extension block for bit reader is not * initialized, so initialize it. */ memset(&rar->vol.push_buf[block_size], 0, 8); /* A single block can span across multiple multivolume archive files, * so we use a loop here. This loop will consume enough multivolume * archive files until the whole block is read. */ while(1) { /* Get the size of current block chunk in this multivolume * archive file and read it. */ cur_block_size = rar5_min(rar->file.bytes_remaining, block_size - partial_offset); if(cur_block_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encountered block size == 0 during block merge"); return ARCHIVE_FATAL; } if(!read_ahead(a, cur_block_size, &lp)) return ARCHIVE_EOF; /* Sanity check; there should never be a situation where this * function reads more data than the block's size. */ if(partial_offset + cur_block_size > block_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Consumed too much data when merging blocks."); return ARCHIVE_FATAL; } /* Merge previous block chunk with current block chunk, * or create first block chunk if this is our first * iteration. */ memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); /* Advance the stream read pointer by this block chunk size. */ if(ARCHIVE_OK != consume(a, cur_block_size)) return ARCHIVE_EOF; /* Update the pointers. `partial_offset` contains information * about the sum of merged block chunks. */ partial_offset += cur_block_size; rar->file.bytes_remaining -= cur_block_size; /* If `partial_offset` is the same as `block_size`, this means * we've merged all block chunks and we have a valid full * block. */ if(partial_offset == block_size) { break; } /* If we don't have any bytes to read, this means we should * switch to another multivolume archive file. */ if(rar->file.bytes_remaining == 0) { rar->merge_mode++; ret = advance_multivolume(a); rar->merge_mode--; if(ret != ARCHIVE_OK) { return ret; } } } *p = rar->vol.push_buf; /* If we're here, we can resume unpacking by processing the block * pointed to by the `*p` memory pointer. */ return ARCHIVE_OK; } static int process_block(struct archive_read* a) { const uint8_t* p; struct rar5* rar = get_context(a); int ret; /* If we don't have any data to be processed, this most probably means * we need to switch to the next volume. */ if(rar->main.volume && rar->file.bytes_remaining == 0) { ret = advance_multivolume(a); if(ret != ARCHIVE_OK) return ret; } if(rar->cstate.block_parsing_finished) { ssize_t block_size; /* The header size won't be bigger than 6 bytes. */ if(!read_ahead(a, 6, &p)) { /* Failed to prefetch data block header. */ return ARCHIVE_EOF; } /* * Read block_size by parsing block header. Validate the header * by calculating CRC byte stored inside the header. Size of * the header is not constant (block size can be stored either * in 1 or 2 bytes), that's why block size is left out from the * `compressed_block_header` structure and returned by * `parse_block_header` as the second argument. */ ret = parse_block_header(a, p, &block_size, &rar->last_block_hdr); if(ret != ARCHIVE_OK) { return ret; } /* Skip block header. Next data is huffman tables, * if present. */ ssize_t to_skip = sizeof(struct compressed_block_header) + bf_byte_count(&rar->last_block_hdr) + 1; if(ARCHIVE_OK != consume(a, to_skip)) return ARCHIVE_EOF; rar->file.bytes_remaining -= to_skip; /* The block size gives information about the whole block size, * but the block could be stored in split form when using * multi-volume archives. In this case, the block size will be * bigger than the actual data stored in this file. Remaining * part of the data will be in another file. */ ssize_t cur_block_size = rar5_min(rar->file.bytes_remaining, block_size); if(block_size > rar->file.bytes_remaining) { /* If current blocks' size is bigger than our data * size, this means we have a multivolume archive. * In this case, skip all base headers until the end * of the file, proceed to next "partXXX.rar" volume, * find its signature, skip all headers up to the first * FILE base header, and continue from there. * * Note that `merge_block` will update the `rar` * context structure quite extensively. */ ret = merge_block(a, block_size, &p); if(ret != ARCHIVE_OK) { return ret; } cur_block_size = block_size; /* Current stream pointer should be now directly * *after* the block that spanned through multiple * archive files. `p` pointer should have the data of * the *whole* block (merged from partial blocks * stored in multiple archives files). */ } else { rar->cstate.switch_multivolume = 0; /* Read the whole block size into memory. This can take * up to 8 megabytes of memory in theoretical cases. * Might be worth to optimize this and use a standard * chunk of 4kb's. */ if(!read_ahead(a, 4 + cur_block_size, &p)) { /* Failed to prefetch block data. */ return ARCHIVE_EOF; } } rar->cstate.block_buf = p; rar->cstate.cur_block_size = cur_block_size; rar->cstate.block_parsing_finished = 0; rar->bits.in_addr = 0; rar->bits.bit_addr = 0; if(bf_is_table_present(&rar->last_block_hdr)) { /* Load Huffman tables. */ ret = parse_tables(a, rar, p); if(ret != ARCHIVE_OK) { /* Error during decompression of Huffman * tables. */ return ret; } } } else { /* Block parsing not finished, reuse previous memory buffer. */ p = rar->cstate.block_buf; } /* Uncompress the block, or a part of it, depending on how many bytes * will be generated by uncompressing the block. * * In case too many bytes will be generated, calling this function * again will resume the uncompression operation. */ ret = do_uncompress_block(a, p); if(ret != ARCHIVE_OK) { return ret; } if(rar->cstate.block_parsing_finished && rar->cstate.switch_multivolume == 0 && rar->cstate.cur_block_size > 0) { /* If we're processing a normal block, consume the whole * block. We can do this because we've already read the whole * block to memory. */ if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) return ARCHIVE_FATAL; rar->file.bytes_remaining -= rar->cstate.cur_block_size; } else if(rar->cstate.switch_multivolume) { /* Don't consume the block if we're doing multivolume * processing. The volume switching function will consume * the proper count of bytes instead. */ rar->cstate.switch_multivolume = 0; } return ARCHIVE_OK; } /* Pops the `buf`, `size` and `offset` from the "data ready" stack. * * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY * when there is no data on the stack. */ static int use_data(struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { int i; for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { struct data_ready *d = &rar->cstate.dready[i]; if(d->used) { if(buf) *buf = d->buf; if(size) *size = d->size; if(offset) *offset = d->offset; d->used = 0; return ARCHIVE_OK; } } return ARCHIVE_RETRY; } /* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready * FIFO stack. Those values will be popped from this stack by the `use_data` * function. */ static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset) { int i; /* Don't push if we're in skip mode. This is needed because solid * streams need full processing even if we're skipping data. After * fully processing the stream, we need to discard the generated bytes, * because we're interested only in the side effect: building up the * internal window circular buffer. This window buffer will be used * later during unpacking of requested data. */ if(rar->skip_mode) return ARCHIVE_OK; /* Sanity check. */ if(offset != rar->file.last_offset + rar->file.last_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Sanity check error: output stream is not continuous"); return ARCHIVE_FATAL; } for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { struct data_ready* d = &rar->cstate.dready[i]; if(!d->used) { d->used = 1; d->buf = buf; d->size = size; d->offset = offset; /* These fields are used only in sanity checking. */ rar->file.last_offset = offset; rar->file.last_size = size; /* Calculate the checksum of this new block before * submitting data to libarchive's engine. */ update_crc(rar, d->buf, d->size); return ARCHIVE_OK; } } /* Program counter will reach this code if the `rar->cstate.data_ready` * stack will be filled up so that no new entries will be allowed. The * code shouldn't allow such situation to occur. So we treat this case * as an internal error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Error: premature end of data_ready stack"); return ARCHIVE_FATAL; } /* This function uncompresses the data that is stored in the base * block. * * The FILE base block looks like this: * *
... * * The
is a block header, that is parsed in parse_block_header(). * It's a "compressed_block_header" structure, containing metadata needed * to know when we should stop looking for more blocks. * * contain data needed to set up the huffman tables, needed * for the actual decompression. * * Each consists of series of literals: * * ... * * Those literals generate the uncompression data. They operate on a circular * buffer, sometimes writing raw data into it, sometimes referencing * some previous data inside this buffer, and sometimes declaring a filter * that will need to be executed on the data stored in the circular buffer. * It all depends on the literal that is used. * * Sometimes blocks produce output data, sometimes they don't. For example, for * some huge files that use lots of filters, sometimes a block is filled with * only filter declaration literals. Such blocks won't produce any data in the * circular buffer. * * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte, * because a literal can reference previously decompressed data. For example, * there can be a literal that says: 'append a byte 0xFE here', and after * it another literal can say 'append 1 megabyte of data from circular buffer * offset 0x12345'. This is how RAR format handles compressing repeated * patterns. * * The RAR compressor creates those literals and the actual efficiency of * compression depends on what those literals are. The literals can also * be seen as a kind of a non-turing-complete virtual machine that simply * tells the decompressor what it should do. * */ static int do_uncompress_file(struct archive_read* a) { struct rar5* rar = get_context(a); int ret; int64_t max_end_pos; if(!rar->cstate.initialized) { /* Don't perform full context reinitialization if we're * processing a solid archive. */ if(!rar->main.solid || !rar->cstate.window_buf) { init_unpack(rar); } rar->cstate.initialized = 1; } if(rar->cstate.all_filters_applied == 1) { /* We use while(1) here, but standard case allows for just 1 * iteration. The loop will iterate if process_block() didn't * generate any data at all. This can happen if the block * contains only filter definitions (this is common in big * files). */ while(1) { ret = process_block(a); if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) return ret; if(rar->cstate.last_write_ptr == rar->cstate.write_ptr) { /* The block didn't generate any new data, * so just process a new block. */ continue; } /* The block has generated some new data, so break * the loop. */ break; } } /* Try to run filters. If filters won't be applied, it means that * insufficient data was generated. */ ret = apply_filters(a); if(ret == ARCHIVE_RETRY) { return ARCHIVE_OK; } else if(ret == ARCHIVE_FATAL) { return ARCHIVE_FATAL; } /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ if(cdeque_size(&rar->cstate.filters) > 0) { /* Check if we can write something before hitting first * filter. */ struct filter_info* flt; /* Get the block_start offset from the first filter. */ if(CDE_OK != cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Can't read first filter"); return ARCHIVE_FATAL; } max_end_pos = rar5_min(flt->block_start, rar->cstate.write_ptr); } else { /* There are no filters defined, or all filters were applied. * This means we can just store the data without any * postprocessing. */ max_end_pos = rar->cstate.write_ptr; } if(max_end_pos == rar->cstate.last_write_ptr) { /* We can't write anything yet. The block uncompression * function did not generate enough data, and no filter can be * applied. At the same time we don't have any data that can be * stored without filter postprocessing. This means we need to * wait for more data to be generated, so we can apply the * filters. * * Signal the caller that we need more data to be able to do * anything. */ return ARCHIVE_RETRY; } else { /* We can write the data before hitting the first filter. * So let's do it. The push_window_data() function will * effectively return the selected data block to the user * application. */ push_window_data(a, rar, rar->cstate.last_write_ptr, max_end_pos); rar->cstate.last_write_ptr = max_end_pos; } return ARCHIVE_OK; } static int uncompress_file(struct archive_read* a) { int ret; while(1) { /* Sometimes the uncompression function will return a * 'retry' signal. If this will happen, we have to retry * the function. */ ret = do_uncompress_file(a); if(ret != ARCHIVE_RETRY) return ret; } } static int do_unstore_file(struct archive_read* a, struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { const uint8_t* p; if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && rar->generic.split_after > 0) { int ret; rar->cstate.switch_multivolume = 1; ret = advance_multivolume(a); rar->cstate.switch_multivolume = 0; if(ret != ARCHIVE_OK) { /* Failed to advance to next multivolume archive * file. */ return ret; } } size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); if(to_read == 0) { return ARCHIVE_EOF; } if(!read_ahead(a, to_read, &p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "I/O error when unstoring file"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != consume(a, to_read)) { return ARCHIVE_EOF; } if(buf) *buf = p; if(size) *size = to_read; if(offset) *offset = rar->cstate.last_unstore_ptr; rar->file.bytes_remaining -= to_read; rar->cstate.last_unstore_ptr += to_read; update_crc(rar, p, to_read); return ARCHIVE_OK; } static int do_unpack(struct archive_read* a, struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { enum COMPRESSION_METHOD { STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, BEST = 5 }; if(rar->file.service > 0) { return do_unstore_file(a, rar, buf, size, offset); } else { switch(rar->cstate.method) { case STORE: return do_unstore_file(a, rar, buf, size, offset); case FASTEST: /* fallthrough */ case FAST: /* fallthrough */ case NORMAL: /* fallthrough */ case GOOD: /* fallthrough */ case BEST: return uncompress_file(a); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Compression method not supported: 0x%x", rar->cstate.method); return ARCHIVE_FATAL; } } #if !defined WIN32 /* Not reached. */ return ARCHIVE_OK; #endif } static int verify_checksums(struct archive_read* a) { int verify_crc; struct rar5* rar = get_context(a); /* Check checksums only when actually unpacking the data. There's no * need to calculate checksum when we're skipping data in solid archives * (skipping in solid archives is the same thing as unpacking compressed * data and discarding the result). */ if(!rar->skip_mode) { /* Always check checksums if we're not in skip mode */ verify_crc = 1; } else { /* We can override the logic above with a compile-time option * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, * and it will check checksums of unpacked data even when * we're skipping it. */ #if defined CHECK_CRC_ON_SOLID_SKIP /* Debug case */ verify_crc = 1; #else /* Normal case */ verify_crc = 0; #endif } if(verify_crc) { /* During unpacking, on each unpacked block we're calling the * update_crc() function. Since we are here, the unpacking * process is already over and we can check if calculated * checksum (CRC32 or BLAKE2sp) is the same as what is stored * in the archive. */ if(rar->file.stored_crc32 > 0) { /* Check CRC32 only when the file contains a CRC32 * value for this file. */ if(rar->file.calculated_crc32 != rar->file.stored_crc32) { /* Checksums do not match; the unpacked file * is corrupted. */ DEBUG_CODE { printf("Checksum error: CRC32 " "(was: %08x, expected: %08x)\n", rar->file.calculated_crc32, rar->file.stored_crc32); } #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: CRC32"); return ARCHIVE_FATAL; #endif } else { DEBUG_CODE { printf("Checksum OK: CRC32 " "(%08x/%08x)\n", rar->file.stored_crc32, rar->file.calculated_crc32); } } } if(rar->file.has_blake2 > 0) { /* BLAKE2sp is an optional checksum algorithm that is * added to RARv5 archives when using the `-htb` switch * during creation of archive. * * We now finalize the hash calculation by calling the * `final` function. This will generate the final hash * value we can use to compare it with the BLAKE2sp * checksum that is stored in the archive. * * The return value of this `final` function is not * very helpful, as it guards only against improper use. * This is why we're explicitly ignoring it. */ uint8_t b2_buf[32]; (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: BLAKE2"); return ARCHIVE_FATAL; #endif } } } /* Finalization for this file has been successfully completed. */ return ARCHIVE_OK; } static int verify_global_checksums(struct archive_read* a) { return verify_checksums(a); } static int rar5_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int ret; struct rar5* rar = get_context(a); if(rar->file.dir > 0) { /* Don't process any data if this file entry was declared * as a directory. This is needed, because entries marked as * directory doesn't have any dictionary buffer allocated, so * it's impossible to perform any decompression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't decompress an entry marked as a directory"); return ARCHIVE_FAILED; } if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Unpacker has written too many bytes"); return ARCHIVE_FATAL; } ret = use_data(rar, buff, size, offset); if(ret == ARCHIVE_OK) { return ret; } if(rar->file.eof == 1) { return ARCHIVE_EOF; } ret = do_unpack(a, rar, buff, size, offset); if(ret != ARCHIVE_OK) { return ret; } if(rar->file.bytes_remaining == 0 && rar->cstate.last_write_ptr == rar->file.unpacked_size) { /* If all bytes of current file were processed, run * finalization. * * Finalization will check checksum against proper values. If * some of the checksums will not match, we'll return an error * value in the last `archive_read_data` call to signal an error * to the user. */ rar->file.eof = 1; return verify_global_checksums(a); } return ARCHIVE_OK; } static int rar5_read_data_skip(struct archive_read *a) { struct rar5* rar = get_context(a); if(rar->main.solid) { /* In solid archives, instead of skipping the data, we need to * extract it, and dispose the result. The side effect of this * operation will be setting up the initial window buffer state * needed to be able to extract the selected file. */ int ret; /* Make sure to process all blocks in the compressed stream. */ while(rar->file.bytes_remaining > 0) { /* Setting the "skip mode" will allow us to skip * checksum checks during data skipping. Checking the * checksum of skipped data isn't really necessary and * it's only slowing things down. * * This is incremented instead of setting to 1 because * this data skipping function can be called * recursively. */ rar->skip_mode++; /* We're disposing 1 block of data, so we use triple * NULLs in arguments. */ ret = rar5_read_data(a, NULL, NULL, NULL); /* Turn off "skip mode". */ rar->skip_mode--; - if(ret < 0) { + if(ret < 0 || ret == ARCHIVE_EOF) { /* Propagate any potential error conditions * to the caller. */ return ret; } } } else { /* In standard archives, we can just jump over the compressed * stream. Each file in non-solid archives starts from an empty * window buffer. */ if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { return ARCHIVE_FATAL; } rar->file.bytes_remaining = 0; } return ARCHIVE_OK; } static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, int whence) { (void) a; (void) offset; (void) whence; /* We're a streaming unpacker, and we don't support seeking. */ return ARCHIVE_FATAL; } static int rar5_cleanup(struct archive_read *a) { struct rar5* rar = get_context(a); free(rar->cstate.window_buf); free(rar->cstate.filtered_buf); free(rar->vol.push_buf); free_filters(rar); cdeque_free(&rar->cstate.filters); free(rar); a->format->data = NULL; return ARCHIVE_OK; } static int rar5_capabilities(struct archive_read * a) { (void) a; return 0; } static int rar5_has_encrypted_entries(struct archive_read *_a) { (void) _a; /* Unsupported for now. */ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; } static int rar5_init(struct rar5* rar) { ssize_t i; memset(rar, 0, sizeof(struct rar5)); /* Decrypt the magic signature pattern. Check the comment near the * `rar5_signature` symbol to read the rationale behind this. */ if(rar5_signature[0] == 243) { for(i = 0; i < rar5_signature_size; i++) { rar5_signature[i] ^= 0xA1; } } if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) return ARCHIVE_FATAL; return ARCHIVE_OK; } int archive_read_support_format_rar5(struct archive *_a) { struct archive_read* ar; int ret; struct rar5* rar; if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) return ret; rar = malloc(sizeof(*rar)); if(rar == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 data"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != rar5_init(rar)) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 filter buffer"); return ARCHIVE_FATAL; } ret = __archive_read_register_format(ar, rar, "rar5", rar5_bid, rar5_options, rar5_read_header, rar5_read_data, rar5_read_data_skip, rar5_seek_data, rar5_cleanup, rar5_capabilities, rar5_has_encrypted_entries); if(ret != ARCHIVE_OK) { (void) rar5_cleanup(ar); } return ret; } Index: stable/10/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c (revision 349524) +++ stable/10/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c (revision 349525) @@ -1,314 +1,314 @@ /*- * 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 "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_private.h" #include "archive_string.h" #include "archive_write_private.h" #define LBYTES 57 struct private_b64encode { int mode; struct archive_string name; struct archive_string encoded_buff; size_t bs; size_t hold_len; unsigned char hold[LBYTES]; }; static int archive_filter_b64encode_options(struct archive_write_filter *, const char *, const char *); static int archive_filter_b64encode_open(struct archive_write_filter *); static int archive_filter_b64encode_write(struct archive_write_filter *, const void *, size_t); static int archive_filter_b64encode_close(struct archive_write_filter *); static int archive_filter_b64encode_free(struct archive_write_filter *); -static void b64_encode(struct archive_string *, const unsigned char *, size_t); +static void la_b64_encode(struct archive_string *, const unsigned char *, size_t); static int64_t atol8(const char *, size_t); static const char base64[] = { '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', '+', '/' }; /* * Add a compress filter to this write handle. */ int archive_write_add_filter_b64encode(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f = __archive_write_allocate_filter(_a); struct private_b64encode *state; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); state = (struct private_b64encode *)calloc(1, sizeof(*state)); if (state == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for b64encode filter"); return (ARCHIVE_FATAL); } archive_strcpy(&state->name, "-"); state->mode = 0644; f->data = state; f->name = "b64encode"; f->code = ARCHIVE_FILTER_UU; f->open = archive_filter_b64encode_open; f->options = archive_filter_b64encode_options; f->write = archive_filter_b64encode_write; f->close = archive_filter_b64encode_close; f->free = archive_filter_b64encode_free; return (ARCHIVE_OK); } /* * Set write options. */ static int archive_filter_b64encode_options(struct archive_write_filter *f, const char *key, const char *value) { struct private_b64encode *state = (struct private_b64encode *)f->data; if (strcmp(key, "mode") == 0) { if (value == NULL) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "mode option requires octal digits"); return (ARCHIVE_FAILED); } state->mode = (int)atol8(value, strlen(value)) & 0777; return (ARCHIVE_OK); } else if (strcmp(key, "name") == 0) { if (value == NULL) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "name option requires a string"); return (ARCHIVE_FAILED); } archive_strcpy(&state->name, value); 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); } /* * Setup callback. */ static int archive_filter_b64encode_open(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; size_t bs = 65536, bpb; int ret; ret = __archive_write_open_filter(f->next_filter); if (ret != ARCHIVE_OK) return (ret); 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->bs = bs; if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for b64encode buffer"); return (ARCHIVE_FATAL); } archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", state->mode, state->name.s); f->data = state; return (0); } static void -b64_encode(struct archive_string *as, const unsigned char *p, size_t len) +la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len) { int c; for (; len >= 3; p += 3, len -= 3) { c = p[0] >> 2; archive_strappend_char(as, base64[c]); c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); archive_strappend_char(as, base64[c]); c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); archive_strappend_char(as, base64[c]); c = p[2] & 0x3f; archive_strappend_char(as, base64[c]); } if (len > 0) { c = p[0] >> 2; archive_strappend_char(as, base64[c]); c = (p[0] & 0x03) << 4; if (len == 1) { archive_strappend_char(as, base64[c]); archive_strappend_char(as, '='); archive_strappend_char(as, '='); } else { c |= (p[1] & 0xf0) >> 4; archive_strappend_char(as, base64[c]); c = (p[1] & 0x0f) << 2; archive_strappend_char(as, base64[c]); archive_strappend_char(as, '='); } } archive_strappend_char(as, '\n'); } /* * Write data to the encoded stream. */ static int archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_b64encode *state = (struct private_b64encode *)f->data; const unsigned char *p = buff; int ret = ARCHIVE_OK; if (length == 0) return (ret); if (state->hold_len) { while (state->hold_len < LBYTES && length > 0) { state->hold[state->hold_len++] = *p++; length--; } if (state->hold_len < LBYTES) return (ret); - b64_encode(&state->encoded_buff, state->hold, LBYTES); + la_b64_encode(&state->encoded_buff, state->hold, LBYTES); state->hold_len = 0; } for (; length >= LBYTES; length -= LBYTES, p += LBYTES) - b64_encode(&state->encoded_buff, p, LBYTES); + la_b64_encode(&state->encoded_buff, p, LBYTES); /* Save remaining bytes. */ if (length > 0) { memcpy(state->hold, p, length); state->hold_len = length; } while (archive_strlen(&state->encoded_buff) >= state->bs) { ret = __archive_write_filter(f->next_filter, state->encoded_buff.s, state->bs); memmove(state->encoded_buff.s, state->encoded_buff.s + state->bs, state->encoded_buff.length - state->bs); state->encoded_buff.length -= state->bs; } return (ret); } /* * Finish the compression... */ static int archive_filter_b64encode_close(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; int ret, ret2; /* Flush remaining bytes. */ if (state->hold_len != 0) - b64_encode(&state->encoded_buff, state->hold, state->hold_len); + la_b64_encode(&state->encoded_buff, state->hold, state->hold_len); archive_string_sprintf(&state->encoded_buff, "====\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); ret = __archive_write_filter(f->next_filter, state->encoded_buff.s, archive_strlen(&state->encoded_buff)); ret2 = __archive_write_close_filter(f->next_filter); if (ret > ret2) ret = ret2; return (ret); } static int archive_filter_b64encode_free(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; archive_string_free(&state->name); archive_string_free(&state->encoded_buff); free(state); return (ARCHIVE_OK); } static int64_t atol8(const char *p, size_t char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else break; p++; l <<= 3; l |= digit; } return (l); } Index: stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 349524) +++ stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 349525) @@ -1,4318 +1,4417 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #if HAVE_SYS_XATTR_H #include #elif HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_UTIME_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PWD_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_UTIME_H #include #endif #ifdef F_GETTIMES /* Tru64 specific */ #include #endif /* * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared. * * It assumes that the input is an integer type of no more than 64 bits. * If the number is less than zero, t must be a signed type, so it fits in * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t * without loss. But it could be a large unsigned value, so we have to clip it * to INT64_MAX.* */ #define to_int64_time(t) \ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t)) #if __APPLE__ #include #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H #include #define HAVE_QUARANTINE 1 #endif #endif #ifdef HAVE_ZLIB_H #include #endif /* TODO: Support Mac OS 'quarantine' feature. This is really just a * standard tag to mark files that have been downloaded as "tainted". * On Mac OS, we should mark the extracted files as tainted if the * archive being read was tainted. Windows has a similar feature; we * should investigate ways to support this generically. */ #include "archive.h" #include "archive_acl_private.h" #include "archive_string.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* Ignore non-int O_NOFOLLOW constant. */ /* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */ #if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX) #undef O_NOFOLLOW #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif + struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; int64_t ctime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long ctime_nanos; unsigned long fflags_set; size_t mac_metadata_size; void *mac_metadata; int fixup; /* bitmask of what needs fixing */ char *name; }; /* * We use a bitmask to track which operations remain to be done for * this file. In particular, this helps us avoid unnecessary * operations when it's possible to take care of one step as a * side-effect of another. For example, mkdir() can specify the mode * for the newly-created object but symlink() cannot. This means we * can skip chmod() if mkdir() succeeded, but we must explicitly * chmod() if we're trying to create a directory that already exists * (mkdir() failed) or if we're restoring a symlink. Similarly, we * need to verify UID/GID before trying to restore SUID/SGID bits; * that verification can occur explicitly through a stat() call or * implicitly because of a successful chown() call. */ #define TODO_MODE_FORCE 0x40000000 #define TODO_MODE_BASE 0x20000000 #define TODO_SUID 0x10000000 #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 #define TODO_APPLEDOUBLE 0x01000000 #define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) #define TODO_TIMES ARCHIVE_EXTRACT_TIME #define TODO_OWNER ARCHIVE_EXTRACT_OWNER #define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS #define TODO_ACLS ARCHIVE_EXTRACT_ACL #define TODO_XATTR ARCHIVE_EXTRACT_XATTR #define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA #define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; int64_t user_uid; int skip_file_set; int64_t skip_file_dev; int64_t skip_file_ino; time_t start_time; int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid); void (*cleanup_uid)(void *private); void *lookup_uid_data; /* * Full path of last file to satisfy symlink checks. */ struct archive_string path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ struct stat st; struct stat *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ char *name; /* Name of entry, possibly edited. */ struct archive_string _name_data; /* backing store for 'name' */ /* 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 void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, int); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname_fsobj(char *, int *, struct archive_string *, int); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); static ssize_t hfs_write_data_block(struct archive_write_disk *, const char *, size_t); static int fixup_appledouble(struct archive_write_disk *, const char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); static int set_mac_metadata(struct archive_write_disk *, const char *, const void *, size_t); static int set_xattrs(struct archive_write_disk *); static int clear_nochange_fflags(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_fflags_platform(struct archive_write_disk *, int fd, const char *name, mode_t mode, unsigned long fflags_set, unsigned long fflags_clear); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_time(int, int, const char *, time_t, long, time_t, long); static int set_times(struct archive_write_disk *, int, int, const char *, time_t, long, time_t, long, time_t, long, time_t, long); static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); static struct archive_vtable *archive_write_disk_vtable(void); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); static int _archive_write_disk_header(struct archive *, struct archive_entry *); static int64_t _archive_write_disk_filter_bytes(struct archive *, int); static int _archive_write_disk_finish_entry(struct archive *); static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); static int +la_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(O_EXEC) + | O_EXEC +#else + | O_RDONLY +#endif + ; + +#if !defined(HAVE_OPENAT) + if (fd != AT_FDCWD) { + errno = ENOTSUP; + return (-1); + } else + return (open(fd, path, flags)); +#else + return (openat(fd, path, flags)); +#endif +} + +static int lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ return (ARCHIVE_OK); } #ifdef HAVE_FSTAT if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } #endif /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ if (lstat(a->name, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static struct archive_vtable * archive_write_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_disk_close; av.archive_filter_bytes = _archive_write_disk_filter_bytes; av.archive_free = _archive_write_disk_free; av.archive_write_header = _archive_write_disk_header; av.archive_write_finish_entry = _archive_write_disk_finish_entry; av.archive_write_data = _archive_write_disk_data; av.archive_write_data_block = _archive_write_disk_data_block; inited = 1; } return (&av); } static int64_t _archive_write_disk_filter_bytes(struct archive *_a, int n) { struct archive_write_disk *a = (struct archive_write_disk *)_a; (void)n; /* UNUSED */ if (n == -1 || n == 0) return (a->total_bytes_written); return (-1); } int archive_write_disk_set_options(struct archive *_a, int flags) { struct archive_write_disk *a = (struct archive_write_disk *)_a; a->flags = flags; return (ARCHIVE_OK); } /* * Extract this entry to disk. * * TODO: Validate hardlinks. According to the standards, we're * supposed to check each extracted hardlink and squawk if it refers * to a file that we didn't restore. I'm not entirely convinced this * is a good idea, but more importantly: Is there any way to validate * hardlinks without keeping a complete list of filenames from the * entire archive?? Ugh. * */ static int _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; 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); /* * Query the umask so we get predictable mode settings. * This gets done on every call to _write_header in case the * user edits their umask during the extraction for some * reason. */ umask(a->user_umask = umask(0)); /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; if (a->flags & ARCHIVE_EXTRACT_PERM) { a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */ /* * SGID requires an extra "check" step because we * cannot easily predict the GID that the system will * assign. (Different systems assign GIDs to files * based on a variety of criteria, including process * credentials and the gid of the enclosing * directory.) We can only restore the SGID bit if * the file has the right GID, and we only know the * GID if we either set it (see set_ownership) or if * we've actually called stat() on the file after it * was restored. Since there are several places at * which we might verify the GID, we need a TODO bit * to keep track. */ if (a->mode & S_ISGID) a->todo |= TODO_SGID | TODO_SGID_CHECK; /* * Verifying the SUID is simpler, but can still be * done in multiple ways, hence the separate "check" bit. */ if (a->mode & S_ISUID) a->todo |= TODO_SUID | TODO_SUID_CHECK; } else { /* * User didn't request full permissions, so don't * restore SUID, SGID bits and obey umask. */ a->mode &= ~S_ISUID; a->mode &= ~S_ISGID; a->mode &= ~S_ISVTX; a->mode &= ~a->user_umask; } if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) { #if ARCHIVE_ACL_DARWIN /* * On MacOS, platform ACLs get stored in mac_metadata, too. * If we intend to extract mac_metadata and it is present * we skip extracting libarchive NFSv4 ACLs. */ size_t metadata_size; if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 || archive_entry_mac_metadata(a->entry, &metadata_size) == NULL || metadata_size == 0) #endif #if ARCHIVE_ACL_LIBRICHACL /* * RichACLs are stored in an extended attribute. * If we intend to extract extended attributes and have this * attribute we skip extracting libarchive NFSv4 ACLs. */ short extract_acls = 1; if (a->flags & ARCHIVE_EXTRACT_XATTR && ( archive_entry_acl_types(a->entry) & ARCHIVE_ENTRY_ACL_TYPE_NFS4)) { const char *attr_name; const void *attr_value; size_t attr_size; int i = archive_entry_xattr_reset(a->entry); while (i--) { archive_entry_xattr_next(a->entry, &attr_name, &attr_value, &attr_size); if (attr_name != NULL && attr_value != NULL && attr_size > 0 && strcmp(attr_name, "trusted.richacl") == 0) { extract_acls = 0; break; } } } if (extract_acls) #endif #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL { #endif if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_ACLS; else a->todo |= TODO_ACLS; #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL } #endif } if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) { if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_MAC_METADATA; else a->todo |= TODO_MAC_METADATA; } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) { unsigned long set, clear; archive_entry_fflags(a->entry, &set, &clear); if ((set & ~clear) & UF_COMPRESSED) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } } if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 && (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } { const char *p; /* Check if the current file name is a type of the * resource fork file. */ p = strrchr(a->name, '/'); if (p == NULL) p = a->name; else p++; if (p[0] == '.' && p[1] == '_') { /* Do not compress "._XXX" files. */ a->todo &= ~TODO_HFS_COMPRESSION; if (a->filesize > 0) a->todo |= TODO_APPLEDOUBLE; } } #endif if (a->flags & ARCHIVE_EXTRACT_XATTR) { #if ARCHIVE_XATTR_DARWIN /* * On MacOS, extended attributes get stored in mac_metadata, * too. If we intend to extract mac_metadata and it is present * we skip extracting extended attributes. */ size_t metadata_size; if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 || archive_entry_mac_metadata(a->entry, &metadata_size) == NULL || metadata_size == 0) #endif a->todo |= TODO_XATTR; } if (a->flags & ARCHIVE_EXTRACT_FFLAGS) a->todo |= TODO_FFLAGS; if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { ret = check_symlinks(a); if (ret != ARCHIVE_OK) return (ret); } #if defined(HAVE_FCHDIR) && defined(PATH_MAX) /* If path exceeds PATH_MAX, shorten the path. */ edit_deep_directories(a); #endif ret = restore_entry(a); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) /* * Check if the filesystem the file is restoring on supports * HFS+ Compression. If not, cancel HFS+ Compression. */ if (a->todo | TODO_HFS_COMPRESSION) { /* * NOTE: UF_COMPRESSED is ignored even if the filesystem * supports HFS+ Compression because the file should * have at least an extended attribute "com.apple.decmpfs" * before the flag is set to indicate that the file have * been compressed. If the filesystem does not support * HFS+ Compression the system call will fail. */ if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0) a->todo &= ~TODO_HFS_COMPRESSION; } #endif /* * TODO: There are rumours that some extended attributes must * be restored before file data is written. If this is true, * then we either need to write all extended attributes both * before and after restoring the data, or find some rule for * determining which must go first and which last. Due to the * many ways people are using xattrs, this may prove to be an * intractable problem. */ #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { archive_set_error(&a->archive, errno, "chdir() failure"); ret = ARCHIVE_FATAL; } close(a->restore_pwd); a->restore_pwd = -1; } #endif /* * Fixup uses the unedited pathname from archive_entry_pathname(), * because it is relative to the base dir and the edited path * might be relative to some intermediate dir as a result of the * deep restore logic. */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } if ((a->deferred & TODO_TIMES) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mode = a->mode; fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { fe->atime = archive_entry_atime(entry); fe->atime_nanos = archive_entry_atime_nsec(entry); } else { /* If atime is unset, use start time. */ fe->atime = a->start_time; fe->atime_nanos = 0; } if (archive_entry_mtime_is_set(entry)) { fe->mtime = archive_entry_mtime(entry); fe->mtime_nanos = archive_entry_mtime_nsec(entry); } else { /* If mtime is unset, use start time. */ fe->mtime = a->start_time; fe->mtime_nanos = 0; } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); fe->birthtime_nanos = archive_entry_birthtime_nsec( entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; fe->birthtime_nanos = fe->mtime_nanos; } } if (a->deferred & TODO_ACLS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_ACLS; archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } if (a->deferred & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { memcpy(fe->mac_metadata, metadata, metadata_size); fe->mac_metadata_size = metadata_size; fe->fixup |= TODO_MAC_METADATA; } } } if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } /* We've created the object and are ready to pour data into it. */ if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. * In particular, dirs, links, etc, don't get written to. */ if (a->fd < 0) { archive_entry_set_size(entry, 0); a->filesize = 0; } return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file"); a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } static ssize_t write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t block_size = 0, bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } if (a->flags & ARCHIVE_EXTRACT_SPARSE) { #if HAVE_STRUCT_STAT_ST_BLKSIZE int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; #else /* XXX TODO XXX Is there a more appropriate choice here ? */ /* This needn't match the filesystem allocation size. */ block_size = 16*1024; #endif } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { if (block_size == 0) { bytes_to_write = size; } else { /* We're sparsifying the file. */ const char *p, *end; int64_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { if (*p != '\0') break; } a->offset += p - buff; size -= p - buff; buff = p; if (size == 0) break; /* Calculate next block boundary after offset. */ block_end = (a->offset / block_size + 1) * block_size; /* If the adjusted write would cross block boundary, * truncate it to the block boundary. */ bytes_to_write = size; if (a->offset + bytes_to_write > block_end) bytes_to_write = block_end - a->offset; } /* Seek if necessary to the specified offset. */ if (a->offset != a->fd_offset) { if (lseek(a->fd, a->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } a->fd_offset = a->offset; } bytes_written = write(a->fd, buff, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) /* * Set UF_COMPRESSED file flag. * This have to be called after hfs_write_decmpfs() because if the * file does not have "com.apple.decmpfs" xattr the flag is ignored. */ static int hfs_set_compressed_fflag(struct archive_write_disk *a) { int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags |= UF_COMPRESSED; if (fchflags(a->fd, a->st.st_flags) != 0) { archive_set_error(&a->archive, errno, "Failed to set UF_COMPRESSED file flag"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression decmpfs * * +------------------------------+ +0 * | Magic(LE 4 bytes) | * +------------------------------+ * | Type(LE 4 bytes) | * +------------------------------+ * | Uncompressed size(LE 8 bytes)| * +------------------------------+ +16 * | | * | Compressed data | * | (Placed only if Type == 3) | * | | * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE * * Type is 3: decmpfs has compressed data. * Type is 4: Resource Fork has compressed data. */ /* * Write "com.apple.decmpfs" */ static int hfs_write_decmpfs(struct archive_write_disk *a) { int r; uint32_t compression_type; r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p, a->decmpfs_attr_size, 0, 0); if (r < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr:%s", DECMPFS_XATTR_NAME); compression_type = archive_le32dec( &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]); if (compression_type == CMP_RESOURCE_FORK) fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, XATTR_SHOWCOMPRESSION); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression Resource Fork * * +-----------------------------+ * | Header(260 bytes) | * +-----------------------------+ * | Block count(LE 4 bytes) | * +-----------------------------+ --+ * +-- | Offset (LE 4 bytes) | | * | | [distance from Block count] | | Block 0 * | +-----------------------------+ | * | | Compressed size(LE 4 bytes) | | * | +-----------------------------+ --+ * | | | * | | .................. | * | | | * | +-----------------------------+ --+ * | | Offset (LE 4 bytes) | | * | +-----------------------------+ | Block (Block count -1) * | | Compressed size(LE 4 bytes) | | * +-> +-----------------------------+ --+ * | Compressed data(n bytes) | Block 0 * +-----------------------------+ * | | * | .................. | * | | * +-----------------------------+ * | Compressed data(n bytes) | Block (Block count -1) * +-----------------------------+ * | Footer(50 bytes) | * +-----------------------------+ * */ /* * Write the header of "com.apple.ResourceFork" */ static int hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff, size_t bytes, uint32_t position) { int ret; ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes, position, a->rsrc_xattr_options); if (ret < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr: %s at %u pos %u bytes", XATTR_RESOURCEFORK_NAME, (unsigned)position, (unsigned)bytes); return (ARCHIVE_WARN); } a->rsrc_xattr_options &= ~XATTR_CREATE; return (ARCHIVE_OK); } static int hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed) { int ret; ret = hfs_write_resource_fork(a, a->compressed_buffer, bytes_compressed, a->compressed_rsrc_position); if (ret == ARCHIVE_OK) a->compressed_rsrc_position += bytes_compressed; return (ret); } static int hfs_write_resource_fork_header(struct archive_write_disk *a) { unsigned char *buff; uint32_t rsrc_bytes; uint32_t rsrc_header_bytes; /* * Write resource fork header + block info. */ buff = a->resource_fork; rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE; rsrc_header_bytes = RSRC_H_SIZE + /* Header base size. */ 4 + /* Block count. */ (a->decmpfs_block_count * 8);/* Block info */ archive_be32enc(buff, 0x100); archive_be32enc(buff + 4, rsrc_bytes); archive_be32enc(buff + 8, rsrc_bytes - 256); archive_be32enc(buff + 12, 0x32); memset(buff + 16, 0, 240); archive_be32enc(buff + 256, rsrc_bytes - 260); return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0); } static size_t hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size) { static const char rsrc_footer[RSRC_F_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm', 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (buff_size < sizeof(rsrc_footer)) return (0); memcpy(buff, rsrc_footer, sizeof(rsrc_footer)); return (sizeof(rsrc_footer)); } static int hfs_reset_compressor(struct archive_write_disk *a) { int ret; if (a->stream_valid) ret = deflateReset(&a->stream); else ret = deflateInit(&a->stream, a->decmpfs_compression_level); if (ret != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize compressor"); return (ARCHIVE_FATAL); } else a->stream_valid = 1; return (ARCHIVE_OK); } static int hfs_decompress(struct archive_write_disk *a) { uint32_t *block_info; unsigned int block_count; uint32_t data_pos, data_size; ssize_t r; ssize_t bytes_written, bytes_to_write; unsigned char *b; block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); block_count = archive_le32dec(block_info++); while (block_count--) { data_pos = RSRC_H_SIZE + archive_le32dec(block_info++); data_size = archive_le32dec(block_info++); r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME, a->compressed_buffer, data_size, data_pos, 0); if (r != data_size) { archive_set_error(&a->archive, (r < 0)?errno:ARCHIVE_ERRNO_MISC, "Failed to read resource fork"); return (ARCHIVE_WARN); } if (a->compressed_buffer[0] == 0xff) { bytes_to_write = data_size -1; b = a->compressed_buffer + 1; } else { uLong dest_len = MAX_DECMPFS_BLOCK_SIZE; int zr; zr = uncompress((Bytef *)a->uncompressed_buffer, &dest_len, a->compressed_buffer, data_size); if (zr != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to decompress resource fork"); return (ARCHIVE_WARN); } bytes_to_write = dest_len; b = (unsigned char *)a->uncompressed_buffer; } do { bytes_written = write(a->fd, b, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } bytes_to_write -= bytes_written; b += bytes_written; } while (bytes_to_write > 0); } r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0); if (r == -1) { archive_set_error(&a->archive, errno, "Failed to remove resource fork"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int hfs_drive_compressor(struct archive_write_disk *a, const char *buff, size_t size) { unsigned char *buffer_compressed; size_t bytes_compressed; size_t bytes_used; int ret; ret = hfs_reset_compressor(a); if (ret != ARCHIVE_OK) return (ret); if (a->compressed_buffer == NULL) { size_t block_size; block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE + + compressBound(MAX_DECMPFS_BLOCK_SIZE); a->compressed_buffer = malloc(block_size); if (a->compressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Resource Fork"); return (ARCHIVE_FATAL); } a->compressed_buffer_size = block_size; a->compressed_buffer_remaining = block_size; } buffer_compressed = a->compressed_buffer + a->compressed_buffer_size - a->compressed_buffer_remaining; a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; a->stream.avail_in = size; a->stream.next_out = buffer_compressed; a->stream.avail_out = a->compressed_buffer_remaining; do { ret = deflate(&a->stream, Z_FINISH); switch (ret) { case Z_OK: case Z_STREAM_END: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to compress data"); return (ARCHIVE_FAILED); } } while (ret == Z_OK); bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out; /* * If the compressed size is larger than the original size, * throw away compressed data, use uncompressed data instead. */ if (bytes_compressed > size) { buffer_compressed[0] = 0xFF;/* uncompressed marker. */ memcpy(buffer_compressed + 1, buff, size); bytes_compressed = size + 1; } a->compressed_buffer_remaining -= bytes_compressed; /* * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE * and the block count in the file is only one, store compressed * data to decmpfs xattr instead of the resource fork. */ if (a->decmpfs_block_count == 1 && (a->decmpfs_attr_size + bytes_compressed) <= MAX_DECMPFS_XATTR_SIZE) { archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_XATTR); memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE, buffer_compressed, bytes_compressed); a->decmpfs_attr_size += bytes_compressed; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); return (ret); } /* Update block info. */ archive_le32enc(a->decmpfs_block_info++, a->compressed_rsrc_position_v - RSRC_H_SIZE); archive_le32enc(a->decmpfs_block_info++, bytes_compressed); a->compressed_rsrc_position_v += bytes_compressed; /* * Write the compressed data to the resource fork. */ bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining; while (bytes_used >= COMPRESSED_W_SIZE) { ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE); if (ret != ARCHIVE_OK) return (ret); bytes_used -= COMPRESSED_W_SIZE; if (bytes_used > COMPRESSED_W_SIZE) memmove(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); else memcpy(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); } a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used; /* * If the current block is the last block, write the remaining * compressed data and the resource fork footer. */ if (a->file_remaining_bytes == 0) { size_t rsrc_size; int64_t bk; /* Append the resource footer. */ rsrc_size = hfs_set_resource_fork_footer( a->compressed_buffer + bytes_used, a->compressed_buffer_remaining); ret = hfs_write_compressed_data(a, bytes_used + rsrc_size); a->compressed_buffer_remaining = a->compressed_buffer_size; /* If the compressed size is not enough smaller than * the uncompressed size. cancel HFS+ compression. * TODO: study a behavior of ditto utility and improve * the condition to fall back into no HFS+ compression. */ bk = HFS_BLOCKS(a->compressed_rsrc_position); bk += bk >> 7; if (bk > HFS_BLOCKS(a->filesize)) return hfs_decompress(a); /* * Write the resourcefork header. */ if (ret == ARCHIVE_OK) ret = hfs_write_resource_fork_header(a); /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ if (ret == ARCHIVE_OK) ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); } return (ret); } static ssize_t hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, size_t size) { const char *buffer_to_write; size_t bytes_to_write; int ret; if (a->decmpfs_block_count == (unsigned)-1) { void *new_block; size_t new_size; unsigned int block_count; if (a->decmpfs_header_p == NULL) { new_block = malloc(MAX_DECMPFS_XATTR_SIZE + sizeof(uint32_t)); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->decmpfs_header_p = new_block; } a->decmpfs_attr_size = DECMPFS_HEADER_SIZE; archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC], DECMPFS_MAGIC); archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_RESOURCE_FORK); archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE], a->filesize); /* Calculate a block count of the file. */ block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) / MAX_DECMPFS_BLOCK_SIZE; /* * Allocate buffer for resource fork. * Set up related pointers; */ new_size = RSRC_H_SIZE + /* header */ 4 + /* Block count */ (block_count * sizeof(uint32_t) * 2) + RSRC_F_SIZE; /* footer */ if (new_size > a->resource_fork_allocated_size) { new_block = realloc(a->resource_fork, new_size); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for ResourceFork"); return (ARCHIVE_FATAL); } a->resource_fork_allocated_size = new_size; a->resource_fork = new_block; } /* Allocate uncompressed buffer */ if (a->uncompressed_buffer == NULL) { new_block = malloc(MAX_DECMPFS_BLOCK_SIZE); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->uncompressed_buffer = new_block; } a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; a->file_remaining_bytes = a->filesize; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Set up a resource fork. */ a->rsrc_xattr_options = XATTR_CREATE; /* Get the position where we are going to set a bunch * of block info. */ a->decmpfs_block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); /* Set the block count to the resource fork. */ archive_le32enc(a->decmpfs_block_info++, block_count); /* Get the position where we are going to set compressed * data. */ a->compressed_rsrc_position = RSRC_H_SIZE + 4 + (block_count * 8); a->compressed_rsrc_position_v = a->compressed_rsrc_position; a->decmpfs_block_count = block_count; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); /* Do not overrun a block size. */ if (size > a->block_remaining_bytes) bytes_to_write = a->block_remaining_bytes; else bytes_to_write = size; /* Do not overrun the file size. */ if (bytes_to_write > a->file_remaining_bytes) bytes_to_write = a->file_remaining_bytes; /* For efficiency, if a copy length is full of the uncompressed * buffer size, do not copy writing data to it. */ if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE) buffer_to_write = buff; else { memcpy(a->uncompressed_buffer + MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes, buff, bytes_to_write); buffer_to_write = a->uncompressed_buffer; } a->block_remaining_bytes -= bytes_to_write; a->file_remaining_bytes -= bytes_to_write; if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) { ret = hfs_drive_compressor(a, buffer_to_write, MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes); if (ret < 0) return (ret); a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); return (bytes_to_write); } static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { bytes_to_write = size; /* Seek if necessary to the specified offset. */ if (a->offset < a->fd_offset) { /* Can't support backward move. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek failed"); return (ARCHIVE_FATAL); } else if (a->offset > a->fd_offset) { int64_t skip = a->offset - a->fd_offset; char nullblock[1024]; memset(nullblock, 0, sizeof(nullblock)); while (skip > 0) { if (skip > (int64_t)sizeof(nullblock)) bytes_written = hfs_write_decmpfs_block( a, nullblock, sizeof(nullblock)); else bytes_written = hfs_write_decmpfs_block( a, nullblock, skip); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } skip -= bytes_written; } a->fd_offset = a->offset; } bytes_written = hfs_write_decmpfs_block(a, buff, bytes_to_write); if (bytes_written < 0) return (bytes_written); buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #else static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { return (write_data_block(a, buff, size)); } #endif static ssize_t _archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, int64_t offset) { struct archive_write_disk *a = (struct archive_write_disk *)_a; ssize_t r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data_block"); a->offset = offset; if (a->todo & TODO_HFS_COMPRESSION) r = hfs_write_data_block(a, buff, size); else r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 return (ARCHIVE_OK); #else return (size); #endif } static ssize_t _archive_write_disk_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); if (a->todo & TODO_HFS_COMPRESSION) return (hfs_write_data_block(a, buff, size)); return (write_data_block(a, buff, size)); } static int _archive_write_disk_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_HEADER) return (ARCHIVE_OK); archive_clear_error(&a->archive); /* Pad or truncate file to the right size. */ if (a->fd < 0) { /* There's no file. */ } else if (a->filesize < 0) { /* File size is unknown, so we can't set the size. */ } else if (a->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) } else if (a->todo & TODO_HFS_COMPRESSION) { char null_d[1024]; ssize_t r; if (a->file_remaining_bytes) memset(null_d, 0, sizeof(null_d)); while (a->file_remaining_bytes) { if (a->file_remaining_bytes > sizeof(null_d)) r = hfs_write_data_block( a, null_d, sizeof(null_d)); else r = hfs_write_data_block( a, null_d, a->file_remaining_bytes); if (r < 0) return ((int)r); } #endif } else { #if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } #endif /* * Not all platforms implement the XSI option to * extend files via ftruncate. Stat() the file again * to see what happened. */ a->pst = NULL; if ((ret = lazy_stat(a)) != ARCHIVE_OK) return (ret); /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ if (a->st.st_size < a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); return (ARCHIVE_FATAL); } a->pst = NULL; } } /* Restore metadata. */ /* * This is specific to Mac OS X. * If the current file is an AppleDouble file, it should be * linked with the data fork file and remove it. */ if (a->todo & TODO_APPLEDOUBLE) { int r2 = fixup_appledouble(a, a->name); if (r2 == ARCHIVE_EOF) { /* The current file has been successfully linked * with the data fork file and removed. So there * is nothing to do on the current file. */ goto finish_metadata; } if (r2 < ret) ret = r2; } /* * Look up the "real" UID only if we're going to need it. * TODO: the TODO_SGID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) { a->uid = archive_write_disk_uid(&a->archive, archive_entry_uname(a->entry), archive_entry_uid(a->entry)); } /* Look up the "real" GID only if we're going to need it. */ /* TODO: the TODO_SUID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) { a->gid = archive_write_disk_gid(&a->archive, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * Restore ownership before set_mode tries to restore suid/sgid * bits. If we set the owner, we know what it is and can skip * a stat() call to examine the ownership of the file on disk. */ if (a->todo & TODO_OWNER) { int r2 = set_ownership(a); if (r2 < ret) ret = r2; } /* * 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. */ if (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 there's an entry, we can release it now. */ archive_entry_free(a->entry); a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup"); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); a->lookup_gid = lookup_gid; a->cleanup_gid = cleanup_gid; a->lookup_gid_data = private_data; return (ARCHIVE_OK); } int archive_write_disk_set_user_lookup(struct archive *_a, void *private_data, int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid), void (*cleanup_uid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup"); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } int64_t archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_gid"); if (a->lookup_gid) return (a->lookup_gid)(a->lookup_gid_data, name, id); return (id); } int64_t archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_uid"); if (a->lookup_uid) return (a->lookup_uid)(a->lookup_uid_data, name, id); return (id); } /* * Create a new archive_write_disk object and initialize it with global state. */ struct archive * archive_write_disk_new(void) { struct archive_write_disk *a; a = (struct archive_write_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = archive_write_disk_vtable(); a->start_time = time(NULL); /* Query and restore the umask. */ umask(a->user_umask = umask(0)); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ if (archive_string_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } #ifdef HAVE_ZLIB_H a->decmpfs_compression_level = 5; #endif return (&a->archive); } /* * If pathname is longer than PATH_MAX, chdir to a suitable * intermediate dir and edit the path down to a shorter suffix. Note * that this routine never returns an error; if the chdir() attempt * fails for any reason, we just go ahead with the long pathname. The * object creation is likely to fail, but any error will get handled * at that time. */ #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *a) { int ret; char *tail = a->name; /* If path is short, avoid the open() below. */ if (strlen(tail) < PATH_MAX) return; /* Try to record our starting dir. */ - a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); + a->restore_pwd = la_opendirat(AT_FDCWD, "."); __archive_ensure_cloexec_flag(a->restore_pwd); if (a->restore_pwd < 0) return; /* As long as the path is too long... */ while (strlen(tail) >= PATH_MAX) { /* Locate a dir prefix shorter than PATH_MAX. */ tail += PATH_MAX - 8; while (tail > a->name && *tail != '/') tail--; /* Exit if we find a too-long path component. */ if (tail <= a->name) return; /* Create the intermediate dir and chdir to it. */ *tail = '\0'; /* Terminate dir portion */ ret = create_dir(a, a->name); if (ret == ARCHIVE_OK && chdir(a->name) != 0) ret = ARCHIVE_FAILED; *tail = '/'; /* Restore the / we removed. */ if (ret != ARCHIVE_OK) return; tail++; /* The chdir() succeeded; we've now shortened the path. */ a->name = tail; } return; } #endif /* * The main restore function. */ static int restore_entry(struct archive_write_disk *a) { int ret = ARCHIVE_OK, en; if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { /* * TODO: Fix this. Apparently, there are platforms * that still allow root to hose the entire filesystem * by unlinking a dir. The S_ISDIR() test above * prevents us from using unlink() here if the new * object is a dir, but that doesn't mean the old * object isn't a dir. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); return(ARCHIVE_FAILED); } } /* Try creating it first; if this fails, we'll try to recover. */ en = create_filesystem_object(a); if ((en == ENOTDIR || en == ENOENT) && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) { /* If the parent dir doesn't exist, try creating it. */ create_parent_dir(a, a->name); /* Now try to create the object again. */ en = create_filesystem_object(a); } if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, "Hard-link target '%s' does not exist.", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ if (S_ISDIR(a->mode)) { /* Don't overwrite any settings on existing directories. */ a->todo = 0; } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } /* * Some platforms return EISDIR if you call * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some * return EEXIST. POSIX is ambiguous, requiring EISDIR * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) * on an existing item. */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; /* * The SECURE_SYMLINKS logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) r = la_stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) r = lstat(a->name, &a->st); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); } /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) && !S_ISDIR(a->st.st_mode)) { if (!older(&(a->st), a->entry)) { archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } } /* If it's our archive, we're done. */ if (a->skip_file_set && a->st.st_dev == (dev_t)a->skip_file_dev && a->st.st_ino == (ino_t)a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } if (!S_ISDIR(a->st.st_mode)) { /* A non-dir is in the way, unlink it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (unlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't replace existing directory with non-directory"); return (ARCHIVE_FAILED); } /* Try again. */ en = create_filesystem_object(a); } else { /* * There's a dir in the way of a dir. Don't * waste time with rmdir()/mkdir(), just fix * up the permissions on the existing dir. * Note that we don't change perms on existing * dirs unless _EXTRACT_PERM is specified. */ if ((a->mode != a->st.st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ en = 0; /* Forget the EEXIST. */ } } if (en) { /* Everything failed; give up here. */ if ((&a->archive)->error == NULL) archive_set_error(&a->archive, en, "Can't create '%s'", a->name); return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ return (ret); } /* * Returns 0 if creation succeeds, or else returns errno value from * the failed system call. Note: This function should only ever perform * a single system call. */ static int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const char *linkname; mode_t final_mode, mode; int r; /* these for check_symlinks_fsobj */ char *linkname_copy; /* non-const copy of linkname */ struct stat st; struct archive_string error_string; int error_number; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ linkname = archive_entry_hardlink(a->entry); if (linkname != NULL) { #if !HAVE_LINK return (EPERM); #else archive_string_init(&error_string); linkname_copy = strdup(linkname); if (linkname_copy == NULL) { return (EPERM); } /* * TODO: consider using the cleaned-up path as the link * target? */ r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } free(linkname_copy); archive_string_free(&error_string); r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. * * If the hardlink was successfully created and * the archive doesn't have carry data for it, * consider it to be non-authoritative for meta data. * This is consistent with GNU tar and BSD pax. * If the hardlink does carry data, let the last * archive entry decide ownership. */ if (r == 0 && a->filesize <= 0) { a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { #ifdef HAVE_LSTAT r = lstat(a->name, &st); #else r = la_stat(a->name, &st); #endif if (r != 0) r = errno; else if ((st.st_mode & AE_IFMT) == AE_IFREG) { a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC | O_NOFOLLOW); __archive_ensure_cloexec_flag(a->fd); if (a->fd < 0) r = errno; } } return (r); #endif } linkname = archive_entry_symlink(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK 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; switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: a->fd = open(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); __archive_ensure_cloexec_flag(a->fd); r = (a->fd < 0); break; case AE_IFCHR: #ifdef HAVE_MKNOD /* Note: we use AE_IFCHR for the case label, and * S_IFCHR for the mknod() call. This is correct. */ r = mknod(a->name, mode | S_IFCHR, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a char device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFBLK: #ifdef HAVE_MKNOD r = mknod(a->name, mode | S_IFBLK, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; r = mkdir(a->name, mode); if (r == 0) { /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; /* Never use an immediate chmod(). */ /* We can't avoid the chmod() entirely if EXTRACT_PERM * because of SysV SGID inheritance. */ if ((mode != final_mode) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; } break; case AE_IFIFO: #ifdef HAVE_MKFIFO r = mkfifo(a->name, mode); break; #else /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); #endif /* HAVE_MKFIFO */ } /* All the system calls above set errno on failure. */ if (r) return (errno); /* If we managed to set the final mode, we've avoided a chmod(). */ if (mode == final_mode) a->todo &= ~TODO_MODE; return (0); } /* * Cleanup function for archive_extract. Mostly, this involves processing * the fixup list, which is used to address a number of problems: * * Dir permissions might prevent us from restoring a file in that * dir, so we restore the dir with minimum 0700 permissions first, * then correct the mode at the end. * * Similarly, the act of restoring a file touches the directory * and changes the timestamp on the dir, so we have to touch-up dir * timestamps at the end as well. * * Some file flags can interfere with the restore by, for example, * preventing the creation of hardlinks to those files. * * Mac OS extended metadata includes ACLs, so must be deferred on dirs. * * Note that tar/cpio do not require that archives be in a particular * order; there is no way to know when the last file has been restored * within a directory, so there's no way to optimize the memory usage * here by fixing up the directory any earlier than the * end-of-archive. * * XXX TODO: Directory ACLs should be restored here, for the same * reason we set directory perms here. XXX */ static int _archive_write_disk_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; - int ret; + int fd, ret; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_close"); ret = _archive_write_disk_finish_entry(&a->archive); /* Sort dir list so directories are fixed up in depth-first order. */ p = sort_dir_list(a->fixup_list); while (p != NULL) { + fd = -1; a->pst = NULL; /* Mark stat cache as out-of-date. */ + if (p->fixup & + (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { + fd = open(p->name, + O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); + } if (p->fixup & TODO_TIMES) { - set_times(a, -1, p->mode, p->name, + 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) + if (p->fixup & TODO_MODE_BASE) { +#ifdef HAVE_FCHMOD + if (fd >= 0) + fchmod(fd, p->mode); + else +#endif chmod(p->name, p->mode); + } if (p->fixup & TODO_ACLS) - archive_write_disk_set_acls(&a->archive, -1, p->name, - &p->acl, p->mode); + archive_write_disk_set_acls(&a->archive, fd, + p->name, &p->acl, p->mode); if (p->fixup & TODO_FFLAGS) - set_fflags_platform(a, -1, p->name, + set_fflags_platform(a, fd, p->name, p->mode, p->fflags_set, 0); if (p->fixup & TODO_MAC_METADATA) set_mac_metadata(a, p->name, p->mac_metadata, p->mac_metadata_size); next = p->next; archive_acl_clear(&p->acl); free(p->mac_metadata); free(p->name); + if (fd >= 0) + close(fd); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_disk_free(struct archive *_a) { struct archive_write_disk *a; int ret; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free"); a = (struct archive_write_disk *)_a; ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); free(a->decmpfs_header_p); free(a->resource_fork); free(a->compressed_buffer); free(a->uncompressed_buffer); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) if (a->stream_valid) { switch (deflateEnd(&a->stream)) { case Z_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; break; } } #endif free(a); return (ret); } /* * Simple O(n log n) merge sort to order the fixup list. In * particular, we want to restore dir timestamps depth-first. */ static struct fixup_entry * sort_dir_list(struct fixup_entry *p) { struct fixup_entry *a, *b, *t; if (p == NULL) return (NULL); /* A one-item list is already sorted. */ if (p->next == NULL) return (p); /* Step 1: split the list. */ t = p; a = p->next->next; while (a != NULL) { /* Step a twice, t once. */ a = a->next; if (a != NULL) a = a->next; t = t->next; } /* Now, t is at the mid-point, so break the list here. */ b = t->next; t->next = NULL; a = p; /* Step 2: Recursively sort the two sub-lists. */ a = sort_dir_list(a); b = sort_dir_list(b); /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ if (strcmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { t = p = b; b = b->next; } /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { if (strcmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { t->next = b; b = b->next; } t = t->next; } /* Only one list is non-empty, so just splice it on. */ if (a != NULL) t->next = a; if (b != NULL) t->next = b; return (p); } /* * Returns a new, initialized fixup entry. * * TODO: Reduce the memory requirements for this list by using a tree * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive_write_disk *a, const char *pathname) { struct fixup_entry *fe; fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry)); if (fe == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a fixup"); return (NULL); } fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; fe->name = strdup(pathname); return (fe); } /* * Returns a fixup structure for the current entry. */ static struct fixup_entry * current_fixup(struct archive_write_disk *a, const char *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); return (a->current_fixup); } /* Error helper for new *_fsobj functions */ static void fsobj_error(int *a_eno, struct archive_string *a_estr, int err, const char *errstr, const char *path) { if (a_eno) *a_eno = err; if (a_estr) archive_string_sprintf(a_estr, "%s%s", errstr, path); } /* * TODO: Someday, integrate this with the deep dir support; they both * scan the path and both can be optimized by comparing against other * recent paths. */ -/* TODO: Extend this to support symlinks on Windows Vista and later. */ - /* * Checks the given path to see if any elements along it are symlinks. Returns * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. */ static int check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { -#if !defined(HAVE_LSTAT) +#if !defined(HAVE_LSTAT) && \ + !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ (void)error_number; /* UNUSED */ (void)error_string; /* UNUSED */ (void)flags; /* UNUSED */ return (ARCHIVE_OK); #else int res = ARCHIVE_OK; char *tail; char *head; int last; char c; int r; struct stat st; - int restore_pwd; + 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 */ - restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); - __archive_ensure_cloexec_flag(restore_pwd); - if (restore_pwd < 0) { + chdir_fd = la_opendirat(AT_FDCWD, "."); + __archive_ensure_cloexec_flag(chdir_fd); + if (chdir_fd < 0) { fsobj_error(a_eno, a_estr, errno, "Could not open ", path); return (ARCHIVE_FATAL); } head = path; tail = path; last = 0; /* TODO: reintroduce a safe cache here? */ /* Skip the root directory if the path is absolute. */ if(tail == path && tail[0] == '/') ++tail; /* Keep going until we've checked the entire name. * head, tail, path all alias the same string, which is * temporarily zeroed at tail, so be careful restoring the * stashed (c=tail[0]) for error messages. * Exiting the loop with break is okay; continue is not. */ while (!last) { /* * Skip the separator we just consumed, plus any adjacent ones */ while (*tail == '/') ++tail; /* Skip the next path element. */ while (*tail != '\0' && *tail != '/') ++tail; /* is this the last path component? */ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0'); /* temporarily truncate the string here */ c = tail[0]; tail[0] = '\0'; /* Check that we haven't hit a symlink. */ +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW); +#else r = lstat(head, &st); +#endif if (r != 0) { tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) { break; } else { /* * Treat any other error as fatal - best to be * paranoid here. * Note: This effectively disables deep * directory support when security checks are * enabled. Otherwise, very long pathnames that * trigger an error here could evade the * sandbox. * TODO: We could do better, but it would * probably require merging the symlink checks * with the deep-directory editing. */ fsobj_error(a_eno, a_estr, errno, "Could not stat ", path); res = ARCHIVE_FAILED; break; } } else if (S_ISDIR(st.st_mode)) { if (!last) { - if (chdir(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + fd = la_opendirat(chdir_fd, head); + if (fd < 0) + r = -1; + else { + r = 0; + close(chdir_fd); + chdir_fd = fd; + } +#else + r = chdir(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); res = (ARCHIVE_FATAL); break; } /* Our view is now from inside this dir: */ head = tail + 1; } } else if (S_ISLNK(st.st_mode)) { if (last) { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ - if (unlink(head)) { +#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 (unlink(head) != 0) { +#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 (chdir(head) != 0) { +#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; -#ifdef HAVE_FCHDIR +#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 (restore_pwd >= 0) { - r = fchdir(restore_pwd); + if (chdir_fd >= 0) { + r = fchdir(chdir_fd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } - close(restore_pwd); - restore_pwd = -1; + close(chdir_fd); + chdir_fd = -1; if (r != 0) { res = (ARCHIVE_FATAL); } } #endif /* TODO: reintroduce a safe cache here? */ return res; #endif } /* * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED} */ static int check_symlinks(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); a->pst = NULL; /* to be safe */ return rc; } #if defined(__CYGWIN__) /* * 1. Convert a path separator from '\' to '/' . * We shouldn't check multibyte character directly because some * character-set have been using the '\' character for a part of * its multibyte character code. * 2. Replace unusable characters in Windows with underscore('_'). * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static void cleanup_pathname_win(char *path) { wchar_t wc; char *p; size_t alen, l; int mb, complete, utf8; alen = 0; mb = 0; complete = 1; utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0; for (p = path; *p != '\0'; p++) { ++alen; if (*p == '\\') { /* If previous byte is smaller than 128, * this is not second byte of multibyte characters, * so we can replace '\' with '/'. */ if (utf8 || !mb) *p = '/'; else complete = 0;/* uncompleted. */ } else if (*(unsigned char *)p > 127) mb = 1; else mb = 0; /* Rewrite the path name if its next character is unusable. */ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; } if (complete) return; /* * Convert path separator in wide-character. */ p = path; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == (size_t)-1) { while (*p != '\0') { if (*p == '\\') *p = '/'; ++p; } break; } if (l == 1 && wc == L'\\') *p = '/'; p += l; alen -= l; } } #endif /* * Canonicalize the pathname. In particular, this strips duplicate * '/' characters, '.' elements, and trailing '/'. It also raises an * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS * is set) if the path is absolute. */ static int cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { char *dest, *src; char separator = '\0'; dest = src = path; if (*src == '\0') { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Invalid empty ", "pathname"); return (ARCHIVE_FAILED); } #if defined(__CYGWIN__) cleanup_pathname_win(path); #endif /* Skip leading '/'. */ if (*src == '/') { if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path is ", "absolute"); return (ARCHIVE_FAILED); } separator = *src++; } /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '/' */ if (src[0] == '\0') { break; } else if (src[0] == '/') { /* Found '//', ignore second one. */ src++; continue; } else if (src[0] == '.') { if (src[1] == '\0') { /* Ignore trailing '.' */ break; } else if (src[1] == '/') { /* Skip './'. */ src += 2; continue; } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path contains ", "'..'"); return (ARCHIVE_FAILED); } } /* * Note: Under no circumstances do we * remove '..' elements. In * particular, restoring * '/foo/../bar/' should create the * 'foo' dir as a side-effect. */ } } /* Copy current element, including leading '/'. */ if (separator) *dest++ = '/'; while (*src != '\0' && *src != '/') { *dest++ = *src++; } if (*src == '\0') break; /* Skip '/' separator. */ separator = *src++; } /* * We've just copied zero or more path elements, not including the * final '/'. */ if (dest == path) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. */ if (separator) *dest++ = '/'; else *dest++ = '.'; } /* Terminate the result. */ *dest = '\0'; return (ARCHIVE_OK); } static int cleanup_pathname(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); return rc; } /* * Create the parent directory of the specified path, assuming path * is already in mutable storage. */ static int create_parent_dir(struct archive_write_disk *a, char *path) { char *slash; int r; /* Remove tail element to obtain parent name. */ slash = strrchr(path, '/'); if (slash == NULL) return (ARCHIVE_OK); *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } /* * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int create_dir(struct archive_write_disk *a, char *path) { struct stat st; struct fixup_entry *le; char *slash, *base; mode_t mode_final, mode; int r; /* Check for special names and just skip them. */ slash = strrchr(path, '/'); if (slash == NULL) base = path; else base = slash + 1; if (base[0] == '\0' || (base[0] == '.' && base[1] == '\0') || (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } return (ARCHIVE_OK); } /* * Yes, this should be stat() and not lstat(). Using lstat() * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ if (la_stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); return (ARCHIVE_FAILED); } if (unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed", path); return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; if (r != ARCHIVE_OK) return (r); } /* * Mode we want for the final restored directory. Per POSIX, * implicitly-created dirs must be created obeying the umask. * There's no mention whether this is different for privileged * restores (which the rest of this code handles by pretending * umask=0). I've chosen here to always obey the user's umask for * implicit dirs, even if _EXTRACT_PERM was specified. */ mode_final = DEFAULT_DIR_MODE & ~a->user_umask; /* Mode we want on disk during the restore process. */ mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; if (mkdir(path, mode) == 0) { if (mode != mode_final) { le = new_fixup(a, path); if (le == NULL) return (ARCHIVE_FATAL); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); } /* * Without the following check, a/b/../b/c/d fails at the * second visit to 'b', so 'd' can't be created. Note that we * don't add it to the fixup list here, as it's already been * added. */ if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); return (ARCHIVE_FAILED); } /* * Note: Although we can skip setting the user id if the desired user * id matches the current user, we cannot skip setting the group, as * many systems set the gid based on the containing directory. So * we have to perform a chown syscall if we want to set the SGID * bit. (The alternative is to stat() and then possibly chown(); it's * more efficient to skip the stat() and just always chown().) Note * that a successful chown() here clears the TODO_SGID_CHECK bit, which * allows set_mode to skip the stat() check for the GID. */ static int set_ownership(struct archive_write_disk *a) { #if !defined(__CYGWIN__) && !defined(__linux__) /* * On Linux, a process may have the CAP_CHOWN capability. * On Windows there is no 'root' user with uid 0. * Elsewhere we can skip calling chown if we are not root and the desired * user id does not match the current user. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%jd", (intmax_t)a->uid); return (ARCHIVE_WARN); } #endif #ifdef HAVE_FCHOWN /* If we have an fd, we can avoid a race. */ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif /* We prefer lchown() but will use chown() if that's all we have. */ /* Of course, if we have neither, this will always fail. */ #ifdef HAVE_LCHOWN if (lchown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #elif HAVE_CHOWN if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif archive_set_error(&a->archive, errno, "Can't set user=%jd/group=%jd for %s", (intmax_t)a->uid, (intmax_t)a->gid, a->name); return (ARCHIVE_WARN); } /* * Note: Returns 0 on success, non-zero on failure. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { /* Select the best implementation for this platform. */ #if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS) /* * utimensat() and futimens() are defined in * POSIX.1-2008. They support ns resolution and setting times * on fds and symlinks. */ struct timespec ts[2]; (void)mode; /* UNUSED */ ts[0].tv_sec = atime; ts[0].tv_nsec = atime_nsec; ts[1].tv_sec = mtime; ts[1].tv_nsec = mtime_nsec; if (fd >= 0) return futimens(fd, ts); return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW); #elif HAVE_UTIMES /* * The utimes()-family functions support µs-resolution and * setting times fds and symlinks. utimes() is documented as * LEGACY by POSIX, futimes() and lutimes() are not described * in POSIX. */ struct timeval times[2]; times[0].tv_sec = atime; times[0].tv_usec = atime_nsec / 1000; times[1].tv_sec = mtime; times[1].tv_usec = mtime_nsec / 1000; #ifdef HAVE_FUTIMES if (fd >= 0) return (futimes(fd, times)); #else (void)fd; /* UNUSED */ #endif #ifdef HAVE_LUTIMES (void)mode; /* UNUSED */ return (lutimes(name, times)); #else if (S_ISLNK(mode)) return (0); return (utimes(name, times)); #endif #elif defined(HAVE_UTIME) /* * utime() is POSIX-standard but only supports 1s resolution and * does not support fds or symlinks. */ struct utimbuf times; (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ times.actime = atime; times.modtime = mtime; if (S_ISLNK(mode)) return (ARCHIVE_OK); return (utime(name, ×)); #else /* * We don't know how to set the time on this platform. */ (void)fd; /* UNUSED */ (void)mode; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ return (ARCHIVE_WARN); #endif } #ifdef F_SETTIMES static int set_time_tru64(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec, time_t ctime, long ctime_nsec) { struct attr_timbuf tstamp; tstamp.atime.tv_sec = atime; tstamp.mtime.tv_sec = mtime; tstamp.ctime.tv_sec = ctime; #if defined (__hpux) && defined (__ia64) tstamp.atime.tv_nsec = atime_nsec; tstamp.mtime.tv_nsec = mtime_nsec; tstamp.ctime.tv_nsec = ctime_nsec; #else tstamp.atime.tv_usec = atime_nsec / 1000; tstamp.mtime.tv_usec = mtime_nsec / 1000; tstamp.ctime.tv_usec = ctime_nsec / 1000; #endif return (fcntl(fd,F_SETTIMES,&tstamp)); } #endif /* F_SETTIMES */ static int set_times(struct archive_write_disk *a, int fd, int mode, const char *name, time_t atime, long atime_nanos, time_t birthtime, long birthtime_nanos, time_t mtime, long mtime_nanos, time_t cctime, long ctime_nanos) { /* Note: set_time doesn't use libarchive return conventions! * It uses syscall conventions. So 0 here instead of ARCHIVE_OK. */ int r1 = 0, r2 = 0; #ifdef F_SETTIMES /* * on Tru64 try own fcntl first which can restore even the * ctime, fall back to default code path below if it fails * or if we are not running as root */ if (a->user_uid == 0 && set_time_tru64(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos, cctime, ctime_nanos) == 0) { return (ARCHIVE_OK); } #else /* Tru64 */ (void)cctime; /* UNUSED */ (void)ctime_nanos; /* UNUSED */ #endif /* Tru64 */ #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME /* * If you have struct stat.st_birthtime, we assume BSD * birthtime semantics, in which {f,l,}utimes() updates * birthtime to earliest mtime. So we set the time twice, * first using the birthtime, then using the mtime. If * birthtime == mtime, this isn't necessary, so we skip it. * If birthtime > mtime, then this won't work, so we skip it. */ if (birthtime < mtime || (birthtime == mtime && birthtime_nanos < mtime_nanos)) r1 = set_time(fd, mode, name, atime, atime_nanos, birthtime, birthtime_nanos); #else (void)birthtime; /* UNUSED */ (void)birthtime_nanos; /* UNUSED */ #endif r2 = set_time(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos); if (r1 != 0 || r2 != 0) { archive_set_error(&a->archive, errno, "Can't restore time"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int set_times_from_entry(struct archive_write_disk *a) { time_t atime, birthtime, mtime, cctime; long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec; /* Suitable defaults. */ atime = birthtime = mtime = cctime = a->start_time; atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0; /* If no time was provided, we're done. */ if (!archive_entry_atime_is_set(a->entry) #if HAVE_STRUCT_STAT_ST_BIRTHTIME && !archive_entry_birthtime_is_set(a->entry) #endif && !archive_entry_mtime_is_set(a->entry)) return (ARCHIVE_OK); if (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } if (archive_entry_birthtime_is_set(a->entry)) { birthtime = archive_entry_birthtime(a->entry); birthtime_nsec = archive_entry_birthtime_nsec(a->entry); } if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (archive_entry_ctime_is_set(a->entry)) { cctime = archive_entry_ctime(a->entry); ctime_nsec = archive_entry_ctime_nsec(a->entry); } return set_times(a, a->fd, a->mode, a->name, atime, atime_nsec, birthtime, birthtime_nsec, mtime, mtime_nsec, cctime, ctime_nsec); } static int set_mode(struct archive_write_disk *a, int mode) { int r = ARCHIVE_OK; + int r2; mode &= 07777; /* Strip off file type bits. */ if (a->todo & TODO_SGID_CHECK) { /* * If we don't know the GID is right, we must stat() * to verify it. We can't just check the GID of this * process, since systems sometimes set GID from * the enclosing dir or based on ACLs. */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { /* * This is only an error if you * requested owner restore. If you * didn't, we'll try to restore * sgid/suid, but won't consider it a * problem if we can't. */ archive_set_error(&a->archive, -1, "Can't restore SGID bit"); r = ARCHIVE_WARN; } } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; } else if (a->todo & TODO_SUID_CHECK) { /* * If we don't know the UID is right, we can just check * the user, since all systems set the file UID from * the process UID. */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } } a->todo &= ~TODO_SUID_CHECK; } if (S_ISLNK(a->mode)) { #ifdef HAVE_LCHMOD /* * If this is a symlink, use lchmod(). If the * platform doesn't support lchmod(), just skip it. A * platform that doesn't provide a way to set * permissions on symlinks probably ignores * permissions on symlinks, so a failure here has no * impact. */ if (lchmod(a->name, mode) != 0) { switch (errno) { case ENOTSUP: case ENOSYS: #if ENOTSUP != EOPNOTSUPP case EOPNOTSUPP: #endif /* * if lchmod is defined but the platform * doesn't support it, silently ignore * error */ break; default: archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } #endif } else if (!S_ISDIR(a->mode)) { /* * If it's not a symlink and not a dir, then use * fchmod() or chmod(), depending on whether we have * an fd. Dirs get their perms set during the * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD - if (a->fd >= 0) { - if (fchmod(a->fd, mode) != 0) { - archive_set_error(&a->archive, errno, - "Can't set permissions to 0%o", (int)mode); - r = ARCHIVE_WARN; - } - } else + if (a->fd >= 0) + r2 = fchmod(a->fd, mode); + else #endif - /* If this platform lacks fchmod(), then - * we'll just use chmod(). */ - if (chmod(a->name, mode) != 0) { - archive_set_error(&a->archive, errno, - "Can't set permissions to 0%o", (int)mode); - r = ARCHIVE_WARN; - } + /* If this platform lacks fchmod(), then + * we'll just use chmod(). */ + r2 = chmod(a->name, mode); + + if (r2 != 0) { + archive_set_error(&a->archive, errno, + "Can't set permissions to 0%o", (int)mode); + r = ARCHIVE_WARN; + } } return (r); } static int set_fflags(struct archive_write_disk *a) { struct fixup_entry *le; unsigned long set, clear; int r; mode_t mode = archive_entry_mode(a->entry); /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, * SF_IMMUTABLE prevents hardlinks from being created, so * should not be set until after any hardlinks are created. To * preserve some semblance of portability, this uses #ifdef * extensively. Ugly, but it works. * * Yes, Virginia, this does create a security race. It's mitigated * somewhat by the practice of creating dirs 0700 until the extract * is done, but it would be nice if we could do more than that. * People restoring critical file systems should be wary of * other programs that might try to muck with files as they're * being restored. */ const int critical_flags = 0 #ifdef SF_IMMUTABLE | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE | UF_IMMUTABLE #endif #ifdef SF_APPEND | SF_APPEND #endif #ifdef UF_APPEND | UF_APPEND #endif #if defined(FS_APPEND_FL) | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) | EXT2_APPEND_FL #endif #if defined(FS_IMMUTABLE_FL) | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) | EXT2_IMMUTABLE_FL #endif #ifdef FS_JOURNAL_DATA_FL | FS_JOURNAL_DATA_FL #endif ; if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); /* * The first test encourages the compiler to eliminate * all of this if it's not necessary. */ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, a->name); if (le == NULL) return (ARCHIVE_FATAL); le->fixup |= TODO_FFLAGS; le->fflags_set = set; /* Store the mode if it's not already there. */ if ((le->fixup & TODO_MODE) == 0) le->mode = mode; } else { r = set_fflags_platform(a, a->fd, a->name, mode, set, clear); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } static int clear_nochange_fflags(struct archive_write_disk *a) { mode_t mode = archive_entry_mode(a->entry); const int nochange_flags = 0 #ifdef SF_IMMUTABLE | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE | UF_IMMUTABLE #endif #ifdef SF_APPEND | SF_APPEND #endif #ifdef UF_APPEND | UF_APPEND #endif #ifdef EXT2_APPEND_FL | EXT2_APPEND_FL #endif #ifdef EXT2_IMMUTABLE_FL | EXT2_IMMUTABLE_FL #endif ; return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); } #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS) /* * BSD reads flags using stat() and sets them with one of {f,l,}chflags() */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; const int sf_mask = 0 #ifdef SF_APPEND | SF_APPEND #endif #ifdef SF_ARCHIVED | SF_ARCHIVED #endif #ifdef SF_IMMUTABLE | SF_IMMUTABLE #endif #ifdef SF_NOUNLINK | SF_NOUNLINK #endif ; (void)mode; /* UNUSED */ if (set == 0 && clear == 0) return (ARCHIVE_OK); /* * XXX Is the stat here really necessary? Or can I just use * the 'set' flags directly? In particular, I'm not sure * about the correct approach if we're overwriting an existing * file that already has flags on it. XXX */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags &= ~clear; a->st.st_flags |= set; /* Only super-user may change SF_* flags */ if (a->user_uid != 0) a->st.st_flags &= ~sf_mask; #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif /* * If we can't use the fd to set the flags, we'll use the * pathname to set flags. We prefer lchflags() but will use * chflags() if we must. */ #ifdef HAVE_LCHFLAGS if (lchflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #elif defined(HAVE_CHFLAGS) if (S_ISLNK(a->st.st_mode)) { archive_set_error(&a->archive, errno, "Can't set file flags on symlink."); return (ARCHIVE_WARN); } if (chflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif archive_set_error(&a->archive, errno, "Failed to set file flags"); return (ARCHIVE_WARN); } #elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* * Linux uses ioctl() to read and write file flags. */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int ret; int myfd = fd; int newflags, oldflags; /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but * there seem to be some Linux systems that lack these * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ const int sf_mask = 0 #if defined(FS_IMMUTABLE_FL) | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) | EXT2_IMMUTABLE_FL #endif #if defined(FS_APPEND_FL) | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) | EXT2_APPEND_FL #endif #if defined(FS_JOURNAL_DATA_FL) | FS_JOURNAL_DATA_FL #endif ; if (set == 0 && clear == 0) return (ARCHIVE_OK); /* Only regular files and dirs can have flags. */ if (!S_ISREG(mode) && !S_ISDIR(mode)) return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ if (myfd < 0) { myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(myfd); } if (myfd < 0) return (ARCHIVE_OK); /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX */ ret = ARCHIVE_OK; /* Read the current file flags. */ if (ioctl(myfd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &oldflags) < 0) goto fail; /* Try setting the flags as given. */ newflags = (oldflags & ~clear) | set; if (ioctl(myfd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &newflags) >= 0) goto cleanup; if (errno != EPERM) goto fail; /* If we couldn't set all the flags, try again with a subset. */ newflags &= ~sf_mask; oldflags &= sf_mask; newflags |= oldflags; if (ioctl(myfd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &newflags) >= 0) goto cleanup; /* We couldn't set the flags, so report the failure. */ fail: archive_set_error(&a->archive, errno, "Failed to set file flags"); ret = ARCHIVE_WARN; cleanup: if (fd < 0) close(myfd); return (ret); } #else /* * Of course, some systems have neither BSD chflags() nor Linux' flags * support through ioctl(). */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)mode; /* UNUSED */ (void)set; /* UNUSED */ (void)clear; /* UNUSED */ return (ARCHIVE_OK); } #endif /* __linux */ #ifndef HAVE_COPYFILE_H /* Default is to simply drop Mac extended metadata. */ static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ (void)metadata; /* UNUSED */ (void)metadata_size; /* UNUSED */ return (ARCHIVE_OK); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ return (ARCHIVE_OK); } #else /* * On Mac OS, we use copyfile() to unpack the metadata and * apply it to the target file. */ #if defined(HAVE_SYS_XATTR_H) static int copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) { ssize_t xattr_size; char *xattr_names = NULL, *xattr_val = NULL; int ret = ARCHIVE_OK, xattr_i; xattr_size = flistxattr(tmpfd, NULL, 0, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_names = malloc(xattr_size); if (xattr_names == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for metadata(xattr)"); ret = ARCHIVE_FATAL; goto exit_xattr; } xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } for (xattr_i = 0; xattr_i < xattr_size; xattr_i += strlen(xattr_names + xattr_i) + 1) { char *xattr_val_saved; ssize_t s; int f; s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_val_saved = xattr_val; xattr_val = realloc(xattr_val, s); if (xattr_val == NULL) { archive_set_error(&a->archive, ENOMEM, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; free(xattr_val_saved); goto exit_xattr; } s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (f == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } } exit_xattr: free(xattr_names); free(xattr_val); return (ret); } #endif static int copy_acls(struct archive_write_disk *a, int tmpfd, int dffd) { #ifndef HAVE_SYS_ACL_H return 0; #else acl_t acl, dfacl = NULL; int acl_r, ret = ARCHIVE_OK; acl = acl_get_fd(tmpfd); if (acl == NULL) { if (errno == ENOENT) /* There are not any ACLs. */ return (ret); archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } dfacl = acl_dup(acl); acl_r = acl_set_fd(dffd, dfacl); if (acl_r == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } exit_acl: if (acl) acl_free(acl); if (dfacl) acl_free(dfacl); return (ret); #endif } static int create_tempdatafork(struct archive_write_disk *a, const char *pathname) { struct archive_string tmpdatafork; int tmpfd; archive_string_init(&tmpdatafork); archive_strcpy(&tmpdatafork, "tar.md.XXXXXX"); tmpfd = mkstemp(tmpdatafork.s); if (tmpfd < 0) { archive_set_error(&a->archive, errno, "Failed to mkstemp"); archive_string_free(&tmpdatafork); return (-1); } if (copyfile(pathname, tmpdatafork.s, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); close(tmpfd); tmpfd = -1; } unlink(tmpdatafork.s); archive_string_free(&tmpdatafork); return (tmpfd); } static int copy_metadata(struct archive_write_disk *a, const char *metadata, const char *datafork, int datafork_compressed) { int ret = ARCHIVE_OK; if (datafork_compressed) { int dffd, tmpfd; tmpfd = create_tempdatafork(a, metadata); if (tmpfd == -1) return (ARCHIVE_WARN); /* * Do not open the data fork compressed by HFS+ compression * with at least a writing mode(O_RDWR or O_WRONLY). it * makes the data fork uncompressed. */ dffd = open(datafork, 0); if (dffd == -1) { archive_set_error(&a->archive, errno, "Failed to open the data fork for metadata"); close(tmpfd); return (ARCHIVE_WARN); } #if defined(HAVE_SYS_XATTR_H) ret = copy_xattrs(a, tmpfd, dffd); if (ret == ARCHIVE_OK) #endif ret = copy_acls(a, tmpfd, dffd); close(tmpfd); close(dffd); } else { if (copyfile(metadata, datafork, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } } return (ret); } static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { struct archive_string tmp; ssize_t written; int fd; int ret = ARCHIVE_OK; /* This would be simpler if copyfile() could just accept the * metadata as a block of memory; then we could sidestep this * silly dance of writing the data to disk just so that * copyfile() can read it back in again. */ archive_string_init(&tmp); archive_strcpy(&tmp, pathname); archive_strcat(&tmp, ".XXXXXX"); fd = mkstemp(tmp.s); if (fd < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); archive_string_free(&tmp); return (ARCHIVE_WARN); } written = write(fd, metadata, metadata_size); close(fd); if ((size_t)written != metadata_size) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } else { int compressed; #if defined(UF_COMPRESSED) if ((a->todo & TODO_HFS_COMPRESSION) != 0 && (ret = lazy_stat(a)) == ARCHIVE_OK) compressed = a->st.st_flags & UF_COMPRESSED; else #endif compressed = 0; ret = copy_metadata(a, tmp.s, pathname, compressed); } unlink(tmp.s); archive_string_free(&tmp); return (ret); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { char buff[8]; struct stat st; const char *p; struct archive_string datafork; int fd = -1, ret = ARCHIVE_OK; archive_string_init(&datafork); /* Check if the current file name is a type of the resource * fork file. */ p = strrchr(pathname, '/'); if (p == NULL) p = pathname; else p++; if (p[0] != '.' || p[1] != '_') goto skip_appledouble; /* * Check if the data fork file exists. * * TODO: Check if this write disk object has handled it. */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); if (lstat(datafork.s, &st) == -1 || (st.st_mode & AE_IFMT) != AE_IFREG) goto skip_appledouble; /* * Check if the file is in the AppleDouble form. */ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd == -1) { archive_set_error(&a->archive, errno, "Failed to open a restoring file"); ret = ARCHIVE_WARN; goto skip_appledouble; } if (read(fd, buff, 8) == -1) { archive_set_error(&a->archive, errno, "Failed to read a restoring file"); close(fd); ret = ARCHIVE_WARN; goto skip_appledouble; } close(fd); /* Check AppleDouble Magic Code. */ if (archive_be32dec(buff) != 0x00051607) goto skip_appledouble; /* Check AppleDouble Version. */ if (archive_be32dec(buff+4) != 0x00020000) goto skip_appledouble; ret = copy_metadata(a, pathname, datafork.s, #if defined(UF_COMPRESSED) st.st_flags & UF_COMPRESSED); #else 0); #endif if (ret == ARCHIVE_OK) { unlink(pathname); ret = ARCHIVE_EOF; } skip_appledouble: archive_string_free(&datafork); return (ret); } #endif #if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX /* * Restore extended attributes - Linux, Darwin and AIX implementations: * AIX' ea interface is syntaxwise identical to the Linux xattr interface. */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL && strncmp(name, "xfsroot.", 8) != 0 && strncmp(name, "system.", 7) != 0) { int e; 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) { if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else archive_set_error(&a->archive, errno, "Failed to set extended attribute"); ret = ARCHIVE_WARN; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended attribute encountered"); ret = ARCHIVE_WARN; } } 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; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); 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; if (strncmp(name, "user.", 5) == 0) { /* "user." attributes go to user namespace */ name += 5; namespace = EXTATTR_NAMESPACE_USER; } else { /* Warn about other extended attributes. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't restore extended attribute ``%s''", name); ret = ARCHIVE_WARN; continue; } errno = 0; if (a->fd >= 0) { e = extattr_set_fd(a->fd, namespace, name, value, size); } else { e = extattr_set_link( archive_entry_pathname(entry), namespace, name, value, size); } if (e != (ssize_t)size) { if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else { archive_set_error(&a->archive, errno, "Failed to set extended attribute"); } ret = ARCHIVE_WARN; } } } return (ret); } #else /* * Restore extended attributes - stub implementation for unsupported systems */ static int set_xattrs(struct archive_write_disk *a) { static int warning_done = 0; /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); } /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); } #endif /* * Test if file on disk is older than entry. */ static int older(struct stat *st, struct archive_entry *entry) { /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry))) return (1); /* Definitely younger. */ if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry))) return (0); /* If this platform supports fractional seconds, try those. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC /* Definitely older. */ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Definitely older. */ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_N /* older. */ if (st->st_mtime_n < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_UMTIME /* older. */ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC /* older. */ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry)) return (1); #else /* This system doesn't have high-res timestamps. */ #endif /* Same age or newer, so not older. */ return (0); } #ifndef ARCHIVE_ACL_SUPPORT int archive_write_disk_set_acls(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl, __LA_MODE_T mode) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)abstract_acl; /* UNUSED */ (void)mode; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !_WIN32 || __CYGWIN__ */ Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_rar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_rar.c (revision 349524) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_rar.c (revision 349525) @@ -1,3781 +1,3810 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Andres Mejia * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include DEFINE_TEST(test_read_format_rar_set_format) { struct archive *a; struct archive_entry *ae; const char reffile[] = "test_read_format_rar.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)); assertA(0 == archive_read_open_filename(a, reffile, 10240)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_basic) { char buff[64]; const char reffile[] = "test_read_format_rar.rar"; const char test_txt[] = "test text document\r\n"; int size = sizeof(test_txt)-1; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(20, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testlink", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41471, archive_entry_mode(ae)); assertEqualString("test.txt", archive_entry_symlink(ae)); assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff))); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(20, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_subblock) { char buff[64]; const char reffile[] = "test_read_format_rar_subblock.rar"; const char test_txt[] = "test text document\r\n"; int size = sizeof(test_txt)-1; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(20, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_noeof) { char buff[64]; const char reffile[] = "test_read_format_rar_noeof.rar"; const char test_txt[] = "test text document\r\n"; int size = sizeof(test_txt)-1; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(20, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_unicode_UTF8) { char buff[30]; const char reffile[] = "test_read_format_rar_unicode.rar"; const char test_txt[] = "kanji"; struct archive_entry *ae; struct archive *a; if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { skipping("en_US.UTF-8 locale not available on this system."); return; } 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f1name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82\xA9"\ "\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x99/\xE6\x96\xB0\xE8\xA6\x8F"\ "\xE3\x83\x86\xE3\x82\xAD\xE3\x82\xB9\xE3\x83\x88 "\ "\xE3\x83\x88\xE3\x82\x99\xE3\x82\xAD\xE3\x83\xA5\xE3\x83\xA1"\ "\xE3\x83\xB3\xE3\x83\x88.txt" /* NFD */ #else #define f1name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82\xA9"\ "\xE3\x83\xAB\xE3\x83\x80/\xE6\x96\xB0\xE8\xA6\x8F"\ "\xE3\x83\x86\xE3\x82\xAD\xE3\x82\xB9\xE3\x83\x88 "\ "\xE3\x83\x89\xE3\x82\xAD\xE3\x83\xA5\xE3\x83\xA1"\ "\xE3\x83\xB3\xE3\x83\x88.txt" /* NFC */ #endif assertEqualUTF8String(f1name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f2name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE6\xBC\xA2\xE5\xAD\x97\xE9\x95\xB7\xE3\x81\x84\xE3\x83\x95"\ "\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\xE5\x90\x8Dlong-filename-in-"\ "\xE6\xBC\xA2\xE5\xAD\x97.txt" /* NFD */ #else #define f2name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE6\xBC\xA2\xE5\xAD\x97\xE9\x95\xB7\xE3\x81\x84\xE3\x83\x95"\ "\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\xE5\x90\x8Dlong-filename-in-"\ "\xE6\xBC\xA2\xE5\xAD\x97.txt" /* NFC */ #endif assertEqualUTF8String(f2name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(5, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualIntA(a, 5, archive_read_data(a, buff, 5)); assertEqualMem(buff, test_txt, 5); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f3name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82"\ "\xA9\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x99" /* NFD */ #else #define f3name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82"\ "\xA9\xE3\x83\xAB\xE3\x83\x80" /* NFC */ #endif assertEqualUTF8String(f3name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f4name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88" /* NFD */ #else #define f4name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88" /* NFC */ #endif assertEqualUTF8String(f4name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fifth header, which has a symbolic-link name in multi-byte characters. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f5name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB" /* NFD */ #else #define f5name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB" /* NFC */ #endif assertEqualUTF8String(f5name, archive_entry_pathname(ae)); assertEqualUTF8String( "\xE6\xBC\xA2\xE5\xAD\x97\xE9\x95\xB7\xE3\x81\x84\xE3\x83\x95" "\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\xE5\x90\x8Dlong-filename-in-" "\xE6\xBC\xA2\xE5\xAD\x97.txt", archive_entry_symlink(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41453, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff))); /* Sixth header */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualUTF8String( "abcdefghijklmnopqrs\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33204, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, 16, archive_read_data(a, buff, sizeof(buff))); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_unicode_CP932) { char buff[30]; const char reffile[] = "test_read_format_rar_unicode.rar"; const char test_txt[] = "kanji"; struct archive_entry *ae; struct archive *a; if (NULL == setlocale(LC_ALL, "Japanese_Japan") && NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { skipping("CP932 locale not available on this system."); return; } 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)); /* Specify the charset of symbolic-link file name. */ if (ARCHIVE_OK != archive_read_set_options(a, "rar:hdrcharset=UTF-8")) { skipping("This system cannot convert character-set" " from UTF-8 to CP932."); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } assertA(0 == archive_read_open_filename(a, reffile, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/\x90\x56\x82\xb5\x82\xa2" "\x83\x74\x83\x48\x83\x8b\x83\x5f/\x90\x56\x8b\x4b\x83\x65\x83\x4c" "\x83\x58\x83\x67 \x83\x68\x83\x4c\x83\x85\x83\x81\x83\x93\x83\x67.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/\x8a\xbf\x8e\x9a" "\x92\xb7\x82\xa2\x83\x74\x83\x40\x83\x43\x83\x8b\x96\xbc\x6c" "\x6f\x6e\x67\x2d\x66\x69\x6c\x65\x6e\x61\x6d\x65\x2d\x69\x6e" "\x2d\x8a\xbf\x8e\x9a.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(5, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(5 == archive_read_data(a, buff, 5)); assertEqualMem(buff, test_txt, 5); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/" "\x90\x56\x82\xb5\x82\xa2\x83\x74\x83\x48\x83\x8b\x83\x5f", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fifth header, which has a symbolic-link name in multi-byte characters. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/" "\x83\x74\x83\x40\x83\x43\x83\x8B", archive_entry_pathname(ae)); assertEqualString("\x8a\xbf\x8e\x9a" "\x92\xb7\x82\xa2\x83\x74\x83\x40\x83\x43\x83\x8b\x96\xbc\x6c" "\x6f\x6e\x67\x2d\x66\x69\x6c\x65\x6e\x61\x6d\x65\x2d\x69\x6e" "\x2d\x8a\xbf\x8e\x9a.txt", archive_entry_symlink(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41453, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff))); /* Sixth header */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualUTF8String( "abcdefghijklmnopqrs\x83\x65\x83\x58\x83\x67.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33204, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, 16, archive_read_data(a, buff, sizeof(buff))); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_compress_normal) { const char reffile[] = "test_read_format_rar_compress_normal.rar"; char file1_buff[20111]; int file1_size = sizeof(file1_buff); const char file1_test_txt[] = "


\n" "

\n" "\n" ""; char file2_buff[20]; int file2_size = sizeof(file2_buff); const char file2_test_txt[] = "test text document\r\n"; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testlink", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41471, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae)); assertEqualIntA(a, 0, archive_read_data(a, file1_buff, 30)); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file2_size == archive_read_data(a, file2_buff, file2_size)); assertEqualMem(&file2_buff[file2_size + 1 - sizeof(file2_test_txt)], file2_test_txt, sizeof(file2_test_txt) - 1); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Sixth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* This test is for sufficiently large files that would have been compressed * using multiple lzss blocks. */ DEFINE_TEST(test_read_format_rar_multi_lzss_blocks) { const char reffile[] = "test_read_format_rar_multi_lzss_blocks.rar"; const char test_txt[] = "-bottom: 0in\">
\n

\n\n"; int size = 20131111, offset = 0; char buff[64]; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("multi_lzss_blocks_test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); while (offset + (int)sizeof(buff) < size) { assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); offset += sizeof(buff); } assertA(size - offset == archive_read_data(a, buff, size - offset)); assertEqualMem(buff, test_txt, size - offset); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_compress_best) { const char reffile[] = "test_read_format_rar_compress_best.rar"; char file1_buff[20111]; int file1_size = sizeof(file1_buff); const char file1_test_txt[] = "


\n" "

\n" "\n" ""; char file2_buff[20]; int file2_size = sizeof(file2_buff); const char file2_test_txt[] = "test text document\r\n"; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testlink", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41471, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae)); assertEqualIntA(a, 0, archive_read_data(a, file1_buff, 30)); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file2_size == archive_read_data(a, file2_buff, file2_size)); assertEqualMem(&file2_buff[file2_size + 1 - sizeof(file2_test_txt)], file2_test_txt, sizeof(file2_test_txt) - 1); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Sixth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* This is a test for RAR files compressed using a technique where compression * switches back and forth to and from ppmd and lzss decoding. */ DEFINE_TEST(test_read_format_rar_ppmd_lzss_conversion) { const char reffile[] = "test_read_format_rar_ppmd_lzss_conversion.rar"; const char test_txt[] = "gin-bottom: 0in\">
\n

\n\n"; int size = 241647978, offset = 0; char buff[64]; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("ppmd_lzss_conversion_test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); while (offset + (int)sizeof(buff) < size) { assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); offset += sizeof(buff); } assertA(size - offset == archive_read_data(a, buff, size - offset)); assertEqualMem(buff, test_txt, size - offset); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_binary) { const char reffile[] = "test_read_format_rar_binary_data.rar"; char *file1_buff = malloc(1048576); int file1_size = 1048576; const char file1_test_txt[] = "\x37\xef\xb2\xbe\x33\xf6\xcc\xcb\xee\x2a\x10" "\x9d\x2e\x01\xe9\xf6\xf9\xe5\xe6\x67\x0c\x2b" "\xd8\x6b\xa0\x26\x9a\xf7\x93\x87\x42\xf1\x08" "\x42\xdc\x9b\x76\x91\x20\xa4\x01\xbe\x67\xbd" "\x08\x74\xde\xec"; char file2_buff[32618]; int file2_size = sizeof(file2_buff); const char file2_test_txt[] = "\x00\xee\x78\x00\x00\x4d\x45\x54\x41\x2d\x49" "\x4e\x46\x2f\x6d\x61\x6e\x69\x66\x65\x73\x74" "\x2e\x78\x6d\x6c\x50\x4b\x05\x06\x00\x00\x00" "\x00\x12\x00\x12\x00\xaa\x04\x00\x00\xaa\x7a" "\x00\x00\x00\x00"; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("random_data.bin", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.odt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file2_size == archive_read_data(a, file2_buff, file2_size)); assertEqualMem(&file2_buff[file2_size + 1 - sizeof(file2_test_txt)], file2_test_txt, sizeof(file2_test_txt) - 1); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(2, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); free(file1_buff); } DEFINE_TEST(test_read_format_rar_windows) { char buff[441]; const char reffile[] = "test_read_format_rar_windows.rar"; const char test_txt[] = "test text file\r\n"; int size = sizeof(test_txt)-1; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testshortcut.lnk", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(sizeof(buff), archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_multivolume) { const char *reffiles[] = { "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 }; int file1_size = 241647978, offset = 0; char buff[64]; const char file1_test_txt[] = "gin-bottom: 0in\">
\n

\n\n" ""; char file2_buff[20111]; int file2_size = sizeof(file2_buff); const char file2_test_txt[] = "


\n" "

\n" "\n" ""; char file3_buff[20]; int file3_size = sizeof(file3_buff); const char file3_test_txt[] = "test text document\r\n"; struct archive_entry *ae; struct archive *a; extract_reference_files(reffiles); 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, reffiles, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("ppmd_lzss_conversion_test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); while (offset + (int)sizeof(buff) < file1_size) { assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); offset += sizeof(buff); } assertA(file1_size - offset == archive_read_data(a, buff, file1_size - offset)); assertEqualMem(buff, file1_test_txt, file1_size - offset); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file2_size == archive_read_data(a, file2_buff, file2_size)); assertEqualMem(&file2_buff[file2_size - sizeof(file2_test_txt) + 1], file2_test_txt, sizeof(file2_test_txt) - 1); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testlink", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41471, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae)); assertEqualIntA(a, 0, archive_read_data(a, file2_buff, 30)); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file3_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file3_size == archive_read_data(a, file3_buff, file3_size)); assertEqualMem(&file3_buff[file3_size + 1 - sizeof(file3_test_txt)], file3_test_txt, sizeof(file3_test_txt) - 1); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file2_size == archive_read_data(a, file2_buff, file2_size)); assertEqualMem(&file2_buff[file2_size - sizeof(file2_test_txt) + 1], file2_test_txt, sizeof(file2_test_txt) - 1); /* Sixth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Seventh header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(7, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_multivolume_skip) { const char *reffiles[] = { "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 }; int file1_size = 241647978; int file2_size = 20111; int file3_size = 20; struct archive_entry *ae; struct archive *a; extract_reference_files(reffiles); 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, reffiles, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("ppmd_lzss_conversion_test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testlink", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41471, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae)); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file3_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Sixth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Seventh header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(7, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_sfx) { char buff[441]; const char reffile[] = "test_read_format_rar_sfx.exe"; const char test_txt[] = "test text file\r\n"; int size = sizeof(test_txt)-1; struct archive_entry *ae; struct archive *a; 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)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testshortcut.lnk", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(sizeof(buff), archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_multivolume_stored_file) { const char *reffiles[] = { "test_rar_multivolume_single_file.part1.rar", "test_rar_multivolume_single_file.part2.rar", "test_rar_multivolume_single_file.part3.rar", NULL }; char file_buff[20111]; int file_size = sizeof(file_buff); const char file_test_txt[] = "


\n" "

\n" "\n" ""; struct archive_entry *ae; struct archive *a; extract_reference_files(reffiles); 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, reffiles, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertA(file_size == archive_read_data(a, file_buff, file_size)); assertEqualMem(&file_buff[file_size - sizeof(file_test_txt) + 1], file_test_txt, sizeof(file_test_txt) - 1); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_multivolume_stored_file_skip) { const char *reffiles[] = { "test_rar_multivolume_single_file.part1.rar", "test_rar_multivolume_single_file.part2.rar", "test_rar_multivolume_single_file.part3.rar", NULL }; int file_size = 20111; struct archive_entry *ae; struct archive *a; extract_reference_files(reffiles); 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, reffiles, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_multivolume_seek_data) { const char *reffiles[] = { "test_rar_multivolume_single_file.part1.rar", "test_rar_multivolume_single_file.part2.rar", "test_rar_multivolume_single_file.part3.rar", NULL }; char buff[64]; int file_size = 20111; const char file_test_txt1[] = "d. \n

\n

" "
\n

\n\n"; const char file_test_txt2[] = "\n<"; const char file_test_txt3[] = "mplify writing such tests,\ntry to use plat" "form-independent codin"; const char file_test_txt4[] = "lString in the example above)\ngenerat" "e detailed log message"; const char file_test_txt5[] = "SS=\"western\">make check will usually" " run\n\tall of the tests."; struct archive_entry *ae; struct archive *a; extract_reference_files(reffiles); 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, reffiles, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Seek to the end minus 64 bytes */ assertA(file_size - (int)sizeof(buff) == archive_seek_data(a, file_size - (int)sizeof(buff), SEEK_SET)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt1, sizeof(file_test_txt1) - 1); /* Seek back to the beginning */ assertA(0 == archive_seek_data(a, 0, SEEK_SET)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt2, sizeof(file_test_txt2) - 1); /* Seek to the middle of the combined data block */ assertA(10054 == archive_seek_data(a, 10054, SEEK_SET)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt3, sizeof(file_test_txt3) - 1); /* Seek to 32 bytes before the end of the first data sub-block */ assertA(6860 == archive_seek_data(a, 6860, SEEK_SET)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt4, sizeof(file_test_txt4) - 1); /* Seek to 32 bytes before the end of the second data sub-block */ assertA(13752 == archive_seek_data(a, 13752, SEEK_SET)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt5, sizeof(file_test_txt5) - 1); /* Use various combinations of SEEK_SET, SEEK_CUR, and SEEK_END */ assertEqualInt(file_size, archive_seek_data(a, 0, SEEK_END)); assertEqualInt(0, archive_seek_data(a, 0, SEEK_SET)); assertEqualInt(0, archive_seek_data(a, 0, SEEK_CUR)); assertEqualInt(-1, archive_seek_data(a, -10, SEEK_CUR)); assertEqualInt(10, archive_seek_data(a, 10, SEEK_CUR)); assertEqualInt(-1, archive_seek_data(a, -20, SEEK_CUR)); assertEqualInt(10, archive_seek_data(a, 0, SEEK_CUR)); assertEqualInt(file_size, archive_seek_data(a, 0, SEEK_END)); assertEqualInt(file_size - 20, archive_seek_data(a, -20, SEEK_END)); assertEqualInt(file_size + 40, archive_seek_data(a, 40, SEEK_END)); assertEqualInt(file_size + 40, archive_seek_data(a, 0, SEEK_CUR)); assertEqualInt(file_size + 40 + 20, archive_seek_data(a, 20, SEEK_CUR)); assertEqualInt(file_size + 40 + 20 + 20, archive_seek_data(a, 20, SEEK_CUR)); assertEqualInt(file_size + 20, archive_seek_data(a, 20, SEEK_END)); assertEqualInt(file_size - 20, archive_seek_data(a, -20, SEEK_END)); /* * Attempt to read from the end of the file. These should return * 0 for end of file. */ assertEqualInt(file_size, archive_seek_data(a, 0, SEEK_END)); assertA(0 == archive_read_data(a, buff, sizeof(buff))); assertEqualInt(file_size + 40, archive_seek_data(a, 40, SEEK_CUR)); assertA(0 == archive_read_data(a, buff, sizeof(buff))); /* Seek to the end minus 64 bytes */ assertA(0 == archive_seek_data(a, 0, SEEK_SET)); assertA(file_size - (int)sizeof(buff) == archive_seek_data(a, -(int)sizeof(buff), SEEK_END)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt1, sizeof(file_test_txt1) - 1); /* The file position should be at the end of the file here */ assertA(file_size == archive_seek_data(a, 0, SEEK_CUR)); /* Seek back to the beginning */ assertA(0 == archive_seek_data(a, -file_size, SEEK_CUR)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt2, sizeof(file_test_txt2) - 1); /* Seek to the middle of the combined data block */ assertA(10054 == archive_seek_data(a, 10054 - (int)sizeof(buff), SEEK_CUR)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt3, sizeof(file_test_txt3) - 1); /* Seek to 32 bytes before the end of the first data sub-block */ assertA(6860 == archive_seek_data(a, 6860 - (10054 + (int)sizeof(buff)), SEEK_CUR)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt4, sizeof(file_test_txt4) - 1); /* Seek to 32 bytes before the end of the second data sub-block */ assertA(13752 == archive_seek_data(a, 13752 - file_size, SEEK_END)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, file_test_txt5, sizeof(file_test_txt5) - 1); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_rar_multivolume_seek_multiple_files) { const char *reffiles[] = { "test_rar_multivolume_multiple_files.part1.rar", "test_rar_multivolume_multiple_files.part2.rar", "test_rar_multivolume_multiple_files.part3.rar", "test_rar_multivolume_multiple_files.part4.rar", "test_rar_multivolume_multiple_files.part5.rar", "test_rar_multivolume_multiple_files.part6.rar", NULL }; char buff[64]; int file_size = 20111; const char file_test_txt1[] = "d. \n

\n

" "
\n

\n\n"; const char file_test_txt2[] = "\n<"; const char file_test_txt3[] = "mplify writing such tests,\ntry to use plat" "form-independent codin"; const char file_test_txt4[] = "\nfailures. \n

\n

<" "A NAME=\"Life_cycle_of_a_te"; const char file_test_txt5[] = "LE=\"margin-bottom: 0in\">DO use runtime te" "sts for platform\n\tfeatu"; const char file_test_txt6[] = "rough test suite is essential\nboth for ver" "ifying new ports and f"; const char file_test_txt7[] = "m: 0in\">Creates a temporary directory\n\tw" "hose name matches the na"; const char file_test_txt8[] = "lt\ninput file and verify the results. Thes" "e use \n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, -1, archive_seek_data(a, -(((int)sizeof(buff)-1)*2), SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_seek_data(a, 0, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualIntA(a, ((sizeof(buff)-1)*2), archive_seek_data(a, 0, SEEK_CUR)); assertEqualStringA(a, "\n\n\t\n\t\n\t 0); /* Seek to the end minus (sizeof(buff)-1) bytes */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); /* Seek back to the beginning */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_SET works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, 13164, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, 13164, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, 13164, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_CUR works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, 13164, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -13227, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, -6947, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 6821, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, -6947, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -13227, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_END works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, -6947, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, -6947, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13164, archive_seek_data(a, -6947, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "ertEqualInt,\n\tassertEqualString, " "assertEqualMem to test equalit", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* * Second header. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualStringA(a, "testdir/testsubdir/LibarchiveAddingTest2.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualIntA(a, 20111, archive_entry_size(ae)); assertEqualIntA(a, 33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* Read from the beginning to the end of the file */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); do { memset(buff, 0, sizeof(buff)); bytes_read = archive_read_data(a, buff, (sizeof(buff)-1)); } while (bytes_read > 0); /* Seek to the end minus (sizeof(buff)-1) bytes */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); /* Seek back to the beginning */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_SET works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, 6162, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 19347, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 19347, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, 6162, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, 6162, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 19347, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 19347, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, 6162, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); /* Test that SEEK_CUR works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, 6162, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 13122, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 638, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, -764, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13248, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -6225, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13949, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 13122, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -19410, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, 19284, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13248, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); /* Test that SEEK_END works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13949, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, -764, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, -764, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13949, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13949, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, -764, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 19347, archive_seek_data(a, -764, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " when a block being written out by\n" "the archive writer is the sa", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 6162, archive_seek_data(a, -13949, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "arguments satisfy certain conditions. " "If the assertion fails--f", buff); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* * Third header. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualStringA(a, "LibarchiveAddingTest2.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualIntA(a, 20111, archive_entry_size(ae)); assertEqualIntA(a, 33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* Read from the beginning to the end of the file */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); do { memset(buff, 0, sizeof(buff)); bytes_read = archive_read_data(a, buff, (sizeof(buff)-1)); } while (bytes_read > 0); /* Seek to the end minus (sizeof(buff)-1) bytes */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); /* Seek back to the beginning */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_SET works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, 12353, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, 12353, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, 12353, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_CUR works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, 12353, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -12416, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, -7758, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 7632, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, -7758, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -12416, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_END works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, -7758, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, -7758, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 12353, archive_seek_data(a, -7758, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, " 0.2in\">    " "extract_reference_file("test_foo.tar", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* * Fourth header. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualStringA(a, "testdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualIntA(a, 20111, archive_entry_size(ae)); assertEqualIntA(a, 33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* Read from the beginning to the end of the file */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); do { memset(buff, 0, sizeof(buff)); bytes_read = archive_read_data(a, buff, (sizeof(buff)-1)); } while (bytes_read > 0); /* Seek to the end minus (sizeof(buff)-1) bytes */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); /* Seek back to the beginning */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_SET works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, 5371, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 13165, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 13165, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, 5371, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, 5371, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 13165, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 13165, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, 5371, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); /* Test that SEEK_CUR works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, 5371, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 7731, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 6820, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, -6946, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -7857, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -5434, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -14740, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 7731, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -13228, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, 13102, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -7857, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); /* Test that SEEK_END works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -14740, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, -6946, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, -6946, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -14740, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -14740, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, -6946, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 13165, archive_seek_data(a, -6946, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "rtEqualInt,\n\tassertEqualString, " "assertEqualMem to test equality", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 5371, archive_seek_data(a, -14740, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "zip)\n  {\n    " "/* ... setup omitted ... */\n  ", buff); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* * Fifth header. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualStringA(a, "testdir/testsubdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualIntA(a, 20111, archive_entry_size(ae)); assertEqualIntA(a, 33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* Read from the beginning to the end of the file */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); do { memset(buff, 0, sizeof(buff)); bytes_read = archive_read_data(a, buff, (sizeof(buff)-1)); } while (bytes_read > 0); /* Seek to the end minus (sizeof(buff)-1) bytes */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); /* Seek back to the beginning */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_SET works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, 11568, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, 11568, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, 11568, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n", buff); /* Test that SEEK_CUR works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, 11568, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, -8543, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, -8543, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n", buff); /* Test that SEEK_END works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, -8543, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, -8543, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 11568, archive_seek_data(a, -8543, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ", assertFileContents," "\n\t\n", buff); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* * Sixth header. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualStringA(a, "LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualIntA(a, 20111, archive_entry_size(ae)); assertEqualIntA(a, 33188, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); test_read_format_rar_multivolume_uncompressed_files_helper(a); /* Read from the beginning to the end of the file */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); do { memset(buff, 0, sizeof(buff)); bytes_read = archive_read_data(a, buff, (sizeof(buff)-1)); } while (bytes_read > 0); /* Seek to the end minus (sizeof(buff)-1) bytes */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); /* Seek back to the beginning */ memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); /* Test that SEEK_SET works correctly between data blocks */ assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, 4576, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 17749, archive_seek_data(a, 17749, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\">Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, 4576, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, 4576, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 20111 - (int)(sizeof(buff)-1), SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, 0, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 17749, archive_seek_data(a, 17749, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\">Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, 4576, SEEK_SET)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

\n

Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 2236, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 17749, archive_seek_data(a, -2362, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\">Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, -13236, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, -15535, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -17812, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, 19985, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 17749, archive_seek_data(a, 17686, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\">Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, -13236, SEEK_CUR)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

\n

Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 17749, archive_seek_data(a, -2362, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\">Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, -15535, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, -15535, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 20111 - (int)(sizeof(buff)-1), archive_seek_data(a, -((int)sizeof(buff)-1), SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, ". \n

\n


\n" "

\n\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 0, archive_seek_data(a, -20111, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\n", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 17749, archive_seek_data(a, -2362, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "\">Large tar tester

\n

The " "large tar tester attempts to", buff); memset(buff, 0, sizeof(buff)); assertEqualIntA(a, 4576, archive_seek_data(a, -15535, SEEK_END)); assertEqualIntA(a, (sizeof(buff)-1), archive_read_data(a, buff, (sizeof(buff)-1))); assertEqualStringA(a, "hat was expected. \n

\n

#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)); 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(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(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(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(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(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(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(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(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"); assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * buffer overflow errors during reading rar5 tables. */ 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_leftshift1) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_leftshift1.rar"); assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations 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_leftshift2) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_leftshift2.rar"); assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations 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_truncated_huff) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_truncated_huff.rar"); assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations 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_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"); assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * errors related to variable overflows 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_nonempty_dir_stream) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_nonempty_dir_stream.rar"); assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * errors related to buffer overflows 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_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(); +} Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_rar5_different_window_size.rar.uu =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_rar5_different_window_size.rar.uu (nonexistent) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_rar5_different_window_size.rar.uu (revision 349525) @@ -0,0 +1,675 @@ +begin 600 test_read_format_rar5_different_window_size.rar +M4F%R(1H'`0"-[P+2``'#M#P\7P$'`0"-[P+2``7#`/KZ^OKZA5N8F)B8F)@` +MF`*8T@7"F!=A_________P$$_____________________R%285(A&@?_____ +M_________________V@`[E##M#P\7P$'`0"-[P+2``7"87)26`!W=%)A\"T@`"QP\)`'(A&@\"T@`" +MQP\`"7(AFC`!&B/2+0`"*"%285(A&@<8`0"-[P+2``7#10!A4B$:!Q@!`#)S +M-/_______U)A\"T@`"!QS1T='1T='1T='6T='1T='1T='1T='1 +MT='1T='1T='1T='1T=&UQX`.`"'X"/\E``*H'#`"`/+__TO__O_G*_____\` +M`"\O``#_02+M____`-X`_["&AFVQJ@,#45TW,?;V]@$``/____\3]O;V]O;_ +M_S\``/]!(.VG+R\``/]!(.T)__^PWOS_```O+S$``/8@[;L````````````` +M-3XR9%Q<7%Q<7%Q<7%Q<7%QZ7%PJ7%Q<7"]<7%Q<7%Q<7%Q<7%Q<7%Q<7%Q< +M7#)<7#9<,F1I9V5R=#4V,61I870]+@HN"G0@9&5V270@9&1I9V5S7%Q<7'=E +M9"XR9&EG97)T-38R9&EA=#TN"BX*="!D979)="!D9&EG97-T/2X*+@HN"@HF +M+BX**%)A\"T@`"!QS1T='1T='1T='6T='1T='1T='1T='1T='1 +MT='1T='1T='1T=&UQX`.`"'X"/\E``*H'#`"`/+__TO__O_G*_____\``"\O +M``#_02+M____`-X`_["&AFVQJ@,#45TW,?;V]@$````````3]O;V]O;__S\` +M`/]!(.VG+R\``/]!(.T)__^PWOS_```O+P```/8@[;L`````````````-38R +M9%Q<7%Q<7%Q<7%Q<7%QZ7%PJ7%Q<7"]<7%Q<7%Q<7%Q<7%Q<7%Q<7%Q<7#)< +M7#9<,F1I9V5R=#4V,61I870]+@HN"G0@9&5V270@9&1I9V5S7%Q<7'=E9"XR +M9&EG97)T-38R9&EA=#TN"BX*="!D979)="!D9&EG97-T/2X*+@HN"@HF+E)A +M\"T@`"PP<KJZNKJZNKJZNKJZNKJZNKJ +MZNKJZNKJZNKJZNKJZNKJZNKJZNKJZNKJZNKJZNK#P\/#P\/#P\/#R\-MP\/# +MP\/#P\/#P\/#P\/#P\/#`\"T@`#QP\`"2$:TB,M``(H(5)A4B$:!Q@!`(WO`M(`!0`````````````` +M`````````0``````_____P#_965E965E965E965E965E965E8'-T/2X*+@HN +M"C(P-S`W,&EA='EAEQ<*EQ<7%PO7%Q<7%Q<7%Q<7%Q<7%Q<7%Q<7%PR7%PV7#!D:6=E +M\"T@`%PP`:!P`$9P`` +M````````[P+2``+'#P`)`"X),AP`_0$`%___"0`N"3(<`/T!`!?__Q%287(A +M_Q%287(A&H\Q`"@HW,@```/V-[P+2 +M``+'`"X),1P`_0$`%___$5)A<@$`C>\"T@`%PP`:!P`$9P``````````[P+2 +M``+'#P`)`"X),AP`_0$`%___$5)A\"T@`#QP\`"2$:TB,M``DA +M&M(C+0`"*"%285(A&@<8`0"-[P+2``7#`!H'`/R8__]E965E965E965E965E +M965E965@6%R(0HN"@HF+B8N"B@*+@HN"BY0 +M7B]A,C5I-F1G97-T/2X*+@HN"@HF+BX*"@HF+BX**`HN"BX*+E!<-3,R-C)D +M+W-.="`@6W-T/3TN"BX*+@H*)BXN"B@*+@HN"BY07#4S,C8A(2$A(2$A(2$A +M(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A +M(2$A(2$A(2$A(2$A(2$A(2$A+@H*)BXN+EPV7#)D:6=E\"T@`%```` +M```````````````````!``````#_____```O+P``_T$@[?___[#>`/\`AH:Q +M;?;V]@.J_O;_S_\`Q+[?$?;?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?___?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_? +MW]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W_^[XP## +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````#_________ +M__________________________________________\````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````"`````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````+8````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````!287(A&@\"T@`%PF%R4E@`=W1287(A&@EFZ>45J>E"(8*1QP&#AP1O__`````!X````````````` +MNKJZNKJZN@'U``"R____`````!X```"````````````````````````````` +M`"Q!`````````"T`%0``:#H`6@$M\"T@`"PP<< +MP@2```#_;24``F<<`0(`#@```0!02P@#`"8F)@!`___/0T+_____\?__K2-T +M='3_______[______R9"____,3,U__\3$Q,3$Q,3$Q,3$Q,3$Q,3$Q,3$Q,3 +M$Q,3$Q,3$Q,3$Q,3$Q,3$Q,3$R8J8BQB`/______0D)"0D)"0D)"0D)"0D)" +M0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D+;V]O;V]O;V]O;V]O;-C8V +M-C8V&4PA`C8V-C8V-C8V-C8V-C8V&4PA`C8V-C8V-C8V-C8V-C8H*"@H*"@H +M*"@H*"@H(1H'`0"-[P(H*"@H*"@V-C8V-C8V-C8V-I8V-C8V-C8V-MO;VP`` +M````````````````````````````V]O;V]L`VP````#;V]L`VR@H*"@H*#8V +M-C8V-C8V-C8VEC8V-C8V-C8VV]O;``````````````````````````````#; +MV]O;VP#;`````-O;VP#;``````````````````````!"0D)"0D)"0D)S0D)" +M0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0EU=75)A4B$:!V%R0D)"0D)"0D)" +M0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0MO;V]O;V]O; +MV]O;V]LV-C8V-C893"$"-C8V-C8V-C8V-C8V-C893"$"-C8V-C8V-C8V-C8V +M-B@H*"@H*"@H*"@H*"@A&@\" +M*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V-C;;V]L````````````````````` +M`````````-O;V]O;`-L`````V]O;`-L``````````````````````$)"0D)" +M0D)"0G-"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"75U=4F%2(1H'87)" +M0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)" +MV]O;V]O;V]O;V]O;VS8V-C8V-AE,(0(V-C8V-C8V-C8V-C8V-AE,(0(V-C8V +M-C8V-C8V-C8V*"@H*"@H*"@H*"@H*"$:!P$`C>\"*"@H*"@H-C8V-C8V-C8V +M-C:6-C8V-C8V-C;;V]L``````````````````````````````-O;V]O;`-L` +M````V]O;`-LH*"@H*"@V-C8V-C8V-C8V-I8V-C8V-C8V-MO;VP`````````` +M````````````````````V]O;V]L`VP````#;V]L`VP`````````````````` +M````0D)"0D)"0D)"\"T@`%PT4```!&%<\"T@`%PF%R4E@`=W1287(A&@7EY0H```#EY>7EY>7EY>7EY>7EY>7EY>7EY>7E +MY>7EY>7EY>5W=V%R(2\O+R`N"@H*75U=75U9R,C(R,C(R,C(R,C(R"<.`'\` +M``I=70!=!UU=7=D`Y>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7E +MY>7EY>7EY>7EY>7EY>7EY>7EY4$X.#@X]C@X+3@X.#@X.#@X.#@X.#@X.#@X +M.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#AR.#@X.#@X.#@X.#@X.#@X.#@X +M.#@X./DX.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X\"T@`%P_J%6YB8F)B8F`"8`IC2!<*8%V'_ +M________`03_____________________(5)A4B$:!___________________ +M____:`#N4,.T/#Q?`0\"T@`" +MQP\)`'(A&@\"T@`"QP\`"7(AFC`! +M&B/2+0`"*"%285(A&@<8`0"-[P+2``7#15\!!P$`C>\"T@`%PF%R4E@`=W12 +M87(A&@\"T@`%PP```$85!<)A +M\"T@`%PT4```!&%<\"T@`%PF%R4E@` +M=W1287(A&@`````````````+JZNKJZNKH!]0``LO___P`````>```` +M@``````````````````````````````L00`````````M`!4``&@Z`%H!+7-L +M-S8`R!$`````````XJ$`8VDP`#(`93$``/_______________P`````````` +M``H*"@H```!=75U=75U=75U=75U=75T````````````````````````````` +M``````!287(A&@7E"@```.7EY>7EY>7EY>7EY>7EY>7EY>7E +MY>7EY>7EY>7EY7=W87(A+R\O("X*"@I=75U=75G(R,C(R,C(R,C(R,C()PX` +M?P``"EU=`%T'75U=V0#EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7E +MY>7EY>7EY>7EY>7EY>7EY>7EY>7E03@X.#CV.#@M.#@X.#@X.#@X.#@X.#@X +M.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.'(X.#@X.#@X.#@X.#@X.#@X +M.#@X.#@X^3@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#AR.#@X=_[_________ +M&4PA`C8V-C8V-C8V-C8V-C8R&4PA`C8V-C8V-C8V-C8V-C8V-C8V-C8V"#8V +M-C:6-C8V-C8V-C;;V]O;V]O;V__;V]O;V]O;V]O;V]O;V]O;VS8V-C8V-C8V +M-C8V-C8V-@@V-C8VEC8V-C8V-C8VV]O;V]O;V]O;V]O;V]O;V]O;V]O;V]O; +MV]LV-C8V-C;;V]O;V]O;V]O;VUU=75U=75U=75U=75U=75U=75U=V]O;V]O; +MV]LV-C8V-C8V&4PA`C8V-C8V-C8V-MO;V]O;V]O;V]O;V]O;V]O;V]O;V]O; +MV]O;-C8V-C8V-AE,(0(V-C8V-C8V-C8V-C8V-AE,(0(V-C8V-C8V-C8V-C8V +M-C8V-C8V-C8V-C:6-C8V-C8V-C;;V]O;V]O;V]O;V]O;V]O;V]O;V]O;V]O; +MV]O;V]O;V]O;V]O;V]O;V]O;V]O;V]M"0D)"```````````````````````` +M``````````````````````````````````````````````````!"0D)"0D)" +M0D)S0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0EU=75)A4B$:!V%R0D)" +M0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0MO; +MV]O;V]O;V]O;V]LV-C8V-C893"$"-C8V-C8V-C8V-C8V-C893"$"-C8V-C8V +M-C8V-C8V-B@H*"@H*"@H*"@H*"@A&@\"*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V +M-C;;V]L``````````````````````````````-O;V]O;`-L`````V]O;`-O_ +M____________________________________________________________ +M_____________________R8@)B8?)B8F)B8F)B8F)B8F)R8F```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````/8``)0X.#@X73@X..7EY>4*```` +MY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7E=W=A7EY>7EY>7E +MY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>5!.#@X +M./8X."TX.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X +M.#@X\"*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V-C;;V]L````````````````` +M`````````````-O;V]O;`-L`````V]O;`-L``````````````````````$)" +M0D)"0D)"0G-"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"75U=4F%2(1H' +M87)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)" +M0D)"V]O;V]O;V]O;V]O;VS8V-C8V-AE,(0(V-C8V-C8V-C8V-C8V-AE,(0(V +M-C8V-C8V-C8V-C8V*"@H*"@H*"@H*"@H*"$:!P$`C>\"*"@H*"@H-C8V-C8V +M-C8V-C:6-C8V-C8V-C;;V]L``````````````````````````````-O;V]O; +M`-L`````V]O;`-LH*"@H*"@V-C8V-C8V-C8V-I8V-C8V-C8V-MO;VP`````` +M````````````````````````V]O;V]L`VP````#;V]L`VP`````````````` +M````````0D)"0D)"0D)"\"*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V-C;;V]L````` +M`````````````````````````-O;V]O;`-L`````V]O;`-L"-C8V-C8V-C8V +M-C8V-B@H*"@H*"@H*"@H*"@A&@\"*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V-C;; +MV]L``````````````````````````````-O;V]O;`-L`````V]O;`-O_____ +M____________________________________________________________ +M_________________R8@)B8?)B8F)B8F)B8F)B8F)R8F```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````/8``)0X.#@X73@X..7EY>4*````Y>7E +MY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7E=W=A7EY>7EY>7EY>7E +MY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>7EY>5!.#@X./8X +M."TX.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X.#@X +M\" +M*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V-C;;V]L````````````````````` +M`````````-O;V]O;`-L`````V]O;`-L``````````````````````$)"0D)" +M0D)"0G-"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"75U=4F%2(1H'87)" +M0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)" +MV]O;V]O;V]O;V]O;VS8V-C8V-AE,(0(V-C8V-C8V-C8V-C8V-AE,(0(V-C8V +M-C8V-C8V-C8V*"@H*"@H*"@H*"@H*"$:!P$`C>\"*"@H*"@H-C8V-C8V-C8V +M-C:6-C8V-C8V-C;;V]L``````````````````````````````-O;V]O;`-L` +M````V]O;`-LH*"@H*"@V-C8V-C8V-C8V-I8V-C8V-C8V-MO;VP`````````` +M````````````````````V]O;V]L`VP````#;V]L`VP`````````````````` +M````0D)"0D)"0D)"\"*"@H*"@H-C8V-C8V-C8V-C:6-C8V-C8V-C;;V]L````````` +@`````````````````````-O;V]O;`-L`````V]O;`-L` +` +end Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free2.rar.uu =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free2.rar.uu (nonexistent) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free2.rar.uu (revision 349525) @@ -0,0 +1,10 @@ +begin 664 test_read_format_rar_ppmd_use_after_free2.rar +M4F%R(1H'``1G=$Q24`!W````>U!+`P0Q`'#_J7\`+@TU'`#]`0`7__]"0D)" +M+W5N)B8F)F=I9`UD#1T+``!"`````````&%R(1H'``3_________`F@`H2`` +M``"`P\/#2\/#P\/#P\/#P\-3PP"`P\/#PYZ>AYZ>GI[#4\,`@,/#`L,@(""= +M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(/______ +M__\@("`@("`@("`@("`@("`@("`@("`@("`$9W1,4E``=P```'M02P,$,0!P +M_ZE_`"X--1P`_0$`%___0D)"0B]U;B8F)B9G:60-9`T="P``0@````````!A +0 #include #include #include #include "err.h" #include "line_reader.h" #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) #define strdup _strdup #endif /* * Read lines from file and do something with each one. If option_null * is set, lines are terminated with zero bytes; otherwise, they're * terminated with newlines. * * This uses a self-sizing buffer to handle arbitrarily-long lines. */ struct lafe_line_reader { FILE *f; - char *buff, *buff_end, *line_start, *line_end, *p; + char *buff, *buff_end, *line_start, *line_end; char *pathname; size_t buff_length; int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */ - int ret; }; struct lafe_line_reader * lafe_line_reader(const char *pathname, int nullSeparator) { struct lafe_line_reader *lr; lr = calloc(1, sizeof(*lr)); if (lr == NULL) lafe_errc(1, ENOMEM, "Can't open %s", pathname); lr->nullSeparator = nullSeparator; lr->pathname = strdup(pathname); if (strcmp(pathname, "-") == 0) lr->f = stdin; else lr->f = fopen(pathname, "r"); if (lr->f == NULL) lafe_errc(1, errno, "Couldn't open %s", pathname); lr->buff_length = 8192; lr->line_start = lr->line_end = lr->buff_end = lr->buff = NULL; return (lr); } static void lafe_line_reader_find_eol(struct lafe_line_reader *lr) { lr->line_end += strcspn(lr->line_end, lr->nullSeparator ? "" : "\x0d\x0a"); *lr->line_end = '\0'; /* Noop if line_end == buff_end */ } const char * lafe_line_reader_next(struct lafe_line_reader *lr) { size_t bytes_wanted, bytes_read, new_buff_size; char *line_start, *p; for (;;) { /* If there's a line in the buffer, return it immediately. */ while (lr->line_end < lr->buff_end) { line_start = lr->line_start; lr->line_start = ++lr->line_end; lafe_line_reader_find_eol(lr); if (lr->nullSeparator || line_start[0] != '\0') return (line_start); } /* If we're at end-of-file, process the final data. */ if (lr->f == NULL) { if (lr->line_start == lr->buff_end) return (NULL); /* No more text */ line_start = lr->line_start; lr->line_start = lr->buff_end; return (line_start); } /* Buffer only has part of a line. */ if (lr->line_start > lr->buff) { /* Move a leftover fractional line to the beginning. */ memmove(lr->buff, lr->line_start, lr->buff_end - lr->line_start); lr->buff_end -= lr->line_start - lr->buff; lr->line_end -= lr->line_start - lr->buff; lr->line_start = lr->buff; } else { /* Line is too big; enlarge the buffer. */ new_buff_size = lr->buff_length * 2; if (new_buff_size <= lr->buff_length) lafe_errc(1, ENOMEM, "Line too long in %s", lr->pathname); lr->buff_length = new_buff_size; /* * Allocate one extra byte to allow terminating * the buffer. */ p = realloc(lr->buff, new_buff_size + 1); if (p == NULL) lafe_errc(1, ENOMEM, "Line too long in %s", lr->pathname); lr->buff_end = p + (lr->buff_end - lr->buff); lr->line_end = p + (lr->line_end - lr->buff); lr->line_start = lr->buff = p; } /* Get some more data into the buffer. */ bytes_wanted = lr->buff + lr->buff_length - lr->buff_end; bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f); lr->buff_end += bytes_read; *lr->buff_end = '\0'; /* Always terminate buffer */ lafe_line_reader_find_eol(lr); if (ferror(lr->f)) lafe_errc(1, errno, "Can't read %s", lr->pathname); if (feof(lr->f)) { if (lr->f != stdin) fclose(lr->f); lr->f = NULL; } } } void lafe_line_reader_free(struct lafe_line_reader *lr) { free(lr->buff); free(lr->pathname); free(lr); } Index: stable/10/contrib/libarchive/libarchive_fe/passphrase.c =================================================================== --- stable/10/contrib/libarchive/libarchive_fe/passphrase.c (revision 349524) +++ stable/10/contrib/libarchive/libarchive_fe/passphrase.c (revision 349525) @@ -1,326 +1,333 @@ /*- * 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.22 2010/01/13 10:20:54 dtucker Exp $ */ +/* $OpenBSD: readpassphrase.c,v 1.27 2019/01/25 00:19:25 millert Exp $ */ + /* - * Copyright (c) 2000-2002, 2007 Todd C. Miller + * 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; struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; struct sigaction savetstp, savettin, savettou, savepipe; /* 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; + } + + /* * 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); - /* Turn off echo if possible. */ - 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; + if (!(flags & RPP_STDIN)) { + int r = write(output, prompt, strlen(prompt)); + (void)r; } - - /* No I/O if we are already backgrounded. */ - if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { - 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; + 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; - } } + *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) + errno == EINTR && !signo[SIGTTOU]) continue; + signo[SIGTTOU] = sigttou; } (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); 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); } Index: stable/10/contrib/libarchive/tar/bsdtar.1 =================================================================== --- stable/10/contrib/libarchive/tar/bsdtar.1 (revision 349524) +++ stable/10/contrib/libarchive/tar/bsdtar.1 (revision 349525) @@ -1,1270 +1,1271 @@ .\" 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 October 1, 2017 +.Dd June 3, 2019 .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 flags that might prevent removal. +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 CVS , .Sq RCS , .Sq SCCS , .Sq SVN , .Sq Arch , .Sq Bazaar , .Sq Mercurial and .Sq Darcs . .It Fl Fl fflags (c, r, u, x modes only) -Archive or extract file flags. This is the reverse of +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 attributes -using +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 flags. This is the reverse of +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 attributes using +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 n , Fl Fl norecurse , Fl Fl no-recursion .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, ACLs, -extended attributes or extended file flags). +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 attributes. This is the reverse of +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 The currently supported modules and keys are: .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 from 1 to 22 specifying the zstd compression level. .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 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 owner, file modes, ACLs, -extended attributes and extended file flags, if available, for each item -extracted from the archive. This is te reverse of +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 by root and can be partially overridden by also specifying +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 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 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 attributes. This is the reverse of +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 ".Ev BLOCKSIZE" .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: .Pp .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.) Index: stable/10/lib/libarchive/config_freebsd.h =================================================================== --- stable/10/lib/libarchive/config_freebsd.h (revision 349524) +++ stable/10/lib/libarchive/config_freebsd.h (revision 349525) @@ -1,249 +1,250 @@ /*- * 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$ */ /* FreeBSD 5.0 and later has ACL and extattr support. */ #if __FreeBSD__ > 4 #define ARCHIVE_ACL_FREEBSD 1 #define ARCHIVE_XATTR_FREEBSD 1 #define HAVE_ACL_GET_PERM_NP 1 #define HAVE_ARC4RANDOM_BUF 1 #define HAVE_STRUCT_XVFSCONF 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 #if __FreeBSD__ > 7 /* FreeBSD 8.0 and later has NFSv4 ACL support */ #define ARCHIVE_ACL_FREEBSD_NFS4 1 #define HAVE_ACL_GET_LINK_NP 1 #define HAVE_ACL_IS_TRIVIAL_NP 1 #define HAVE_ACL_SET_LINK_NP 1 #endif /* __FreeBSD__ > 7 */ #endif /* __FreeBSD__ > 4 */ #ifdef WITH_OPENSSL #define HAVE_LIBCRYPTO 1 #define HAVE_OPENSSL_EVP_H 1 #define HAVE_OPENSSL_MD5_H 1 #define HAVE_OPENSSL_RIPEMD_H 1 #define HAVE_OPENSSL_SHA_H 1 #define HAVE_OPENSSL_SHA256_INIT 1 #define HAVE_OPENSSL_SHA384_INIT 1 #define HAVE_OPENSSL_SHA512_INIT 1 #define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1 #define HAVE_SHA256 1 #define HAVE_SHA384 1 #define HAVE_SHA512 1 #else #define HAVE_LIBMD 1 #define HAVE_MD5_H 1 #define HAVE_MD5INIT 1 #define HAVE_RIPEMD_H 1 #define HAVE_SHA_H 1 #define HAVE_SHA1 1 #define HAVE_SHA1_INIT 1 #define HAVE_SHA256 1 #define HAVE_SHA256_H 1 #define HAVE_SHA256_INIT 1 #define HAVE_SHA512 1 #define HAVE_SHA512_H 1 #define HAVE_SHA512_INIT 1 #endif #define HAVE_BSDXML_H 1 #define HAVE_BZLIB_H 1 #define HAVE_CHFLAGS 1 #define HAVE_CHOWN 1 #define HAVE_CHROOT 1 #define HAVE_CTIME_R 1 #define HAVE_CTYPE_H 1 #define HAVE_DECL_EXTATTR_NAMESPACE_USER 1 #define HAVE_DECL_INT32_MAX 1 #define HAVE_DECL_INT32_MIN 1 #define HAVE_DECL_INT64_MAX 1 #define HAVE_DECL_INT64_MIN 1 #define HAVE_DECL_INTMAX_MAX 1 #define HAVE_DECL_INTMAX_MIN 1 #define HAVE_DECL_SIZE_MAX 1 #define HAVE_DECL_SSIZE_MAX 1 #define HAVE_DECL_STRERROR_R 1 #define HAVE_DECL_UINT32_MAX 1 #define HAVE_DECL_UINT64_MAX 1 #define HAVE_DECL_UINTMAX_MAX 1 #define HAVE_DIRENT_H 1 #define HAVE_DLFCN_H 1 #define HAVE_D_MD_ORDER 1 #define HAVE_EFTYPE 1 #define HAVE_EILSEQ 1 #define HAVE_ERRNO_H 1 #define HAVE_FCHDIR 1 #define HAVE_FCHFLAGS 1 #define HAVE_FCHMOD 1 #define HAVE_FCHOWN 1 #define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FDOPENDIR 1 #define HAVE_FORK 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 #define HAVE_FSTATAT 1 #define HAVE_FSTATFS 1 #define HAVE_FSTATVFS 1 #define HAVE_FTRUNCATE 1 #define HAVE_FUTIMES 1 #define HAVE_FUTIMESAT 1 #define HAVE_GETEUID 1 #define HAVE_GETGRGID_R 1 #define HAVE_GETGRNAM_R 1 #define HAVE_GETPID 1 #define HAVE_GETPWNAM_R 1 #define HAVE_GETPWUID_R 1 #define HAVE_GETVFSBYNAME 1 #define HAVE_GMTIME_R 1 #define HAVE_GRP_H 1 #define HAVE_INTMAX_T 1 #define HAVE_INTTYPES_H 1 #define HAVE_LANGINFO_H 1 #define HAVE_LCHFLAGS 1 #define HAVE_LCHMOD 1 #define HAVE_LCHOWN 1 #define HAVE_LIBZ 1 #define HAVE_LIMITS_H 1 #define HAVE_LINK 1 #define HAVE_LOCALE_H 1 #define HAVE_LOCALTIME_R 1 #define HAVE_LONG_LONG_INT 1 #define HAVE_LSTAT 1 #define HAVE_LUTIMES 1 #define HAVE_MBRTOWC 1 #define HAVE_MEMMOVE 1 #define HAVE_MEMORY_H 1 #define HAVE_MEMSET 1 #define HAVE_MKDIR 1 #define HAVE_MKFIFO 1 #define HAVE_MKNOD 1 #define HAVE_MKSTEMP 1 #define HAVE_NL_LANGINFO 1 #define HAVE_OPENAT 1 #define HAVE_PATHS_H 1 #define HAVE_PIPE 1 #define HAVE_POLL 1 #define HAVE_POLL_H 1 #define HAVE_POSIX_SPAWNP 1 #define HAVE_PTHREAD_H 1 #define HAVE_PWD_H 1 #define HAVE_READDIR_R 1 #define HAVE_READLINK 1 #define HAVE_READLINKAT 1 #define HAVE_READPASSPHRASE 1 #define HAVE_READPASSPHRASE_H 1 #define HAVE_REGEX_H 1 #define HAVE_SELECT 1 #define HAVE_SETENV 1 #define HAVE_SETLOCALE 1 #define HAVE_SIGACTION 1 #define HAVE_SIGNAL_H 1 #define HAVE_SPAWN_H 1 #define HAVE_STATFS 1 #define HAVE_STATVFS 1 #define HAVE_STDARG_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRCHR 1 #define HAVE_STRDUP 1 #define HAVE_STRERROR 1 #define HAVE_STRERROR_R 1 #define HAVE_STRFTIME 1 #define HAVE_STRINGS_H 1 #define HAVE_STRING_H 1 #define HAVE_STRRCHR 1 #define HAVE_STRUCT_STATFS_F_NAMEMAX 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 #define HAVE_STRUCT_STAT_ST_FLAGS 1 #define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 #define HAVE_STRUCT_TM_TM_GMTOFF 1 #define HAVE_SYMLINK 1 #define HAVE_SYS_CDEFS_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_POLL_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STATVFS_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TIME_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_SYS_UTSNAME_H 1 #define HAVE_SYS_WAIT_H 1 #define HAVE_TIMEGM 1 #define HAVE_TIME_H 1 #define HAVE_TZSET 1 #define HAVE_UINTMAX_T 1 #define HAVE_UNISTD_H 1 +#define HAVE_UNLINKAT 1 #define HAVE_UNSETENV 1 #define HAVE_UNSIGNED_LONG_LONG 1 #define HAVE_UNSIGNED_LONG_LONG_INT 1 #define HAVE_UTIME 1 #define HAVE_UTIMES 1 #define HAVE_UTIME_H 1 #define HAVE_VFORK 1 #define HAVE_VPRINTF 1 #define HAVE_WCHAR_H 1 #define HAVE_WCHAR_T 1 #define HAVE_WCRTOMB 1 #define HAVE_WCSCMP 1 #define HAVE_WCSCPY 1 #define HAVE_WCSLEN 1 #define HAVE_WCTOMB 1 #define HAVE_WCTYPE_H 1 #define HAVE_WMEMCMP 1 #define HAVE_WMEMCPY 1 #define HAVE_WMEMMOVE 1 #define HAVE_ZLIB_H 1 #define TIME_WITH_SYS_TIME 1 /* FreeBSD 4 and earlier lack intmax_t/uintmax_t */ #if __FreeBSD__ < 5 #define intmax_t int64_t #define uintmax_t uint64_t #endif /* FreeBSD defines for archive_hash.h */ #ifdef WITH_OPENSSL #define ARCHIVE_CRYPTO_MD5_OPENSSL 1 #define ARCHIVE_CRYPTO_RMD160_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA1_OPENSSL #define ARCHIVE_CRYPTO_SHA256_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA384_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA512_OPENSSL 1 #else #define ARCHIVE_CRYPTO_MD5_LIBMD 1 #define ARCHIVE_CRYPTO_SHA1_LIBMD 1 #define ARCHIVE_CRYPTO_SHA256_LIBMD 1 #define ARCHIVE_CRYPTO_SHA512_LIBMD 1 #endif Index: stable/10/lib/libarchive/tests/Makefile =================================================================== --- stable/10/lib/libarchive/tests/Makefile (revision 349524) +++ stable/10/lib/libarchive/tests/Makefile (revision 349525) @@ -1,628 +1,630 @@ # $FreeBSD$ SRCTOP= ${.CURDIR:H:H:H} TESTSDIR= ${TESTSBASE}/lib/libarchive LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test BINDIR= ${TESTSDIR} PROGS+= libarchive_test CFLAGS+= -I${.CURDIR} -I${.CURDIR:H} -I${.OBJDIR} CFLAGS+= -I${LIBARCHIVEDIR}/libarchive -I${LIBARCHIVEDIR}/libarchive/test CFLAGS+= -I${LIBARCHIVEDIR}/test_utils CFLAGS+= -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC .PATH: ${LIBARCHIVEDIR}/libarchive/test TESTS_SRCS= \ test_acl_nfs4.c \ test_acl_pax.c \ test_acl_platform_nfs4.c \ test_acl_platform_posix1e.c \ test_acl_posix1e.c \ test_acl_text.c \ test_archive_api_feature.c \ test_archive_clear_error.c \ test_archive_cmdline.c \ test_archive_digest.c \ test_archive_getdate.c \ test_archive_match_time.c \ test_archive_match_owner.c \ test_archive_match_path.c \ test_archive_pathmatch.c \ test_archive_read_add_passphrase.c \ test_archive_read_close_twice.c \ test_archive_read_close_twice_open_fd.c \ test_archive_read_close_twice_open_filename.c \ test_archive_read_multiple_data_objects.c \ test_archive_read_next_header_empty.c \ test_archive_read_next_header_raw.c \ test_archive_read_open2.c \ test_archive_read_set_filter_option.c \ test_archive_read_set_format_option.c \ test_archive_read_set_option.c \ test_archive_read_set_options.c \ test_archive_read_support.c \ test_archive_set_error.c \ test_archive_string.c \ test_archive_string_conversion.c \ test_archive_write_add_filter_by_name.c \ test_archive_write_set_filter_option.c \ test_archive_write_set_format_by_name.c \ test_archive_write_set_format_filter_by_ext.c \ test_archive_write_set_format_option.c \ test_archive_write_set_option.c \ test_archive_write_set_options.c \ test_archive_write_set_passphrase.c \ test_bad_fd.c \ test_compat_bzip2.c \ test_compat_cpio.c \ test_compat_gtar.c \ test_compat_gzip.c \ test_compat_lz4.c \ test_compat_lzip.c \ test_compat_lzma.c \ test_compat_lzop.c \ test_compat_mac.c \ test_compat_pax_libarchive_2x.c \ test_compat_perl_archive_tar.c \ test_compat_plexus_archiver_tar.c \ test_compat_solaris_tar_acl.c \ test_compat_solaris_pax_sparse.c \ test_compat_star_acl.c \ test_compat_tar_hardlink.c \ test_compat_uudecode.c \ test_compat_uudecode_large.c \ test_compat_xz.c \ test_compat_zip.c \ test_compat_zstd.c \ test_empty_write.c \ test_entry.c \ test_entry_strmode.c \ test_extattr_freebsd.c \ test_filter_count.c \ test_fuzz.c \ test_gnutar_filename_encoding.c \ test_link_resolver.c \ test_open_fd.c \ test_open_failure.c \ test_open_file.c \ test_open_filename.c \ test_pax_filename_encoding.c \ test_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_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_mtree.c \ test_read_format_mtree_crash747.c \ test_read_format_pax_bz2.c \ test_read_format_rar.c \ test_read_format_rar5.c \ test_read_format_rar_encryption_data.c \ test_read_format_rar_encryption_header.c \ test_read_format_rar_encryption_partially.c \ test_read_format_rar_invalid1.c \ test_read_format_raw.c \ test_read_format_tar.c \ test_read_format_tar_concatenated.c \ test_read_format_tar_empty_filename.c \ test_read_format_tar_empty_pax.c \ test_read_format_tar_empty_with_gnulabel.c \ test_read_format_tar_filename.c \ test_read_format_tbz.c \ test_read_format_tgz.c \ test_read_format_tlz.c \ test_read_format_txz.c \ test_read_format_tz.c \ test_read_format_ustar_filename.c \ test_read_format_warc.c \ test_read_format_xar.c \ test_read_format_zip.c \ test_read_format_zip_7075_utf8_paths.c \ test_read_format_zip_comment_stored.c \ test_read_format_zip_encryption_data.c \ test_read_format_zip_encryption_header.c \ test_read_format_zip_encryption_partially.c \ test_read_format_zip_extra_padding.c \ test_read_format_zip_filename.c \ test_read_format_zip_high_compression.c \ test_read_format_zip_jar.c \ test_read_format_zip_mac_metadata.c \ test_read_format_zip_malformed.c \ test_read_format_zip_msdos.c \ test_read_format_zip_nested.c \ test_read_format_zip_nofiletype.c \ test_read_format_zip_padded.c \ test_read_format_zip_sfx.c \ test_read_format_zip_traditional_encryption_data.c \ test_read_format_zip_winzip_aes.c \ test_read_format_zip_winzip_aes_large.c \ test_read_format_zip_with_invalid_traditional_eocd.c \ test_read_format_zip_zip64.c \ test_read_large.c \ test_read_pax_truncated.c \ test_read_position.c \ test_read_set_format.c \ test_read_too_many_filters.c \ test_read_truncated.c \ test_read_truncated_filter.c \ test_sparse_basic.c \ test_tar_filenames.c \ test_tar_large.c \ test_warn_missing_hardlink_target.c \ test_ustar_filenames.c \ test_ustar_filename_encoding.c \ test_write_disk.c \ test_write_disk_appledouble.c \ test_write_disk_failures.c \ test_write_disk_hardlink.c \ test_write_disk_hfs_compression.c \ test_write_disk_lookup.c \ test_write_disk_mac_metadata.c \ test_write_disk_no_hfs_compression.c \ test_write_disk_perms.c \ test_write_disk_secure.c \ test_write_disk_secure744.c \ test_write_disk_secure745.c \ test_write_disk_secure746.c \ test_write_disk_sparse.c \ test_write_disk_symlink.c \ test_write_disk_times.c \ test_write_filter_b64encode.c \ test_write_filter_bzip2.c \ test_write_filter_compress.c \ test_write_filter_gzip.c \ test_write_filter_gzip_timestamp.c \ test_write_filter_lrzip.c \ test_write_filter_lz4.c \ test_write_filter_lzip.c \ test_write_filter_lzma.c \ test_write_filter_lzop.c \ test_write_filter_program.c \ test_write_filter_uuencode.c \ test_write_filter_xz.c \ test_write_filter_zstd.c \ test_write_format_7zip.c \ test_write_format_7zip_empty.c \ test_write_format_7zip_large.c \ test_write_format_ar.c \ test_write_format_cpio.c \ test_write_format_cpio_empty.c \ test_write_format_cpio_newc.c \ test_write_format_cpio_odc.c \ test_write_format_gnutar.c \ test_write_format_gnutar_filenames.c \ test_write_format_iso9660.c \ test_write_format_iso9660_boot.c \ test_write_format_iso9660_empty.c \ test_write_format_iso9660_filename.c \ test_write_format_iso9660_zisofs.c \ test_write_format_mtree.c \ test_write_format_mtree_absolute_path.c \ test_write_format_mtree_classic.c \ test_write_format_mtree_classic_indent.c \ test_write_format_mtree_fflags.c \ test_write_format_mtree_no_separator.c \ test_write_format_mtree_quoted_filename.c \ test_write_format_pax.c \ test_write_format_raw.c \ test_write_format_raw_b64.c \ test_write_format_shar_empty.c \ test_write_format_tar.c \ test_write_format_tar_empty.c \ test_write_format_tar_sparse.c \ test_write_format_tar_ustar.c \ test_write_format_tar_v7tar.c \ test_write_format_warc.c \ test_write_format_warc_empty.c \ test_write_format_xar.c \ test_write_format_xar_empty.c \ test_write_format_zip.c \ test_write_format_zip_compression_store.c \ test_write_format_zip_empty.c \ test_write_format_zip_empty_zip64.c \ test_write_format_zip_file.c \ test_write_format_zip_file_zip64.c \ test_write_format_zip_large.c \ test_write_format_zip_zip64.c \ test_write_open_memory.c \ test_write_read_format_zip.c \ test_xattr_platform.c \ test_zip_filename_encoding.c # Deterministic failures: # Crashes with SIGBUS BROKEN_TESTS+= test_archive_rmd160 # Fails with `libarchive/test/test_archive_crypto.c:121: md != actualmd` BROKEN_TESTS+= test_archive_sha384 # Fails with `test_compat_pax_libarchive_2x.c:122: ARCHIVE_WARN != archive_read_next_header(a, &ae)` BROKEN_TESTS+= test_compat_pax_libarchive_2x # Fails with `test_read_disk_directory_traversals.c:1094: File at has atime 886622, 1443306049 seconds ago` BROKEN_TESTS+= test_read_disk_directory_traversals # Non-deterministic failures: # (Times out?) [and] crashes BROKEN_TESTS+= test_fuzz_rar # Build the test program. SRCS.libarchive_test= \ ${TESTS_SRCS} \ read_open_memory.c \ list.h DPADD.libarchive_test= ${LIBARCHIVE} LDADD.libarchive_test= -larchive .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} CLEANFILES+= list.h list.h.tmp FILES+= README FILES+= test_acl_pax_posix1e.tar.uu FILES+= test_acl_pax_nfs4.tar.uu FILES+= test_archive_string_conversion.txt.Z.uu FILES+= test_compat_bzip2_1.tbz.uu FILES+= test_compat_bzip2_2.tbz.uu FILES+= test_compat_cpio_1.cpio.uu FILES+= test_compat_gtar_1.tar.uu FILES+= test_compat_gtar_2.tar.uu FILES+= test_compat_gzip_1.tgz.uu FILES+= test_compat_gzip_2.tgz.uu FILES+= test_compat_lz4_1.tar.lz4.uu FILES+= test_compat_lz4_2.tar.lz4.uu FILES+= test_compat_lz4_3.tar.lz4.uu FILES+= test_compat_lz4_B4.tar.lz4.uu FILES+= test_compat_lz4_B4BD.tar.lz4.uu FILES+= test_compat_lz4_B4BDBX.tar.lz4.uu FILES+= test_compat_lz4_B5.tar.lz4.uu FILES+= test_compat_lz4_B5BD.tar.lz4.uu FILES+= test_compat_lz4_B6.tar.lz4.uu FILES+= test_compat_lz4_B6BD.tar.lz4.uu FILES+= test_compat_lz4_B7.tar.lz4.uu FILES+= test_compat_lz4_B7BD.tar.lz4.uu FILES+= test_compat_lzip_1.tlz.uu FILES+= test_compat_lzip_2.tlz.uu FILES+= test_compat_lzma_1.tlz.uu FILES+= test_compat_lzma_2.tlz.uu FILES+= test_compat_lzma_3.tlz.uu FILES+= test_compat_lzop_1.tar.lzo.uu FILES+= test_compat_lzop_2.tar.lzo.uu FILES+= test_compat_lzop_3.tar.lzo.uu FILES+= test_compat_mac-1.tar.Z.uu FILES+= test_compat_mac-2.tar.Z.uu FILES+= test_compat_perl_archive_tar.tar.uu FILES+= test_compat_plexus_archiver_tar.tar.uu FILES+= test_compat_solaris_pax_sparse_1.pax.Z.uu FILES+= test_compat_solaris_pax_sparse_2.pax.Z.uu FILES+= test_compat_solaris_tar_acl.tar.uu FILES+= test_compat_star_acl_nfs4.tar.uu FILES+= test_compat_star_acl_posix1e.tar.uu FILES+= test_compat_tar_hardlink_1.tar.uu FILES+= test_compat_uudecode_large.tar.Z.uu FILES+= test_compat_xz_1.txz.uu FILES+= test_compat_zip_1.zip.uu FILES+= test_compat_zip_2.zip.uu FILES+= test_compat_zip_3.zip.uu FILES+= test_compat_zip_4.zip.uu FILES+= test_compat_zip_5.zip.uu FILES+= test_compat_zip_6.zip.uu FILES+= test_compat_zip_7.xps.uu FILES+= test_compat_zip_8.zip.uu FILES+= test_compat_zstd_1.tar.zst.uu FILES+= test_fuzz.cab.uu FILES+= test_fuzz.lzh.uu FILES+= test_fuzz_1.iso.Z.uu FILES+= test_pax_filename_encoding.tar.uu FILES+= test_rar_multivolume_multiple_files.part1.rar.uu FILES+= test_rar_multivolume_multiple_files.part2.rar.uu FILES+= test_rar_multivolume_multiple_files.part3.rar.uu FILES+= test_rar_multivolume_multiple_files.part4.rar.uu FILES+= test_rar_multivolume_multiple_files.part5.rar.uu FILES+= test_rar_multivolume_multiple_files.part6.rar.uu FILES+= test_rar_multivolume_single_file.part1.rar.uu FILES+= test_rar_multivolume_single_file.part2.rar.uu FILES+= test_rar_multivolume_single_file.part3.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part01.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part02.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part03.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part04.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part05.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part06.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part07.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part08.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part09.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part10.rar.uu FILES+= test_read_filter_grzip.tar.grz.uu FILES+= test_read_filter_lrzip.tar.lrz.uu FILES+= test_read_filter_lzop.tar.lzo.uu FILES+= test_read_filter_lzop_multiple_parts.tar.lzo.uu FILES+= test_read_format_7zip_bcj2_bzip2.7z.uu FILES+= test_read_format_7zip_bcj2_copy_1.7z.uu FILES+= test_read_format_7zip_bcj2_copy_2.7z.uu FILES+= test_read_format_7zip_bcj2_copy_lzma.7z.uu FILES+= test_read_format_7zip_bcj2_deflate.7z.uu FILES+= test_read_format_7zip_bcj2_lzma1_1.7z.uu FILES+= test_read_format_7zip_bcj2_lzma1_2.7z.uu FILES+= test_read_format_7zip_bcj2_lzma2_1.7z.uu FILES+= test_read_format_7zip_bcj2_lzma2_2.7z.uu FILES+= test_read_format_7zip_bcj_bzip2.7z.uu FILES+= test_read_format_7zip_bcj_copy.7z.uu FILES+= test_read_format_7zip_bcj_deflate.7z.uu FILES+= test_read_format_7zip_bcj_lzma1.7z.uu FILES+= test_read_format_7zip_bcj_lzma2.7z.uu FILES+= test_read_format_7zip_bzip2.7z.uu FILES+= test_read_format_7zip_copy.7z.uu FILES+= test_read_format_7zip_copy_2.7z.uu FILES+= test_read_format_7zip_deflate.7z.uu FILES+= test_read_format_7zip_delta_lzma1.7z.uu FILES+= test_read_format_7zip_delta_lzma2.7z.uu FILES+= test_read_format_7zip_empty_archive.7z.uu FILES+= test_read_format_7zip_empty_file.7z.uu FILES+= test_read_format_7zip_encryption.7z.uu FILES+= test_read_format_7zip_encryption_header.7z.uu FILES+= test_read_format_7zip_encryption_partially.7z.uu FILES+= test_read_format_7zip_lzma1.7z.uu FILES+= test_read_format_7zip_lzma1_2.7z.uu FILES+= test_read_format_7zip_lzma1_lzma2.7z.uu FILES+= test_read_format_7zip_lzma2.7z.uu FILES+= test_read_format_7zip_malformed.7z.uu FILES+= test_read_format_7zip_malformed2.7z.uu FILES+= test_read_format_7zip_ppmd.7z.uu FILES+= test_read_format_7zip_symbolic_name.7z.uu FILES+= test_read_format_ar.ar.uu FILES+= test_read_format_cab_1.cab.uu FILES+= test_read_format_cab_2.cab.uu FILES+= test_read_format_cab_3.cab.uu FILES+= test_read_format_cab_filename_cp932.cab.uu FILES+= test_read_format_cpio_bin_be.cpio.uu FILES+= test_read_format_cpio_bin_le.cpio.uu FILES+= test_read_format_cpio_filename_cp866.cpio.uu FILES+= test_read_format_cpio_filename_eucjp.cpio.uu FILES+= test_read_format_cpio_filename_koi8r.cpio.uu FILES+= test_read_format_cpio_filename_utf8_jp.cpio.uu FILES+= test_read_format_cpio_filename_utf8_ru.cpio.uu FILES+= test_read_format_cpio_svr4_bzip2_rpm.rpm.uu FILES+= test_read_format_cpio_svr4_gzip_rpm.rpm.uu FILES+= test_read_format_gtar_filename_cp866.tar.Z.uu FILES+= test_read_format_gtar_filename_eucjp.tar.Z.uu FILES+= test_read_format_gtar_filename_koi8r.tar.Z.uu FILES+= test_read_format_gtar_sparse_1_13.tar.uu FILES+= test_read_format_gtar_sparse_1_17.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix00.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix01.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix10.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu FILES+= test_read_format_gtar_sparse_skip_entry.tar.Z.uu FILES+= test_read_format_iso.iso.Z.uu FILES+= test_read_format_iso_2.iso.Z.uu FILES+= test_read_format_iso_joliet.iso.Z.uu FILES+= test_read_format_iso_joliet_by_nero.iso.Z.uu FILES+= test_read_format_iso_joliet_long.iso.Z.uu FILES+= test_read_format_iso_joliet_rockridge.iso.Z.uu FILES+= test_read_format_iso_multi_extent.iso.Z.uu FILES+= test_read_format_iso_rockridge.iso.Z.uu FILES+= test_read_format_iso_rockridge_ce.iso.Z.uu FILES+= test_read_format_iso_rockridge_new.iso.Z.uu FILES+= test_read_format_iso_rockridge_rr_moved.iso.Z.uu FILES+= test_read_format_iso_xorriso.iso.Z.uu FILES+= test_read_format_iso_zisofs.iso.Z.uu FILES+= test_read_format_lha_bugfix_0.lzh.uu FILES+= test_read_format_lha_filename_cp932.lzh.uu FILES+= test_read_format_lha_header0.lzh.uu FILES+= test_read_format_lha_header1.lzh.uu FILES+= test_read_format_lha_header2.lzh.uu FILES+= test_read_format_lha_header3.lzh.uu FILES+= test_read_format_lha_lh0.lzh.uu FILES+= test_read_format_lha_lh6.lzh.uu FILES+= test_read_format_lha_lh7.lzh.uu FILES+= test_read_format_lha_withjunk.lzh.uu FILES+= test_read_format_mtree.mtree.uu FILES+= test_read_format_mtree_crash747.mtree.bz2.uu FILES+= test_read_format_mtree_nomagic.mtree.uu FILES+= test_read_format_mtree_nomagic2.mtree.uu FILES+= test_read_format_mtree_nomagic3.mtree.uu FILES+= test_read_format_mtree_noprint.mtree.uu FILES+= test_read_format_rar.rar.uu FILES+= test_read_format_rar_binary_data.rar.uu FILES+= test_read_format_rar_compress_best.rar.uu FILES+= test_read_format_rar_compress_normal.rar.uu FILES+= test_read_format_rar_encryption_data.rar.uu FILES+= test_read_format_rar_encryption_header.rar.uu FILES+= test_read_format_rar_encryption_partially.rar.uu FILES+= test_read_format_rar_invalid1.rar.uu FILES+= test_read_format_rar_multi_lzss_blocks.rar.uu FILES+= test_read_format_rar_multivolume.part0001.rar.uu FILES+= test_read_format_rar_multivolume.part0002.rar.uu FILES+= test_read_format_rar_multivolume.part0003.rar.uu FILES+= test_read_format_rar_multivolume.part0004.rar.uu FILES+= test_read_format_rar_noeof.rar.uu FILES+= test_read_format_rar_ppmd_lzss_conversion.rar.uu FILES+= test_read_format_rar_ppmd_use_after_free.rar.uu +FILES+= test_read_format_rar_ppmd_use_after_free2.rar.uu FILES+= test_read_format_rar_sfx.exe.uu FILES+= test_read_format_rar_subblock.rar.uu FILES+= test_read_format_rar_unicode.rar.uu FILES+= test_read_format_rar_windows.rar.uu FILES+= test_read_format_rar5_arm.rar.uu FILES+= test_read_format_rar5_blake2.rar.uu FILES+= test_read_format_rar5_compressed.rar.uu +FILES+= test_read_format_rar5_different_window_size.rar.uu FILES+= test_read_format_rar5_distance_overflow.rar.uu FILES+= test_read_format_rar5_extra_field_version.rar.uu FILES+= test_read_format_rar5_fileattr.rar.uu FILES+= test_read_format_rar5_hardlink.rar.uu FILES+= test_read_format_rar5_invalid_dict_reference.rar.uu FILES+= test_read_format_rar5_leftshift1.rar.uu FILES+= test_read_format_rar5_leftshift2.rar.uu FILES+= test_read_format_rar5_multiarchive.part01.rar.uu FILES+= test_read_format_rar5_multiarchive.part02.rar.uu FILES+= test_read_format_rar5_multiarchive.part03.rar.uu FILES+= test_read_format_rar5_multiarchive.part04.rar.uu FILES+= test_read_format_rar5_multiarchive.part05.rar.uu FILES+= test_read_format_rar5_multiarchive.part06.rar.uu FILES+= test_read_format_rar5_multiarchive.part07.rar.uu FILES+= test_read_format_rar5_multiarchive.part08.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part01.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part02.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part03.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part04.rar.uu FILES+= test_read_format_rar5_multiple_files.rar.uu FILES+= test_read_format_rar5_multiple_files_solid.rar.uu FILES+= test_read_format_rar5_nonempty_dir_stream.rar.uu FILES+= test_read_format_rar5_owner.rar.uu FILES+= test_read_format_rar5_readtables_overflow.rar.uu FILES+= test_read_format_rar5_solid.rar.uu FILES+= test_read_format_rar5_stored.rar.uu FILES+= test_read_format_rar5_stored_manyfiles.rar.uu FILES+= test_read_format_rar5_symlink.rar.uu FILES+= test_read_format_rar5_truncated_huff.rar.uu FILES+= test_read_format_rar5_win32.rar.uu FILES+= test_read_format_raw.bufr.uu FILES+= test_read_format_raw.data.Z.uu FILES+= test_read_format_raw.data.gz.uu FILES+= test_read_format_raw.data.uu FILES+= test_read_format_tar_concatenated.tar.uu FILES+= test_read_format_tar_empty_filename.tar.uu FILES+= test_read_format_tar_empty_pax.tar.Z.uu FILES+= test_read_format_tar_empty_with_gnulabel.tar.uu FILES+= test_read_format_tar_filename_koi8r.tar.Z.uu FILES+= test_read_format_ustar_filename_cp866.tar.Z.uu FILES+= test_read_format_ustar_filename_eucjp.tar.Z.uu FILES+= test_read_format_ustar_filename_koi8r.tar.Z.uu FILES+= test_read_format_warc.warc.uu FILES+= test_read_format_zip.zip.uu FILES+= test_read_format_zip_7075_utf8_paths.zip.uu FILES+= test_read_format_zip_bz2_hang.zip.uu FILES+= test_read_format_zip_bzip2.zipx.uu FILES+= test_read_format_zip_bzip2_multi.zipx.uu FILES+= test_read_format_zip_comment_stored_1.zip.uu FILES+= test_read_format_zip_comment_stored_2.zip.uu FILES+= test_read_format_zip_encryption_data.zip.uu FILES+= test_read_format_zip_encryption_header.zip.uu FILES+= test_read_format_zip_encryption_partially.zip.uu FILES+= test_read_format_zip_extra_padding.zip.uu FILES+= test_read_format_zip_filename_cp866.zip.uu FILES+= test_read_format_zip_filename_cp932.zip.uu FILES+= test_read_format_zip_filename_koi8r.zip.uu FILES+= test_read_format_zip_filename_utf8_jp.zip.uu FILES+= test_read_format_zip_filename_utf8_ru.zip.uu FILES+= test_read_format_zip_filename_utf8_ru2.zip.uu FILES+= test_read_format_zip_high_compression.zip.uu FILES+= test_read_format_zip_jar.jar.uu FILES+= test_read_format_zip_length_at_end.zip.uu FILES+= test_read_format_zip_lzma_alone_leak.zipx.uu FILES+= test_read_format_zip_lzma.zipx.uu FILES+= test_read_format_zip_lzma_multi.zipx.uu FILES+= test_read_format_zip_mac_metadata.zip.uu FILES+= test_read_format_zip_malformed1.zip.uu FILES+= test_read_format_zip_msdos.zip.uu FILES+= test_read_format_zip_nested.zip.uu FILES+= test_read_format_zip_nofiletype.zip.uu FILES+= test_read_format_zip_padded1.zip.uu FILES+= test_read_format_zip_padded2.zip.uu FILES+= test_read_format_zip_padded3.zip.uu FILES+= test_read_format_zip_ppmd8.zipx.uu FILES+= test_read_format_zip_ppmd8_crash_1.zipx.uu FILES+= test_read_format_zip_ppmd8_crash_2.zipx.uu FILES+= test_read_format_zip_ppmd8_multi.zipx.uu FILES+= test_read_format_zip_sfx.uu FILES+= test_read_format_zip_symlink.zip.uu FILES+= test_read_format_zip_traditional_encryption_data.zip.uu FILES+= test_read_format_zip_ux.zip.uu FILES+= test_read_format_zip_with_invalid_traditional_eocd.zip.uu FILES+= test_read_format_zip_winzip_aes128.zip.uu FILES+= test_read_format_zip_winzip_aes256.zip.uu FILES+= test_read_format_zip_winzip_aes256_large.zip.uu FILES+= test_read_format_zip_winzip_aes256_stored.zip.uu FILES+= test_read_format_zip_xz_multi.zipx.uu FILES+= test_read_format_zip_zip64a.zip.uu FILES+= test_read_format_zip_zip64b.zip.uu FILES+= test_read_large_splitted_rar_aa.uu FILES+= test_read_large_splitted_rar_ab.uu FILES+= test_read_large_splitted_rar_ac.uu FILES+= test_read_large_splitted_rar_ad.uu FILES+= test_read_large_splitted_rar_ae.uu FILES+= test_read_pax_schily_xattr.tar.uu FILES+= test_read_splitted_rar_aa.uu FILES+= test_read_splitted_rar_ab.uu FILES+= test_read_splitted_rar_ac.uu FILES+= test_read_splitted_rar_ad.uu FILES+= test_read_too_many_filters.gz.uu FILES+= test_splitted_rar_seek_support_aa.uu FILES+= test_splitted_rar_seek_support_ab.uu FILES+= test_splitted_rar_seek_support_ac.uu FILES+= test_write_disk_appledouble.cpio.gz.uu FILES+= test_write_disk_hfs_compression.tgz.uu FILES+= test_write_disk_mac_metadata.tar.gz.uu FILES+= test_write_disk_no_hfs_compression.tgz.uu .include Index: stable/10/usr.bin/bsdcat/Makefile =================================================================== --- stable/10/usr.bin/bsdcat/Makefile (revision 349524) +++ stable/10/usr.bin/bsdcat/Makefile (revision 349525) @@ -1,39 +1,39 @@ # $FreeBSD$ .include _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive _LIBARCHIVECONFDIR= ${SRCTOP}/lib/libarchive PROG= bsdcat -BSDCAT_VERSION_STRING= 3.3.3 +BSDCAT_VERSION_STRING= 3.4.0 .PATH: ${_LIBARCHIVEDIR}/cat SRCS= bsdcat.c cmdline.c .PATH: ${_LIBARCHIVEDIR}/libarchive_fe SRCS+= err.c CFLAGS+= -DBSDCAT_VERSION_STRING=\"${BSDCAT_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\" CFLAGS+= -I${_LIBARCHIVEDIR}/cat -I${_LIBARCHIVEDIR}/libarchive_fe DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} ${LIBLZMA} ${LIBPTHREAD} ${LIBBSDXML} LDADD= -larchive -lbz2 -lz -llzma -lpthread -lbsdxml .if ${MK_OPENSSL} != "no" DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .else DPADD+= ${LIBMD} LDADD+= -lmd .endif .if ${MK_ICONV} != "no" CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST=const .endif .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/10/usr.bin/cpio/Makefile =================================================================== --- stable/10/usr.bin/cpio/Makefile (revision 349524) +++ stable/10/usr.bin/cpio/Makefile (revision 349525) @@ -1,47 +1,47 @@ # $FreeBSD$ .include LIBARCHIVEDIR= ${.CURDIR}/../../contrib/libarchive LIBARCHIVECONFDIR= ${.CURDIR}/../../lib/libarchive PROG= bsdcpio -BSDCPIO_VERSION_STRING= 3.3.3 +BSDCPIO_VERSION_STRING= 3.4.0 .PATH: ${LIBARCHIVEDIR}/cpio SRCS= cpio.c cmdline.c .PATH: ${LIBARCHIVEDIR}/libarchive_fe SRCS+= err.c line_reader.c passphrase.c CFLAGS+= -DBSDCPIO_VERSION_STRING=\"${BSDCPIO_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"${LIBARCHIVECONFDIR}/config_freebsd.h\" CFLAGS+= -I${LIBARCHIVEDIR}/cpio -I${LIBARCHIVEDIR}/libarchive_fe .ifdef RELEASE_CRUNCH # FreeBSD's installer uses cpio in crunched binaries that are # statically linked, cannot use -lcrypto, and are size sensitive. CFLAGS+= -DSMALLER .endif DPADD= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2} ${LIBLZMA} ${LIBPTHREAD} ${LIBBSDXML} LDADD= -larchive -lz -lbz2 -llzma -lpthread -lbsdxml .if ${MK_OPENSSL} != "no" DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .else DPADD+= ${LIBMD} LDADD+= -lmd .endif .if ${MK_ICONV} != "no" CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST=const .endif SYMLINKS=bsdcpio ${BINDIR}/cpio MLINKS= bsdcpio.1 cpio.1 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/10/usr.bin/tar/Makefile =================================================================== --- stable/10/usr.bin/tar/Makefile (revision 349524) +++ stable/10/usr.bin/tar/Makefile (revision 349525) @@ -1,48 +1,48 @@ # $FreeBSD$ .include LIBARCHIVEDIR= ${.CURDIR}/../../contrib/libarchive PROG= bsdtar -BSDTAR_VERSION_STRING= 3.3.3 +BSDTAR_VERSION_STRING= 3.4.0 .PATH: ${LIBARCHIVEDIR}/tar SRCS= bsdtar.c \ cmdline.c \ creation_set.c \ read.c \ subst.c \ util.c \ write.c .PATH: ${LIBARCHIVEDIR}/libarchive_fe SRCS+= err.c \ line_reader.c \ passphrase.c DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} ${LIBLZMA} ${LIBPTHREAD} ${LIBBSDXML} LDADD= -larchive -lbz2 -lz -llzma -lpthread -lbsdxml .if ${MK_OPENSSL} != "no" DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .else DPADD+= ${LIBMD} LDADD+= -lmd .endif .if ${MK_ICONV} != "no" CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST=const .endif CFLAGS+= -DBSDTAR_VERSION_STRING=\"${BSDTAR_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"${.CURDIR}/../../lib/libarchive/config_freebsd.h\" CFLAGS+= -I${LIBARCHIVEDIR}/tar -I${LIBARCHIVEDIR}/libarchive CFLAGS+= -I${LIBARCHIVEDIR}/libarchive_fe SYMLINKS= bsdtar ${BINDIR}/tar MLINKS= bsdtar.1 tar.1 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/10 =================================================================== --- stable/10 (revision 349524) +++ stable/10 (revision 349525) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r348993,349135