Index: stable/10/contrib/libarchive/FREEBSD-Xlist =================================================================== --- stable/10/contrib/libarchive/FREEBSD-Xlist (revision 316337) +++ stable/10/contrib/libarchive/FREEBSD-Xlist (revision 316338) @@ -1,38 +1,41 @@ .git .gitattributes .gitignore .travis.yml CMakeLists.txt CONTRIBUTING.md CTestConfig.cmake INSTALL Makefile.am build cat/CMakeLists.txt cat/test/CMakeLists.txt configure.ac contrib cpio/CMakeLists.txt cpio/cpio_windows.c cpio/cpio_windows.h cpio/config_freebsd.h cpio/test/CMakeLists.txt doc examples libarchive/CMakeLists.txt libarchive/archive_entry_copy_bhfi.c +libarchive/archive_disk_acl_darwin.c +libarchive/archive_disk_acl_linux.c +libarchive/archive_disk_acl_sunos.c libarchive/archive_read_disk_windows.c libarchive/archive_windows.c libarchive/archive_windows.h libarchive/archive_write_disk_windows.c libarchive/config_freebsd.h libarchive/filter_fork_windows.c libarchive/mtree.5 libarchive/test/.cvsignore libarchive/test/CMakeLists.txt tar/CMakeLists.txt tar/bsdtar_windows.c tar/bsdtar_windows.h tar/config_freebsd.h tar/test/CMakeLists.txt tar/test/test_windows.c Index: stable/10/contrib/libarchive/NEWS =================================================================== --- stable/10/contrib/libarchive/NEWS (revision 316337) +++ stable/10/contrib/libarchive/NEWS (revision 316338) @@ -1,683 +1,685 @@ +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 detaults: + * 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/cpio/cpio.c =================================================================== --- stable/10/contrib/libarchive/cpio/cpio.c (revision 316337) +++ stable/10/contrib/libarchive/cpio/cpio.c (revision 316338) @@ -1,1471 +1,1469 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cpio_platform.h" __FBSDID("$FreeBSD$"); #include #include #include #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "cpio.h" #include "err.h" #include "line_reader.h" #include "passphrase.h" /* Fixed size of uname/gname caches. */ #define name_cache_size 101 #ifndef O_BINARY #define O_BINARY 0 #endif struct name_cache { int probes; int hits; size_t size; struct { id_t id; char *name; } cache[name_cache_size]; }; static int extract_data(struct archive *, struct archive *); const char * cpio_i64toa(int64_t); static const char *cpio_rename(const char *name); static int entry_to_archive(struct cpio *, struct archive_entry *); static int file_to_archive(struct cpio *, const char *); static void free_cache(struct name_cache *cache); static void list_item_verbose(struct cpio *, struct archive_entry *); static void long_help(void) __LA_DEAD; static const char *lookup_gname(struct cpio *, gid_t gid); static int lookup_gname_helper(struct cpio *, const char **name, id_t gid); static const char *lookup_uname(struct cpio *, uid_t uid); static int lookup_uname_helper(struct cpio *, const char **name, id_t uid); static void mode_in(struct cpio *) __LA_DEAD; static void mode_list(struct cpio *) __LA_DEAD; static void mode_out(struct cpio *); static void mode_pass(struct cpio *, const char *); static const char *remove_leading_slash(const char *); static int restore_time(struct cpio *, struct archive_entry *, const char *, int fd); static void usage(void) __LA_DEAD; static void version(void) __LA_DEAD; static const char * passphrase_callback(struct archive *, void *); static void passphrase_free(char *); int main(int argc, char *argv[]) { static char buff[16384]; struct cpio _cpio; /* Allocated on stack. */ struct cpio *cpio; const char *errmsg; int uid, gid; int opt; cpio = &_cpio; memset(cpio, 0, sizeof(*cpio)); cpio->buff = buff; cpio->buff_size = sizeof(buff); #if defined(HAVE_SIGACTION) && defined(SIGPIPE) { /* Ignore SIGPIPE signals. */ struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); } #endif /* Set lafe_progname before calling lafe_warnc. */ lafe_setprogname(*argv, "bsdcpio"); #if HAVE_SETLOCALE if (setlocale(LC_ALL, "") == NULL) lafe_warnc(0, "Failed to set default locale"); #endif cpio->uid_override = -1; cpio->gid_override = -1; cpio->argv = argv; cpio->argc = argc; cpio->mode = '\0'; cpio->verbose = 0; cpio->compress = '\0'; cpio->extract_flags = ARCHIVE_EXTRACT_NO_AUTODIR; cpio->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; cpio->extract_flags |= ARCHIVE_EXTRACT_PERM; cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; cpio->extract_flags |= ARCHIVE_EXTRACT_ACL; #if !defined(_WIN32) && !defined(__CYGWIN__) if (geteuid() == 0) cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; #endif cpio->bytes_per_block = 512; cpio->filename = NULL; cpio->matching = archive_match_new(); if (cpio->matching == NULL) lafe_errc(1, 0, "Out of memory"); while ((opt = cpio_getopt(cpio)) != -1) { switch (opt) { case '0': /* GNU convention: --null, -0 */ cpio->option_null = 1; break; case 'A': /* NetBSD/OpenBSD */ cpio->option_append = 1; break; case 'a': /* POSIX 1997 */ cpio->option_atime_restore = 1; break; case 'B': /* POSIX 1997 */ cpio->bytes_per_block = 5120; break; case OPTION_B64ENCODE: cpio->add_filter = opt; break; case 'C': /* NetBSD/OpenBSD */ cpio->bytes_per_block = atoi(cpio->argument); if (cpio->bytes_per_block <= 0) lafe_errc(1, 0, "Invalid blocksize %s", cpio->argument); break; case 'c': /* POSIX 1997 */ cpio->format = "odc"; break; case 'd': /* POSIX 1997 */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR; break; case 'E': /* NetBSD/OpenBSD */ if (archive_match_include_pattern_from_file( cpio->matching, cpio->argument, cpio->option_null) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); break; case 'F': /* NetBSD/OpenBSD/GNU cpio */ cpio->filename = cpio->argument; break; case 'f': /* POSIX 1997 */ if (archive_match_exclude_pattern(cpio->matching, cpio->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); break; case OPTION_GRZIP: cpio->compress = opt; break; case 'H': /* GNU cpio (also --format) */ cpio->format = cpio->argument; break; case 'h': long_help(); break; case 'I': /* NetBSD/OpenBSD */ cpio->filename = cpio->argument; break; case 'i': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -i and -%c", cpio->mode); cpio->mode = opt; break; case 'J': /* GNU tar, others */ cpio->compress = opt; break; case 'j': /* GNU tar, others */ cpio->compress = opt; break; case OPTION_INSECURE: cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; break; case 'L': /* GNU cpio */ cpio->option_follow_links = 1; break; case 'l': /* POSIX 1997 */ cpio->option_link = 1; break; case OPTION_LRZIP: case OPTION_LZ4: case OPTION_LZMA: /* GNU tar, others */ case OPTION_LZOP: /* GNU tar, others */ cpio->compress = opt; break; case 'm': /* POSIX 1997 */ cpio->extract_flags |= ARCHIVE_EXTRACT_TIME; break; case 'n': /* GNU cpio */ cpio->option_numeric_uid_gid = 1; break; case OPTION_NO_PRESERVE_OWNER: /* GNU cpio */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; break; case 'O': /* GNU cpio */ cpio->filename = cpio->argument; break; case 'o': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -o and -%c", cpio->mode); cpio->mode = opt; break; case 'p': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -p and -%c", cpio->mode); cpio->mode = opt; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; break; case OPTION_PASSPHRASE: cpio->passphrase = cpio->argument; break; case OPTION_PRESERVE_OWNER: cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; break; case OPTION_QUIET: /* GNU cpio */ cpio->quiet = 1; break; case 'R': /* GNU cpio, also --owner */ /* TODO: owner_parse should return uname/gname * also; use that to set [ug]name_override. */ errmsg = owner_parse(cpio->argument, &uid, &gid); if (errmsg) { lafe_warnc(-1, "%s", errmsg); usage(); } if (uid != -1) { cpio->uid_override = uid; cpio->uname_override = NULL; } if (gid != -1) { cpio->gid_override = gid; cpio->gname_override = NULL; } break; case 'r': /* POSIX 1997 */ cpio->option_rename = 1; break; case 't': /* POSIX 1997 */ cpio->option_list = 1; break; case 'u': /* POSIX 1997 */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; break; case OPTION_UUENCODE: cpio->add_filter = opt; break; case 'v': /* POSIX 1997 */ cpio->verbose++; break; case 'V': /* GNU cpio */ cpio->dot++; break; case OPTION_VERSION: /* GNU convention */ version(); break; #if 0 /* * cpio_getopt() handles -W specially, so it's not * available here. */ case 'W': /* Obscure, but useful GNU convention. */ break; #endif case 'y': /* tar convention */ cpio->compress = opt; break; case 'Z': /* tar convention */ cpio->compress = opt; break; case 'z': /* tar convention */ cpio->compress = opt; break; default: usage(); } } /* * Sanity-check args, error out on nonsensical combinations. */ /* -t implies -i if no mode was specified. */ if (cpio->option_list && cpio->mode == '\0') cpio->mode = 'i'; /* -t requires -i */ if (cpio->option_list && cpio->mode != 'i') lafe_errc(1, 0, "Option -t requires -i"); /* -n requires -it */ if (cpio->option_numeric_uid_gid && !cpio->option_list) lafe_errc(1, 0, "Option -n requires -it"); /* Can only specify format when writing */ if (cpio->format != NULL && cpio->mode != 'o') lafe_errc(1, 0, "Option --format requires -o"); /* -l requires -p */ if (cpio->option_link && cpio->mode != 'p') lafe_errc(1, 0, "Option -l requires -p"); /* -v overrides -V */ if (cpio->dot && cpio->verbose) cpio->dot = 0; /* TODO: Flag other nonsensical combinations. */ switch (cpio->mode) { case 'o': /* TODO: Implement old binary format in libarchive, use that here. */ if (cpio->format == NULL) cpio->format = "odc"; /* Default format */ mode_out(cpio); break; case 'i': while (*cpio->argv != NULL) { if (archive_match_include_pattern(cpio->matching, *cpio->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); --cpio->argc; ++cpio->argv; } if (cpio->option_list) mode_list(cpio); else mode_in(cpio); break; case 'p': if (*cpio->argv == NULL || **cpio->argv == '\0') lafe_errc(1, 0, "-p mode requires a target directory"); mode_pass(cpio, *cpio->argv); break; default: lafe_errc(1, 0, "Must specify at least one of -i, -o, or -p"); } archive_match_free(cpio->matching); free_cache(cpio->gname_cache); free_cache(cpio->uname_cache); free(cpio->destdir); passphrase_free(cpio->ppbuff); return (cpio->return_value); } static void usage(void) { const char *p; p = lafe_getprogname(); fprintf(stderr, "Brief Usage:\n"); fprintf(stderr, " List: %s -it < archive\n", p); fprintf(stderr, " Extract: %s -i < archive\n", p); fprintf(stderr, " Create: %s -o < filenames > archive\n", p); fprintf(stderr, " Help: %s --help\n", p); exit(1); } static const char *long_help_msg = "First option must be a mode specifier:\n" " -i Input -o Output -p Pass\n" "Common Options:\n" " -v Verbose filenames -V one dot per file\n" "Create: %p -o [options] < [list of files] > [archive]\n" " -J,-y,-z,--lzma Compress archive with xz/bzip2/gzip/lzma\n" " --format {odc|newc|ustar} Select archive format\n" "List: %p -it < [archive]\n" "Extract: %p -i [options] < [archive]\n"; /* * Note that the word 'bsdcpio' will always appear in the first line * of output. * * In particular, /bin/sh scripts that need to test for the presence * of bsdcpio can use the following template: * * if (cpio --help 2>&1 | grep bsdcpio >/dev/null 2>&1 ) then \ * echo bsdcpio; else echo not bsdcpio; fi */ static void long_help(void) { const char *prog; const char *p; prog = lafe_getprogname(); fflush(stderr); p = (strcmp(prog,"bsdcpio") != 0) ? "(bsdcpio)" : ""; printf("%s%s: manipulate archive files\n", prog, p); for (p = long_help_msg; *p != '\0'; p++) { if (*p == '%') { if (p[1] == 'p') { fputs(prog, stdout); p++; } else putchar('%'); } else putchar(*p); } version(); } static void version(void) { fprintf(stdout,"bsdcpio %s - %s\n", BSDCPIO_VERSION_STRING, archive_version_details()); exit(0); } static void mode_out(struct cpio *cpio) { struct archive_entry *entry, *spare; struct lafe_line_reader *lr; const char *p; int r; if (cpio->option_append) lafe_errc(1, 0, "Append mode not yet supported."); cpio->archive_read_disk = archive_read_disk_new(); if (cpio->archive_read_disk == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); if (cpio->option_follow_links) archive_read_disk_set_symlink_logical(cpio->archive_read_disk); else archive_read_disk_set_symlink_physical(cpio->archive_read_disk); archive_read_disk_set_standard_lookup(cpio->archive_read_disk); cpio->archive = archive_write_new(); if (cpio->archive == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); switch (cpio->compress) { case OPTION_GRZIP: r = archive_write_add_filter_grzip(cpio->archive); break; case 'J': r = archive_write_add_filter_xz(cpio->archive); break; case OPTION_LRZIP: r = archive_write_add_filter_lrzip(cpio->archive); break; case OPTION_LZ4: r = archive_write_add_filter_lz4(cpio->archive); break; case OPTION_LZMA: r = archive_write_add_filter_lzma(cpio->archive); break; case OPTION_LZOP: r = archive_write_add_filter_lzop(cpio->archive); break; case 'j': case 'y': r = archive_write_add_filter_bzip2(cpio->archive); break; case 'z': r = archive_write_add_filter_gzip(cpio->archive); break; case 'Z': r = archive_write_add_filter_compress(cpio->archive); break; default: r = archive_write_add_filter_none(cpio->archive); break; } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested compression not available"); switch (cpio->add_filter) { case 0: r = ARCHIVE_OK; break; case OPTION_B64ENCODE: r = archive_write_add_filter_b64encode(cpio->archive); break; case OPTION_UUENCODE: r = archive_write_add_filter_uuencode(cpio->archive); break; } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested filter not available"); r = archive_write_set_format_by_name(cpio->archive, cpio->format); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); archive_write_set_bytes_per_block(cpio->archive, cpio->bytes_per_block); cpio->linkresolver = archive_entry_linkresolver_new(); archive_entry_linkresolver_set_strategy(cpio->linkresolver, archive_format(cpio->archive)); if (cpio->passphrase != NULL) r = archive_write_set_passphrase(cpio->archive, cpio->passphrase); else r = archive_write_set_passphrase_callback(cpio->archive, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); /* * The main loop: Copy each file into the output archive. */ r = archive_write_open_filename(cpio->archive, cpio->filename); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); lr = lafe_line_reader("-", cpio->option_null); while ((p = lafe_line_reader_next(lr)) != NULL) file_to_archive(cpio, p); lafe_line_reader_free(lr); /* * The hardlink detection may have queued up a couple of entries * that can now be flushed. */ entry = NULL; archive_entry_linkify(cpio->linkresolver, &entry, &spare); while (entry != NULL) { entry_to_archive(cpio, entry); archive_entry_free(entry); entry = NULL; archive_entry_linkify(cpio->linkresolver, &entry, &spare); } r = archive_write_close(cpio->archive); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(cpio->archive, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_free(cpio->archive); } static const char * remove_leading_slash(const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; } do { rp = p; /* Remove leading drive letter from archives created * on Windows. */ if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; } /* Remove leading "/../", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" * for next pass. */ } else p += 1; /* Remove "/". */ } } while (rp != p); return (p); } /* * This is used by both out mode (to copy objects from disk into * an archive) and pass mode (to copy objects from disk to * an archive_write_disk "archive"). */ static int file_to_archive(struct cpio *cpio, const char *srcpath) { const char *destpath; struct archive_entry *entry, *spare; size_t len; int r; /* * Create an archive_entry describing the source file. * */ entry = archive_entry_new(); if (entry == NULL) lafe_errc(1, 0, "Couldn't allocate entry"); archive_entry_copy_sourcepath(entry, srcpath); r = archive_read_disk_entry_from_file(cpio->archive_read_disk, entry, -1, NULL); if (r < ARCHIVE_FAILED) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive_read_disk)); if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(cpio->archive_read_disk)); if (r <= ARCHIVE_FAILED) { archive_entry_free(entry); cpio->return_value = 1; return (r); } if (cpio->uid_override >= 0) { archive_entry_set_uid(entry, cpio->uid_override); archive_entry_set_uname(entry, cpio->uname_override); } if (cpio->gid_override >= 0) { archive_entry_set_gid(entry, cpio->gid_override); archive_entry_set_gname(entry, cpio->gname_override); } /* * Generate a destination path for this entry. * "destination path" is the name to which it will be copied in * pass mode or the name that will go into the archive in * output mode. */ destpath = srcpath; if (cpio->destdir) { len = strlen(cpio->destdir) + strlen(srcpath) + 8; if (len >= cpio->pass_destpath_alloc) { while (len >= cpio->pass_destpath_alloc) { cpio->pass_destpath_alloc += 512; cpio->pass_destpath_alloc *= 2; } free(cpio->pass_destpath); cpio->pass_destpath = malloc(cpio->pass_destpath_alloc); if (cpio->pass_destpath == NULL) lafe_errc(1, ENOMEM, "Can't allocate path buffer"); } strcpy(cpio->pass_destpath, cpio->destdir); strcat(cpio->pass_destpath, remove_leading_slash(srcpath)); destpath = cpio->pass_destpath; } if (cpio->option_rename) destpath = cpio_rename(destpath); if (destpath == NULL) return (0); archive_entry_copy_pathname(entry, destpath); /* * If we're trying to preserve hardlinks, match them here. */ spare = NULL; if (cpio->linkresolver != NULL && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_linkify(cpio->linkresolver, &entry, &spare); } if (entry != NULL) { r = entry_to_archive(cpio, entry); archive_entry_free(entry); if (spare != NULL) { if (r == 0) r = entry_to_archive(cpio, spare); archive_entry_free(spare); } } return (r); } static int entry_to_archive(struct cpio *cpio, struct archive_entry *entry) { const char *destpath = archive_entry_pathname(entry); const char *srcpath = archive_entry_sourcepath(entry); int fd = -1; ssize_t bytes_read; int r; /* Print out the destination name to the user. */ if (cpio->verbose) fprintf(stderr,"%s", destpath); if (cpio->dot) fprintf(stderr, "."); /* * Option_link only makes sense in pass mode and for * regular files. Also note: if a link operation fails * because of cross-device restrictions, we'll fall back * to copy mode for that entry. * * TODO: Test other cpio implementations to see if they * hard-link anything other than regular files here. */ if (cpio->option_link && archive_entry_filetype(entry) == AE_IFREG) { struct archive_entry *t; /* Save the original entry in case we need it later. */ t = archive_entry_clone(entry); if (t == NULL) lafe_errc(1, ENOMEM, "Can't create link"); /* Note: link(2) doesn't create parent directories, * so we use archive_write_header() instead as a * convenience. */ archive_entry_set_hardlink(t, srcpath); /* This is a straight link that carries no data. */ archive_entry_set_size(t, 0); r = archive_write_header(cpio->archive, t); archive_entry_free(t); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); #ifdef EXDEV if (r != ARCHIVE_OK && archive_errno(cpio->archive) == EXDEV) { /* Cross-device link: Just fall through and use * the original entry to copy the file over. */ lafe_warnc(0, "Copying file instead"); } else #endif return (0); } /* * Make sure we can open the file (if necessary) before * trying to write the header. */ if (archive_entry_filetype(entry) == AE_IFREG) { if (archive_entry_size(entry) > 0) { fd = open(srcpath, O_RDONLY | O_BINARY); if (fd < 0) { lafe_warnc(errno, "%s: could not open file", srcpath); goto cleanup; } } } else { archive_entry_set_size(entry, 0); } r = archive_write_header(cpio->archive, entry); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s: %s", srcpath, archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); if (r >= ARCHIVE_WARN && archive_entry_size(entry) > 0 && fd >= 0) { bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); while (bytes_read > 0) { ssize_t bytes_write; bytes_write = archive_write_data(cpio->archive, cpio->buff, bytes_read); if (bytes_write < 0) lafe_errc(1, archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (bytes_write < bytes_read) { lafe_warnc(0, "Truncated write; file may have " "grown while being archived."); } bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); } } fd = restore_time(cpio, entry, srcpath, fd); cleanup: if (cpio->verbose) fprintf(stderr,"\n"); if (fd >= 0) close(fd); return (0); } static int restore_time(struct cpio *cpio, struct archive_entry *entry, const char *name, int fd) { #ifndef HAVE_UTIMES static int warned = 0; (void)cpio; /* UNUSED */ (void)entry; /* UNUSED */ (void)name; /* UNUSED */ if (!warned) lafe_warnc(0, "Can't restore access times on this platform"); warned = 1; return (fd); #else #if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif if (!cpio->option_atime_restore) return (fd); times[1].tv_sec = archive_entry_mtime(entry); times[1].tv_usec = archive_entry_mtime_nsec(entry) / 1000; times[0].tv_sec = archive_entry_atime(entry); times[0].tv_usec = archive_entry_atime_nsec(entry) / 1000; #if defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (fd >= 0 && futimes(fd, times) == 0) return (fd); #endif /* * Some platform cannot restore access times if the file descriptor * is still opened. */ if (fd >= 0) { close(fd); fd = -1; } #ifdef HAVE_LUTIMES if (lutimes(name, times) != 0) #else if ((AE_IFLNK != archive_entry_filetype(entry)) && utimes(name, times) != 0) #endif lafe_warnc(errno, "Can't update time for %s", name); #endif return (fd); } static void mode_in(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; struct archive *ext; const char *destpath; int r; ext = archive_write_disk_new(); if (ext == NULL) lafe_errc(1, 0, "Couldn't allocate restore object"); r = archive_write_disk_set_options(ext, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (cpio->passphrase != NULL) r = archive_read_add_passphrase(a, cpio->passphrase); else r = archive_read_set_passphrase_callback(a, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->option_rename) { destpath = cpio_rename(archive_entry_pathname(entry)); archive_entry_set_pathname(entry, destpath); } else destpath = archive_entry_pathname(entry); if (destpath == NULL) continue; if (cpio->verbose) fprintf(stderr, "%s\n", destpath); if (cpio->dot) fprintf(stderr, "."); if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) archive_entry_set_gid(entry, cpio->gid_override); r = archive_write_header(ext, entry); if (r != ARCHIVE_OK) { fprintf(stderr, "%s: %s\n", archive_entry_pathname(entry), archive_error_string(ext)); } else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) { r = extract_data(a, ext); if (r != ARCHIVE_OK) cpio->return_value = 1; } } r = archive_read_close(a); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); r = archive_write_close(ext); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); archive_write_free(ext); exit(cpio->return_value); } /* * Exits if there's a fatal error. Returns ARCHIVE_OK * if everything is kosher. */ static int extract_data(struct archive *ar, struct archive *aw) { int r; size_t size; const void *block; int64_t offset; for (;;) { r = archive_read_data_block(ar, &block, &size, &offset); if (r == ARCHIVE_EOF) return (ARCHIVE_OK); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(ar), "%s", archive_error_string(ar)); exit(1); } r = (int)archive_write_data_block(aw, block, size, offset); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(aw), "%s", archive_error_string(aw)); return (r); } } } static void mode_list(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; int r; a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (cpio->passphrase != NULL) r = archive_read_add_passphrase(a, cpio->passphrase); else r = archive_read_set_passphrase_callback(a, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->verbose) list_item_verbose(cpio, entry); else fprintf(stdout, "%s\n", archive_entry_pathname(entry)); } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); exit(0); } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct cpio *cpio, struct archive_entry *entry) { char size[32]; char date[32]; char uids[16], gids[16]; const char *uname, *gname; FILE *out = stdout; const char *fmt; time_t mtime; static time_t now; if (!now) time(&now); if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); uname = uids; strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ uname = archive_entry_uname(entry); if (uname == NULL) uname = lookup_uname(cpio, (uid_t)archive_entry_uid(entry)); /* Use gname if it's present, else lookup name from gid. */ gname = archive_entry_gname(entry); if (gname == NULL) gname = lookup_gname(cpio, (uid_t)archive_entry_gid(entry)); } /* Print device number or file size. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(size, sizeof(size), "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(size, cpio_i64toa(archive_entry_size(entry))); } /* Format the time using 'ls -l' conventions. */ mtime = archive_entry_mtime(entry); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ if (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%d %b %Y" : "%b %d %Y"; else fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; #else if (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; #endif strftime(date, sizeof(date), fmt, localtime(&mtime)); fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", archive_entry_strmode(entry), archive_entry_nlink(entry), uname, gname, size, date, archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ fprintf(out, " -> %s", archive_entry_symlink(entry)); fprintf(out, "\n"); } static void mode_pass(struct cpio *cpio, const char *destdir) { struct lafe_line_reader *lr; const char *p; int r; /* Ensure target dir has a trailing '/' to simplify path surgery. */ cpio->destdir = malloc(strlen(destdir) + 8); strcpy(cpio->destdir, destdir); if (destdir[strlen(destdir) - 1] != '/') strcat(cpio->destdir, "/"); cpio->archive = archive_write_disk_new(); if (cpio->archive == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); cpio->linkresolver = archive_entry_linkresolver_new(); archive_write_disk_set_standard_lookup(cpio->archive); cpio->archive_read_disk = archive_read_disk_new(); if (cpio->archive_read_disk == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); if (cpio->option_follow_links) archive_read_disk_set_symlink_logical(cpio->archive_read_disk); else archive_read_disk_set_symlink_physical(cpio->archive_read_disk); archive_read_disk_set_standard_lookup(cpio->archive_read_disk); lr = lafe_line_reader("-", cpio->option_null); while ((p = lafe_line_reader_next(lr)) != NULL) file_to_archive(cpio, p); lafe_line_reader_free(lr); archive_entry_linkresolver_free(cpio->linkresolver); r = archive_write_close(cpio->archive); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(cpio->archive, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_free(cpio->archive); } /* * Prompt for a new name for this entry. Returns a pointer to the * new name or NULL if the entry should not be copied. This * implements the semantics defined in POSIX.1-1996, which specifies * that an input of '.' means the name should be unchanged. GNU cpio * treats '.' as a literal new name. */ static const char * cpio_rename(const char *name) { static char buff[1024]; FILE *t; char *p, *ret; #if defined(_WIN32) && !defined(__CYGWIN__) FILE *to; t = fopen("CONIN$", "r"); if (t == NULL) return (name); to = fopen("CONOUT$", "w"); if (to == NULL) { fclose(t); return (name); } fprintf(to, "%s (Enter/./(new name))? ", name); fclose(to); #else t = fopen("/dev/tty", "r+"); if (t == NULL) return (name); fprintf(t, "%s (Enter/./(new name))? ", name); fflush(t); #endif p = fgets(buff, sizeof(buff), t); fclose(t); if (p == NULL) /* End-of-file is a blank line. */ return (NULL); while (*p == ' ' || *p == '\t') ++p; if (*p == '\n' || *p == '\0') /* Empty line. */ return (NULL); if (*p == '.' && p[1] == '\n') /* Single period preserves original name. */ return (name); ret = p; /* Trim the final newline. */ while (*p != '\0' && *p != '\n') ++p; /* Overwrite the final \n with a null character. */ *p = '\0'; return (ret); } static void free_cache(struct name_cache *cache) { size_t i; if (cache != NULL) { for (i = 0; i < cache->size; i++) free(cache->cache[i].name); free(cache); } } /* * Lookup uname/gname from uid/gid, return NULL if no match. */ static const char * lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable, int (*lookup_fn)(struct cpio *, const char **, id_t), id_t id) { char asnum[16]; struct name_cache *cache; const char *name; int slot; if (*name_cache_variable == NULL) { *name_cache_variable = calloc(1, sizeof(struct name_cache)); if (*name_cache_variable == NULL) lafe_errc(1, ENOMEM, "No more memory"); (*name_cache_variable)->size = name_cache_size; } cache = *name_cache_variable; cache->probes++; slot = id % cache->size; if (cache->cache[slot].name != NULL) { if (cache->cache[slot].id == id) { cache->hits++; return (cache->cache[slot].name); } free(cache->cache[slot].name); cache->cache[slot].name = NULL; } - if (lookup_fn(cpio, &name, id) == 0) { - if (name == NULL || name[0] == '\0') { - /* If lookup failed, format it as a number. */ - snprintf(asnum, sizeof(asnum), "%u", (unsigned)id); - name = asnum; - } - cache->cache[slot].name = strdup(name); - if (cache->cache[slot].name != NULL) { - cache->cache[slot].id = id; - return (cache->cache[slot].name); - } - /* - * Conveniently, NULL marks an empty slot, so - * if the strdup() fails, we've just failed to - * cache it. No recovery necessary. - */ + if (lookup_fn(cpio, &name, id)) { + /* If lookup failed, format it as a number. */ + snprintf(asnum, sizeof(asnum), "%u", (unsigned)id); + name = asnum; } + + cache->cache[slot].name = strdup(name); + if (cache->cache[slot].name != NULL) { + cache->cache[slot].id = id; + return (cache->cache[slot].name); + } + + /* + * Conveniently, NULL marks an empty slot, so + * if the strdup() fails, we've just failed to + * cache it. No recovery necessary. + */ return (NULL); } static const char * lookup_uname(struct cpio *cpio, uid_t uid) { return (lookup_name(cpio, &cpio->uname_cache, &lookup_uname_helper, (id_t)uid)); } static int lookup_uname_helper(struct cpio *cpio, const char **name, id_t id) { struct passwd *pwent; (void)cpio; /* UNUSED */ errno = 0; pwent = getpwuid((uid_t)id); if (pwent == NULL) { - *name = NULL; - if (errno != 0 && errno != ENOENT) + if (errno && errno != ENOENT) lafe_warnc(errno, "getpwuid(%s) failed", cpio_i64toa((int64_t)id)); - return (errno); + return 1; } *name = pwent->pw_name; - return (0); + return 0; } static const char * lookup_gname(struct cpio *cpio, gid_t gid) { return (lookup_name(cpio, &cpio->gname_cache, &lookup_gname_helper, (id_t)gid)); } static int lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) { struct group *grent; (void)cpio; /* UNUSED */ errno = 0; grent = getgrgid((gid_t)id); if (grent == NULL) { - *name = NULL; - if (errno != 0) + if (errno && errno != ENOENT) lafe_warnc(errno, "getgrgid(%s) failed", cpio_i64toa((int64_t)id)); - return (errno); + return 1; } *name = grent->gr_name; - return (0); + return 0; } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are a big headache. Hence the * following simple utility function. */ const char * cpio_i64toa(int64_t n0) { /* 2^64 =~ 1.8 * 10^19, so 20 decimal digits suffice. * We also need 1 byte for '-' and 1 for '\0'. */ static char buff[22]; int64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); n /= 10; } while (n > 0); if (n0 < 0) *--p = '-'; return p; } #define PPBUFF_SIZE 1024 static const char * passphrase_callback(struct archive *a, void *_client_data) { struct cpio *cpio = (struct cpio *)_client_data; (void)a; /* UNUSED */ if (cpio->ppbuff == NULL) { cpio->ppbuff = malloc(PPBUFF_SIZE); if (cpio->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", cpio->ppbuff, PPBUFF_SIZE); } static void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } Index: stable/10/contrib/libarchive/libarchive/archive_write_disk_acl.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_disk_acl.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_disk_acl.c (nonexistent) @@ -1,697 +0,0 @@ -/*- - * 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 - * 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: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 05:35:40Z kientzle $"); - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_ACL_H -#define _ACL_PRIVATE /* For debugging */ -#include -#endif -#if HAVE_DARWIN_ACL -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "archive.h" -#include "archive_entry.h" -#include "archive_acl_private.h" -#include "archive_write_disk_private.h" - -#if !HAVE_POSIX_ACL && !HAVE_NFS4_ACL -/* Default empty function body to satisfy mainline code. */ -int -archive_write_disk_set_acls(struct archive *a, int fd, const char *name, - struct archive_acl *abstract_acl) -{ - (void)a; /* UNUSED */ - (void)fd; /* UNUSED */ - (void)name; /* UNUSED */ - (void)abstract_acl; /* UNUSED */ - return (ARCHIVE_OK); -} - -#else /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */ - -#if HAVE_DARWIN_ACL -#define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACL_TYPE_EXTENDED -#elif HAVE_FREEBSD_NFS4_ACL -#define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACL_TYPE_NFS4 -#endif - -static int set_acl(struct archive *, int fd, const char *, - struct archive_acl *, -#if !HAVE_SUN_ACL - acl_type_t, -#endif - int archive_entry_acl_type, const char *tn); - -int -archive_write_disk_set_acls(struct archive *a, int fd, const char *name, - struct archive_acl *abstract_acl) -{ - int ret = ARCHIVE_OK; - -#if !HAVE_DARWIN_ACL - if ((archive_acl_types(abstract_acl) - & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { -#if HAVE_SUN_ACL - /* Solaris writes POSIX.1e access and default ACLs together */ - ret = set_acl(a, fd, name, abstract_acl, - ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e"); -#else /* HAVE_POSIX_ACL */ - if ((archive_acl_types(abstract_acl) - & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - ret = set_acl(a, fd, name, abstract_acl, - ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, - "access"); - if (ret != ARCHIVE_OK) - return (ret); - } - if ((archive_acl_types(abstract_acl) - & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) - ret = set_acl(a, fd, name, abstract_acl, - ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, - "default"); -#endif /* !HAVE_SUN_ACL */ - /* Simultaneous POSIX.1e and NFSv4 is not supported */ - return (ret); - } -#endif /* !HAVE_DARWIN_ACL */ -#if HAVE_NFS4_ACL - if ((archive_acl_types(abstract_acl) & - ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - ret = set_acl(a, fd, name, abstract_acl, -#if !HAVE_SUN_ACL - ARCHIVE_PLATFORM_ACL_TYPE_NFS4, -#endif - ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); - } -#endif /* HAVE_NFS4_ACL */ - return (ret); -} - -#if !HAVE_SUN_ACL || HAVE_SUN_NFS4_ACL -/* - * Translate system ACL permissions into libarchive internal structure - */ -static const struct { - const int archive_perm; - const int platform_perm; -} acl_perm_map[] = { -#if HAVE_SUN_NFS4_ACL /* Solaris NFSv4 ACL permissions */ - {ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE}, - {ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER}, - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE} -#elif HAVE_DARWIN_ACL /* MacOS ACL permissions */ - {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER}, -#if HAVE_DECL_ACL_SYNCHRONIZE - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} -#endif -#else /* POSIX.1e ACL permissions */ - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, - {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, -#if HAVE_FREEBSD_NFS4_ACL /* FreeBSD NFSv4 ACL permissions */ - {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} -#endif -#endif /* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */ -}; -#endif /* !HAVE_SUN_ACL || HAVE_SUN_NFS4_ACL */ - -#if HAVE_NFS4_ACL -/* - * Translate system NFSv4 inheritance flags into libarchive internal structure - */ -static const struct { - const int archive_inherit; - const int platform_inherit; -} acl_inherit_map[] = { -#if HAVE_SUN_NFS4_ACL /* Solaris NFSv4 inheritance flags */ - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG}, - {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG}, -#ifdef ACE_INHERITED_ACE - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE} -#endif -#elif HAVE_DARWIN_ACL /* MacOS NFSv4 inheritance flags */ - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}, - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT} -#else /* FreeBSD NFSv4 ACL inheritance flags */ - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, - {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, - {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} -#endif /* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */ -}; -#endif /* HAVE_NFS4_ACL */ - -static int -set_acl(struct archive *a, int fd, const char *name, - struct archive_acl *abstract_acl, -#if !HAVE_SUN_ACL - acl_type_t acl_type, -#endif - int ae_requested_type, const char *tname) -{ -#if HAVE_SUN_ACL - aclent_t *aclent; -#if HAVE_SUN_NFS4_ACL - ace_t *ace; -#endif - int cmd, e, r; - void *aclp; -#else - acl_t acl; - acl_entry_t acl_entry; - acl_permset_t acl_permset; -#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL - acl_flagset_t acl_flagset; -#endif -#endif /* HAVE_SUN_ACL */ -#if HAVE_FREEBSD_NFS4_ACL - int r; -#endif - int ret; - int ae_type, ae_permset, ae_tag, ae_id; -#if HAVE_DARWIN_ACL - uuid_t ae_uuid; -#endif - uid_t ae_uid; - gid_t ae_gid; - const char *ae_name; - int entries; - int i; - - ret = ARCHIVE_OK; - entries = archive_acl_reset(abstract_acl, ae_requested_type); - if (entries == 0) - return (ARCHIVE_OK); - -#if HAVE_SUN_ACL - switch (ae_requested_type) { - case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: - cmd = SETACL; - aclp = malloc(entries * sizeof(aclent_t)); - break; -#if HAVE_SUN_NFS4_ACL - case ARCHIVE_ENTRY_ACL_TYPE_NFS4: - cmd = ACE_SETACL; - aclp = malloc(entries * sizeof(ace_t)); - break; -#endif - default: - errno = ENOENT; - archive_set_error(a, errno, "Invalid ACL type"); - return (ARCHIVE_FAILED); - } - - if (aclp == NULL) { - archive_set_error(a, errno, - "Can't allocate memory for acl buffer"); - return (ARCHIVE_FAILED); - } -#else /* !HAVE_SUN_ACL */ - acl = acl_init(entries); - if (acl == (acl_t)NULL) { - archive_set_error(a, errno, - "Failed to initialize ACL working storage"); - return (ARCHIVE_FAILED); - } -#endif /* !HAVE_SUN_ACL */ -#if HAVE_SUN_ACL - e = 0; -#endif - while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, - &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { -#if HAVE_SUN_ACL - aclent = NULL; -#if HAVE_SUN_NFS4_ACL - ace = NULL; -#endif - if (cmd == SETACL) { - aclent = &((aclent_t *)aclp)[e]; - aclent->a_id = -1; - aclent->a_type = 0; - aclent->a_perm = 0; - } -#if HAVE_SUN_NFS4_ACL - else { /* cmd == ACE_SETACL */ - ace = &((ace_t *)aclp)[e]; - ace->a_who = -1; - ace->a_access_mask = 0; - ace->a_flags = 0; - } -#endif /* HAVE_SUN_NFS4_ACL */ -#else /* !HAVE_SUN_ACL */ -#if HAVE_DARWIN_ACL - /* - * Mac OS doesn't support NFSv4 ACLs for - * owner@, group@ and everyone@. - * We skip any of these ACLs found. - */ - if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ || - ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || - ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE) - continue; -#endif - if (acl_create_entry(&acl, &acl_entry) != 0) { - archive_set_error(a, errno, - "Failed to create a new ACL entry"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif /* !HAVE_SUN_ACL */ -#if HAVE_DARWIN_ACL - switch (ae_type) { - case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW); - break; - case ARCHIVE_ENTRY_ACL_TYPE_DENY: - acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY); - break; - default: - /* We don't support any other types on MacOS */ - continue; - } -#endif - switch (ae_tag) { -#if HAVE_SUN_ACL - case ARCHIVE_ENTRY_ACL_USER: - ae_uid = archive_write_disk_uid(a, ae_name, ae_id); - if (aclent != NULL) { - aclent->a_id = ae_uid; - aclent->a_type |= USER; - } -#if HAVE_SUN_NFS4_ACL - else { - ace->a_who = ae_uid; - } -#endif - break; - case ARCHIVE_ENTRY_ACL_GROUP: - ae_gid = archive_write_disk_gid(a, ae_name, ae_id); - if (aclent != NULL) { - aclent->a_id = ae_gid; - aclent->a_type |= GROUP; - } -#if HAVE_SUN_NFS4_ACL - else { - ace->a_who = ae_gid; - ace->a_flags |= ACE_IDENTIFIER_GROUP; - } -#endif - break; - case ARCHIVE_ENTRY_ACL_USER_OBJ: - if (aclent != NULL) - aclent->a_type |= USER_OBJ; -#if HAVE_SUN_NFS4_ACL - else { - ace->a_flags |= ACE_OWNER; - } -#endif - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - if (aclent != NULL) - aclent->a_type |= GROUP_OBJ; -#if HAVE_SUN_NFS4_ACL - else { - ace->a_flags |= ACE_GROUP; - ace->a_flags |= ACE_IDENTIFIER_GROUP; - } - -#endif - break; - case ARCHIVE_ENTRY_ACL_MASK: - if (aclent != NULL) - aclent->a_type |= CLASS_OBJ; - break; - case ARCHIVE_ENTRY_ACL_OTHER: - if (aclent != NULL) - aclent->a_type |= OTHER_OBJ; - break; -#if HAVE_SUN_NFS4_ACL - case ARCHIVE_ENTRY_ACL_EVERYONE: - if (ace != NULL) - ace->a_flags |= ACE_EVERYONE; - break; -#endif -#else /* !HAVE_SUN_ACL */ - case ARCHIVE_ENTRY_ACL_USER: - ae_uid = archive_write_disk_uid(a, ae_name, ae_id); -#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ - acl_set_tag_type(acl_entry, ACL_USER); - acl_set_qualifier(acl_entry, &ae_uid); -#else /* MacOS */ - if (mbr_identifier_to_uuid(ID_TYPE_UID, &ae_uid, - sizeof(uid_t), ae_uuid) != 0) - continue; - if (acl_set_qualifier(acl_entry, &ae_uuid) != 0) - continue; -#endif /* HAVE_DARWIN_ACL */ - break; - case ARCHIVE_ENTRY_ACL_GROUP: - ae_gid = archive_write_disk_gid(a, ae_name, ae_id); -#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ - acl_set_tag_type(acl_entry, ACL_GROUP); - acl_set_qualifier(acl_entry, &ae_gid); -#else /* MacOS */ - if (mbr_identifier_to_uuid(ID_TYPE_GID, &ae_gid, - sizeof(gid_t), ae_uuid) != 0) - continue; - if (acl_set_qualifier(acl_entry, &ae_uuid) != 0) - continue; -#endif /* HAVE_DARWIN_ACL */ - break; -#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ - case ARCHIVE_ENTRY_ACL_USER_OBJ: - acl_set_tag_type(acl_entry, ACL_USER_OBJ); - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); - break; - case ARCHIVE_ENTRY_ACL_MASK: - acl_set_tag_type(acl_entry, ACL_MASK); - break; - case ARCHIVE_ENTRY_ACL_OTHER: - acl_set_tag_type(acl_entry, ACL_OTHER); - break; -#if HAVE_FREEBSD_NFS4_ACL /* FreeBSD only */ - case ARCHIVE_ENTRY_ACL_EVERYONE: - acl_set_tag_type(acl_entry, ACL_EVERYONE); - break; -#endif -#endif /* !HAVE_DARWIN_ACL */ -#endif /* !HAVE_SUN_ACL */ - default: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Unknown ACL tag"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - -#if HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_ACL - r = 0; - switch (ae_type) { -#if HAVE_SUN_ACL -#if HAVE_SUN_NFS4_ACL - case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - if (ace != NULL) - ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; - else - r = -1; - break; - case ARCHIVE_ENTRY_ACL_TYPE_DENY: - if (ace != NULL) - ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE; - else - r = -1; - break; - case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - if (ace != NULL) - ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE; - else - r = -1; - break; - case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - if (ace != NULL) - ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE; - else - r = -1; - break; -#endif - case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: - if (aclent == NULL) - r = -1; - break; - case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: - if (aclent != NULL) - aclent->a_type |= ACL_DEFAULT; - else - r = -1; - break; -#else /* !HAVE_SUN_ACL */ - case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); - break; - case ARCHIVE_ENTRY_ACL_TYPE_DENY: - r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); - break; - case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); - break; - case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); - break; - case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: - case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: - // These don't translate directly into the system ACL. - break; -#endif /* !HAVE_SUN_ACL */ - default: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Unsupported ACL entry type"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - - if (r != 0) { -#if HAVE_SUN_ACL - errno = EINVAL; -#endif - archive_set_error(a, errno, - "Failed to set ACL entry type"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif /* HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_ACL */ - -#if HAVE_SUN_ACL - if (aclent != NULL) { - if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) - aclent->a_perm |= 1; - if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) - aclent->a_perm |= 2; - if (ae_permset & ARCHIVE_ENTRY_ACL_READ) - aclent->a_perm |= 4; - } -#if HAVE_SUN_NFS4_ACL - else /* falls through to for statement below, ace != NULL */ -#endif -#else - if (acl_get_permset(acl_entry, &acl_permset) != 0) { - archive_set_error(a, errno, - "Failed to get ACL permission set"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - if (acl_clear_perms(acl_permset) != 0) { - archive_set_error(a, errno, - "Failed to clear ACL permissions"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif /* !HAVE_SUN_ACL */ -#if HAVE_POSIX_ACL || HAVE_NFS4_ACL - for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { - if (ae_permset & acl_perm_map[i].archive_perm) { -#if HAVE_SUN_ACL - ace->a_access_mask |= - acl_perm_map[i].platform_perm; -#else - if (acl_add_perm(acl_permset, - acl_perm_map[i].platform_perm) != 0) { - archive_set_error(a, errno, - "Failed to add ACL permission"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif - } - } -#endif /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */ - -#if HAVE_NFS4_ACL -#if HAVE_SUN_NFS4_ACL - if (ace != NULL) -#elif HAVE_DARWIN_ACL - if (acl_type == ACL_TYPE_EXTENDED) -#else /* FreeBSD */ - if (acl_type == ACL_TYPE_NFS4) -#endif - { -#if HAVE_POSIX_ACL || HAVE_DARWIN_ACL - /* - * acl_get_flagset_np() fails with non-NFSv4 ACLs - */ - if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { - archive_set_error(a, errno, - "Failed to get flagset from an NFSv4 ACL entry"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - if (acl_clear_flags_np(acl_flagset) != 0) { - archive_set_error(a, errno, - "Failed to clear flags from an NFSv4 ACL flagset"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif /* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */ - for (i = 0; i < (int)(sizeof(acl_inherit_map) /sizeof(acl_inherit_map[0])); ++i) { - if (ae_permset & acl_inherit_map[i].archive_inherit) { -#if HAVE_SUN_ACL - ace->a_flags |= - acl_inherit_map[i].platform_inherit; -#else /* !HAVE_SUN_ACL */ - if (acl_add_flag_np(acl_flagset, - acl_inherit_map[i].platform_inherit) != 0) { - archive_set_error(a, errno, - "Failed to add flag to NFSv4 ACL flagset"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif /* HAVE_SUN_ACL */ - } - } - } -#endif /* HAVE_NFS4_ACL */ -#if HAVE_SUN_ACL - e++; -#endif - } - -#if HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL - /* Try restoring the ACL through 'fd' if we can. */ -#if HAVE_SUN_ACL || HAVE_ACL_SET_FD_NP - if (fd >= 0) -#else /* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */ - if (fd >= 0 && acl_type == ACL_TYPE_ACCESS) -#endif - { -#if HAVE_SUN_ACL - if (facl(fd, cmd, entries, aclp) == 0) -#elif HAVE_ACL_SET_FD_NP - if (acl_set_fd_np(fd, acl, acl_type) == 0) -#else /* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */ - if (acl_set_fd(fd, acl) == 0) -#endif - ret = ARCHIVE_OK; - else { - if (errno == EOPNOTSUPP) { - /* Filesystem doesn't support ACLs */ - ret = ARCHIVE_OK; - } else { - archive_set_error(a, errno, - "Failed to set %s acl on fd", tname); - } - } - } else -#endif /* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL */ -#if HAVE_SUN_ACL - if (acl(name, cmd, entries, aclp) != 0) -#elif HAVE_ACL_SET_LINK_NP - if (acl_set_link_np(name, acl_type, acl) != 0) -#else - /* TODO: Skip this if 'name' is a symlink. */ - if (acl_set_file(name, acl_type, acl) != 0) -#endif - { - if (errno == EOPNOTSUPP) { - /* Filesystem doesn't support ACLs */ - ret = ARCHIVE_OK; - } else { - archive_set_error(a, errno, "Failed to set %s acl", - tname); - ret = ARCHIVE_WARN; - } - } -exit_free: -#if HAVE_SUN_ACL - free(aclp); -#else - acl_free(acl); -#endif - return (ret); -} -#endif /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */ Index: stable/10/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c (nonexistent) +++ stable/10/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c (revision 316338) @@ -0,0 +1,700 @@ +/*- + * Copyright (c) 2003-2009 Tim Kientzle + * Copyright (c) 2010-2012 Michihiro NAKAJIMA + * 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(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" + +#if ARCHIVE_ACL_FREEBSD + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_ACL_H +#define _ACL_PRIVATE /* For debugging */ +#include +#endif + +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_disk_private.h" +#include "archive_write_disk_private.h" + +typedef struct { + const int a_perm; /* Libarchive permission or flag */ + const int p_perm; /* Platform permission or flag */ +} acl_perm_map_t; + +static const acl_perm_map_t acl_posix_perm_map[] = { + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, + {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, +}; + +static const int acl_posix_perm_map_size = + (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0])); + +#if ARCHIVE_ACL_FREEBSD_NFS4 +static const acl_perm_map_t acl_nfs4_perm_map[] = { + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, + {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, + {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, + {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, + {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, + {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, + {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, + {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} +}; + +static const int acl_nfs4_perm_map_size = + (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0])); + +static const acl_perm_map_t acl_nfs4_flag_map[] = { + {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, + {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, + {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} +}; + +static const int acl_nfs4_flag_map_size = + (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0])); +#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ + +static int +translate_acl(struct archive_read_disk *a, + struct archive_entry *entry, acl_t acl, int default_entry_acl_type) +{ +#if ARCHIVE_ACL_FREEBSD_NFS4 + int brand; + acl_flagset_t acl_flagset; + acl_entry_type_t acl_type; +#endif + acl_tag_t acl_tag; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + int i, entry_acl_type, perm_map_size; + const acl_perm_map_t *perm_map; + int r, s, ae_id, ae_tag, ae_perm; + void *q; + const char *ae_name; + +#if ARCHIVE_ACL_FREEBSD_NFS4 + // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 + // Make sure the "brand" on this ACL is consistent + // with the default_entry_acl_type bits provided. + if (acl_get_brand_np(acl, &brand) != 0) { + archive_set_error(&a->archive, errno, + "Failed to read ACL brand"); + return (ARCHIVE_WARN); + } + switch (brand) { + case ACL_BRAND_POSIX: + switch (default_entry_acl_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid ACL entry type for POSIX.1e ACL"); + return (ARCHIVE_WARN); + } + break; + case ACL_BRAND_NFS4: + if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid ACL entry type for NFSv4 ACL"); + return (ARCHIVE_WARN); + } + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unknown ACL brand"); + return (ARCHIVE_WARN); + } +#endif + + s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); + if (s == -1) { + archive_set_error(&a->archive, errno, + "Failed to get first ACL entry"); + return (ARCHIVE_WARN); + } + + while (s == 1) { + ae_id = -1; + ae_name = NULL; + ae_perm = 0; + + if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { + archive_set_error(&a->archive, errno, + "Failed to get ACL tag type"); + return (ARCHIVE_WARN); + } + switch (acl_tag) { + case ACL_USER: + q = acl_get_qualifier(acl_entry); + if (q != NULL) { + ae_id = (int)*(uid_t *)q; + acl_free(q); + ae_name = archive_read_disk_uname(&a->archive, + ae_id); + } + ae_tag = ARCHIVE_ENTRY_ACL_USER; + break; + case ACL_GROUP: + q = acl_get_qualifier(acl_entry); + if (q != NULL) { + ae_id = (int)*(gid_t *)q; + acl_free(q); + ae_name = archive_read_disk_gname(&a->archive, + ae_id); + } + ae_tag = ARCHIVE_ENTRY_ACL_GROUP; + break; + case ACL_MASK: + ae_tag = ARCHIVE_ENTRY_ACL_MASK; + break; + case ACL_USER_OBJ: + ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + break; + case ACL_GROUP_OBJ: + ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + ae_tag = ARCHIVE_ENTRY_ACL_OTHER; + break; +#if ARCHIVE_ACL_FREEBSD_NFS4 + case ACL_EVERYONE: + ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; + break; +#endif + default: + /* Skip types that libarchive can't support. */ + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + continue; + } + + // XXX acl_type maps to allow/deny/audit/YYYY bits + entry_acl_type = default_entry_acl_type; + +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* + * acl_get_entry_type_np() fails with non-NFSv4 ACLs + */ + if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { + archive_set_error(&a->archive, errno, "Failed " + "to get ACL type from a NFSv4 ACL entry"); + return (ARCHIVE_WARN); + } + switch (acl_type) { + case ACL_ENTRY_TYPE_ALLOW: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; + break; + case ACL_ENTRY_TYPE_DENY: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; + break; + case ACL_ENTRY_TYPE_AUDIT: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; + break; + case ACL_ENTRY_TYPE_ALARM: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; + break; + default: + archive_set_error(&a->archive, errno, + "Invalid NFSv4 ACL entry type"); + return (ARCHIVE_WARN); + } + + /* + * Libarchive stores "flag" (NFSv4 inheritance bits) + * in the ae_perm bitmap. + * + * acl_get_flagset_np() fails with non-NFSv4 ACLs + */ + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { + archive_set_error(&a->archive, errno, + "Failed to get flagset from a NFSv4 " + "ACL entry"); + return (ARCHIVE_WARN); + } + for (i = 0; i < acl_nfs4_flag_map_size; ++i) { + r = acl_get_flag_np(acl_flagset, + acl_nfs4_flag_map[i].p_perm); + if (r == -1) { + archive_set_error(&a->archive, errno, + "Failed to check flag in a NFSv4 " + "ACL flagset"); + return (ARCHIVE_WARN); + } else if (r) + ae_perm |= acl_nfs4_flag_map[i].a_perm; + } + } +#endif + + if (acl_get_permset(acl_entry, &acl_permset) != 0) { + archive_set_error(&a->archive, errno, + "Failed to get ACL permission set"); + return (ARCHIVE_WARN); + } + +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + perm_map_size = acl_nfs4_perm_map_size; + perm_map = acl_nfs4_perm_map; + } else { +#endif + perm_map_size = acl_posix_perm_map_size; + perm_map = acl_posix_perm_map; +#if ARCHIVE_ACL_FREEBSD_NFS4 + } +#endif + + for (i = 0; i < perm_map_size; ++i) { + r = acl_get_perm_np(acl_permset, perm_map[i].p_perm); + if (r == -1) { + archive_set_error(&a->archive, errno, + "Failed to check permission in an ACL " + "permission set"); + return (ARCHIVE_WARN); + } else if (r) + ae_perm |= perm_map[i].a_perm; + } + + archive_entry_acl_add_entry(entry, entry_acl_type, + ae_perm, ae_tag, + ae_id, ae_name); + + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + if (s == -1) { + archive_set_error(&a->archive, errno, + "Failed to get next ACL entry"); + return (ARCHIVE_WARN); + } + } + return (ARCHIVE_OK); +} + +static int +set_acl(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl, + int ae_requested_type, const char *tname) +{ + int acl_type = 0; + acl_t acl; + acl_entry_t acl_entry; + acl_permset_t acl_permset; +#if ARCHIVE_ACL_FREEBSD_NFS4 + acl_flagset_t acl_flagset; + int r; +#endif + int ret; + int ae_type, ae_permset, ae_tag, ae_id; + int perm_map_size; + const acl_perm_map_t *perm_map; + uid_t ae_uid; + gid_t ae_gid; + const char *ae_name; + int entries; + int i; + + ret = ARCHIVE_OK; + entries = archive_acl_reset(abstract_acl, ae_requested_type); + if (entries == 0) + return (ARCHIVE_OK); + + + switch (ae_requested_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + acl_type = ACL_TYPE_ACCESS; + break; + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + acl_type = ACL_TYPE_DEFAULT; + break; +#if ARCHIVE_ACL_FREEBSD_NFS4 + case ARCHIVE_ENTRY_ACL_TYPE_NFS4: + acl_type = ACL_TYPE_NFS4; + break; +#endif + default: + errno = ENOENT; + archive_set_error(a, errno, "Unsupported ACL type"); + return (ARCHIVE_FAILED); + } + + acl = acl_init(entries); + if (acl == (acl_t)NULL) { + archive_set_error(a, errno, + "Failed to initialize ACL working storage"); + return (ARCHIVE_FAILED); + } + + while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, + &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { + if (acl_create_entry(&acl, &acl_entry) != 0) { + archive_set_error(a, errno, + "Failed to create a new ACL entry"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + switch (ae_tag) { + case ARCHIVE_ENTRY_ACL_USER: + ae_uid = archive_write_disk_uid(a, ae_name, ae_id); + acl_set_tag_type(acl_entry, ACL_USER); + acl_set_qualifier(acl_entry, &ae_uid); + break; + case ARCHIVE_ENTRY_ACL_GROUP: + ae_gid = archive_write_disk_gid(a, ae_name, ae_id); + acl_set_tag_type(acl_entry, ACL_GROUP); + acl_set_qualifier(acl_entry, &ae_gid); + break; + case ARCHIVE_ENTRY_ACL_USER_OBJ: + acl_set_tag_type(acl_entry, ACL_USER_OBJ); + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); + break; + case ARCHIVE_ENTRY_ACL_MASK: + acl_set_tag_type(acl_entry, ACL_MASK); + break; + case ARCHIVE_ENTRY_ACL_OTHER: + acl_set_tag_type(acl_entry, ACL_OTHER); + break; +#if ARCHIVE_ACL_FREEBSD_NFS4 + case ARCHIVE_ENTRY_ACL_EVERYONE: + acl_set_tag_type(acl_entry, ACL_EVERYONE); + break; +#endif + default: + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Unsupported ACL tag"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + +#if ARCHIVE_ACL_FREEBSD_NFS4 + r = 0; + switch (ae_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_ALLOW); + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_DENY); + break; + case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_AUDIT); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ALARM: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_ALARM); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + // These don't translate directly into the system ACL. + break; + default: + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Unsupported ACL entry type"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + + if (r != 0) { + archive_set_error(a, errno, + "Failed to set ACL entry type"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif + + if (acl_get_permset(acl_entry, &acl_permset) != 0) { + archive_set_error(a, errno, + "Failed to get ACL permission set"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + if (acl_clear_perms(acl_permset) != 0) { + archive_set_error(a, errno, + "Failed to clear ACL permissions"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + perm_map_size = acl_nfs4_perm_map_size; + perm_map = acl_nfs4_perm_map; + } else { +#endif + perm_map_size = acl_posix_perm_map_size; + perm_map = acl_posix_perm_map; +#if ARCHIVE_ACL_FREEBSD_NFS4 + } +#endif + + for (i = 0; i < perm_map_size; ++i) { + if (ae_permset & perm_map[i].a_perm) { + if (acl_add_perm(acl_permset, + perm_map[i].p_perm) != 0) { + archive_set_error(a, errno, + "Failed to add ACL permission"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + } + } + +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* + * acl_get_flagset_np() fails with non-NFSv4 ACLs + */ + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { + archive_set_error(a, errno, + "Failed to get flagset from an NFSv4 " + "ACL entry"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + if (acl_clear_flags_np(acl_flagset) != 0) { + archive_set_error(a, errno, + "Failed to clear flags from an NFSv4 " + "ACL flagset"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + for (i = 0; i < acl_nfs4_flag_map_size; ++i) { + if (ae_permset & acl_nfs4_flag_map[i].a_perm) { + if (acl_add_flag_np(acl_flagset, + acl_nfs4_flag_map[i].p_perm) != 0) { + archive_set_error(a, errno, + "Failed to add flag to " + "NFSv4 ACL flagset"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + } + } + } +#endif + } + + /* Try restoring the ACL through 'fd' if we can. */ + if (fd >= 0) { + if (acl_set_fd_np(fd, acl, acl_type) == 0) + ret = ARCHIVE_OK; + else { + if (errno == EOPNOTSUPP) { + /* Filesystem doesn't support ACLs */ + ret = ARCHIVE_OK; + } else { + archive_set_error(a, errno, + "Failed to set acl on fd: %s", tname); + ret = ARCHIVE_WARN; + } + } + } +#if HAVE_ACL_SET_LINK_NP + else if (acl_set_link_np(name, acl_type, acl) != 0) +#else + /* FreeBSD older than 8.0 */ + else if (acl_set_file(name, acl_type, acl) != 0) +#endif + { + if (errno == EOPNOTSUPP) { + /* Filesystem doesn't support ACLs */ + ret = ARCHIVE_OK; + } else { + archive_set_error(a, errno, "Failed to set acl: %s", + tname); + ret = ARCHIVE_WARN; + } + } +exit_free: + acl_free(acl); + return (ret); +} + +int +archive_read_disk_entry_setup_acls(struct archive_read_disk *a, + struct archive_entry *entry, int *fd) +{ + const char *accpath; + acl_t acl; + int r; + + accpath = NULL; + + if (*fd < 0) { + accpath = archive_read_disk_entry_setup_path(a, entry, fd); + if (accpath == NULL) + return (ARCHIVE_WARN); + } + + archive_entry_acl_clear(entry); + + acl = NULL; + +#if ARCHIVE_ACL_FREEBSD_NFS4 + /* Try NFSv4 ACL first. */ + if (*fd >= 0) + acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4); + else if (!a->follow_symlinks) + acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); + else + acl = acl_get_file(accpath, ACL_TYPE_NFS4); + + /* Ignore "trivial" ACLs that just mirror the file mode. */ + if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { + acl_free(acl); + acl = NULL; + return (ARCHIVE_OK); + } + + if (acl != NULL) { + r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); + acl_free(acl); + acl = NULL; + + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "Couldn't translate NFSv4 ACLs"); + } + + return (r); + } +#endif + + /* Retrieve access ACL from file. */ + if (*fd >= 0) + acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS); +#if HAVE_ACL_GET_LINK_NP + else if (!a->follow_symlinks) + acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); +#else + else if ((!a->follow_symlinks) + && (archive_entry_filetype(entry) == AE_IFLNK)) + /* We can't get the ACL of a symlink, so we assume it can't + have one. */ + acl = NULL; +#endif + else + acl = acl_get_file(accpath, ACL_TYPE_ACCESS); + +#if HAVE_ACL_IS_TRIVIAL_NP + /* Ignore "trivial" ACLs that just mirror the file mode. */ + if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { + acl_free(acl); + acl = NULL; + } +#endif + + if (acl != NULL) { + r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + acl_free(acl); + acl = NULL; + + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "Couldn't translate access ACLs"); + return (r); + } + } + + /* Only directories can have default ACLs. */ + if (S_ISDIR(archive_entry_mode(entry))) { + if (*fd >= 0) + acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT); + else + acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); + if (acl != NULL) { + r = translate_acl(a, entry, acl, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + acl_free(acl); + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "Couldn't translate default ACLs"); + return (r); + } + } + } + return (ARCHIVE_OK); +} + +int +archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl, __LA_MODE_T mode) +{ + int ret = ARCHIVE_OK; + + (void)mode; /* UNUSED */ + + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); + if (ret != ARCHIVE_OK) + return (ret); + } + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + + /* Simultaneous POSIX.1e and NFSv4 is not supported */ + return (ret); + } +#if ARCHIVE_ACL_FREEBSD_NFS4 + else if ((archive_acl_types(abstract_acl) & + ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); + } +#endif + return (ret); +} +#endif /* ARCHIVE_ACL_FREEBSD */ Property changes on: stable/10/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/archive_entry.3 =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_entry.3 (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_entry.3 (revision 316338) @@ -1,150 +1,150 @@ .\" Copyright (c) 2003-2007 Tim Kientzle .\" Copyright (c) 2010 Joerg Sonnenberger .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 Feburary 2, 2012 +.Dd February 2, 2012 .Dt ARCHIVE_ENTRY 3 .Os .Sh NAME .Nm archive_entry_clear , .Nm archive_entry_clone , .Nm archive_entry_free , .Nm archive_entry_new , .Nd functions for managing archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive_entry.h .Ft "struct archive_entry *" .Fn archive_entry_clear "struct archive_entry *" .Ft struct archive_entry * .Fn archive_entry_clone "struct archive_entry *" .Ft void .Fn archive_entry_free "struct archive_entry *" .Ft struct archive_entry * .Fn archive_entry_new "void" .Sh DESCRIPTION These functions create and manipulate data objects that represent entries within an archive. You can think of a .Tn struct archive_entry as a heavy-duty version of .Tn struct stat : it includes everything from .Tn struct stat plus associated pathname, textual group and user names, etc. These objects are used by .Xr libarchive 3 to represent the metadata associated with a particular entry in an archive. .Ss Create and Destroy There are functions to allocate, destroy, clear, and copy .Va archive_entry objects: .Bl -tag -compact -width indent .It Fn archive_entry_clear Erases the object, resetting all internal fields to the same state as a newly-created object. This is provided to allow you to quickly recycle objects without thrashing the heap. .It Fn archive_entry_clone A deep copy operation; all text fields are duplicated. .It Fn archive_entry_free Releases the .Tn struct archive_entry object. .It Fn archive_entry_new Allocate and return a blank .Tn struct archive_entry object. .El .Ss Function groups Due to high number of functions, the accessor functions can be found in man pages grouped by the purpose. .Bl -tag -width ".Xr archive_entry_perms 3" .It Xr archive_entry_acl 3 Access Control List manipulation .It Xr archive_entry_paths 3 Path name manipulation .It Xr archive_entry_perms 3 User, group and mode manipulation .It Xr archive_entry_stat 3 Functions not in the other groups and copying to/from .Vt struct stat . .It Xr archive_entry_time 3 Time field manipulation .El .Pp Most of the functions set or read entries in an object. Such functions have one of the following forms: .Bl -tag -compact -width indent .It Fn archive_entry_set_XXXX Stores the provided data in the object. In particular, for strings, the pointer is stored, not the referenced string. .It Fn archive_entry_copy_XXXX As above, except that the referenced data is copied into the object. .It Fn archive_entry_XXXX Returns the specified data. In the case of strings, a const-qualified pointer to the string is returned. .El String data can be set or accessed as wide character strings or normal .Va char strings. The functions that use wide character strings are suffixed with .Cm _w . Note that these are different representations of the same data: For example, if you store a narrow string and read the corresponding wide string, the object will transparently convert formats using the current locale. Similarly, if you store a wide string and then store a narrow string for the same data, the previously-set wide string will be discarded in favor of the new data. .Pp .\" .Sh EXAMPLE .\" .Sh RETURN VALUES .\" .Sh ERRORS .Sh SEE ALSO .Xr archive_entry_acl 3 , .Xr archive_entry_paths 3 , .Xr archive_entry_perms 3 , .Xr archive_entry_time 3 .Xr libarchive 3 , .Sh HISTORY The .Nm libarchive library first appeared in .Fx 5.3 . .Sh AUTHORS .An -nosplit The .Nm libarchive library was written by .An Tim Kientzle Aq kientzle@acm.org . .\" .Sh BUGS Index: stable/10/contrib/libarchive/libarchive/archive_entry.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_entry.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_entry.c (revision 316338) @@ -1,2035 +1,2038 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #if MAJOR_IN_MKDEV #include #define HAVE_MAJOR #elif MAJOR_IN_SYSMACROS #include #define HAVE_MAJOR #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* for Linux file flags */ #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" #include "archive_acl_private.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_entry_private.h" #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) #endif /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define ae_makedev(maj, min) makedev((maj), (min)) #endif /* * This adjustment is needed to support the following idiom for adding * 1000ns to the stored time: * archive_entry_set_atime(archive_entry_atime(), * archive_entry_atime_nsec() + 1000) * The additional if() here compensates for ambiguity in the C standard, * which permits two possible interpretations of a % b when a is negative. */ #define FIX_NS(t,ns) \ do { \ t += ns / 1000000000; \ ns %= 1000000000; \ if (ns < 0) { --t; ns += 1000000000; } \ } while (0) static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p != L'\0') ++p; return p - s; } #endif #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); archive_mstring_clean(&entry->ae_fflags_text); archive_mstring_clean(&entry->ae_gname); archive_mstring_clean(&entry->ae_hardlink); archive_mstring_clean(&entry->ae_pathname); archive_mstring_clean(&entry->ae_sourcepath); archive_mstring_clean(&entry->ae_symlink); archive_mstring_clean(&entry->ae_uname); archive_entry_copy_mac_metadata(entry, NULL, 0); archive_acl_clear(&entry->acl); archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); memset(entry, 0, sizeof(*entry)); return entry; } struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; struct ae_xattr *xp; struct ae_sparse *sp; size_t s; const void *p; /* Allocate new structure and copy over all of the fields. */ /* TODO: Should we copy the archive over? Or require a new archive * as an argument? */ entry2 = archive_entry_new2(entry->archive); if (entry2 == NULL) return (NULL); entry2->ae_stat = entry->ae_stat; entry2->ae_fflags_set = entry->ae_fflags_set; entry2->ae_fflags_clear = entry->ae_fflags_clear; /* TODO: XXX If clone can have a different archive, what do we do here if * character sets are different? XXX */ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname); archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink); archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname); archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy encryption status */ entry2->encryption = entry->encryption; /* Copy ACL data over. */ archive_acl_copy(&entry2->acl, &entry->acl); /* Copy Mac OS metadata. */ p = archive_entry_mac_metadata(entry, &s); archive_entry_copy_mac_metadata(entry2, p, s); /* Copy xattr data over. */ xp = entry->xattr_head; while (xp != NULL) { archive_entry_xattr_add_entry(entry2, xp->name, xp->value, xp->size); xp = xp->next; } /* Copy sparse data over. */ sp = entry->sparse_head; while (sp != NULL) { archive_entry_sparse_add_entry(entry2, sp->offset, sp->length); sp = sp->next; } return (entry2); } void archive_entry_free(struct archive_entry *entry) { archive_entry_clear(entry); free(entry); } struct archive_entry * archive_entry_new(void) { return archive_entry_new2(NULL); } struct archive_entry * archive_entry_new2(struct archive *a) { struct archive_entry *entry; entry = (struct archive_entry *)calloc(1, sizeof(*entry)); if (entry == NULL) return (NULL); entry->archive = a; return (entry); } /* * Functions for reading fields from an archive_entry. */ time_t archive_entry_atime(struct archive_entry *entry) { return (entry->ae_stat.aest_atime); } long archive_entry_atime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_atime_nsec); } int archive_entry_atime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_ATIME); } time_t archive_entry_birthtime(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime); } long archive_entry_birthtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime_nsec); } int archive_entry_birthtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_BIRTHTIME); } time_t archive_entry_ctime(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime); } int archive_entry_ctime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_CTIME); } long archive_entry_ctime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime_nsec); } dev_t archive_entry_dev(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return ae_makedev(entry->ae_stat.aest_devmajor, entry->ae_stat.aest_devminor); else return (entry->ae_stat.aest_dev); } int archive_entry_dev_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_DEV); } dev_t archive_entry_devmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devmajor); else return major(entry->ae_stat.aest_dev); } dev_t archive_entry_devminor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devminor); else return minor(entry->ae_stat.aest_dev); } mode_t archive_entry_filetype(struct archive_entry *entry) { return (AE_IFMT & entry->acl.mode); } void archive_entry_fflags(struct archive_entry *entry, unsigned long *set, unsigned long *clear) { *set = entry->ae_fflags_set; *clear = entry->ae_fflags_clear; } /* * Note: if text was provided, this just returns that text. If you * really need the text to be rebuilt in a canonical form, set the * text, ask for the bitmaps, then set the bitmaps. (Setting the * bitmaps clears any stored text.) This design is deliberate: if * we're editing archives, we don't want to discard flags just because * they aren't supported on the current system. The bitmap<->text * conversions are platform-specific (see below). */ const char * archive_entry_fflags_text(struct archive_entry *entry) { const char *f; char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) { if (f != NULL) return (f); } else if (errno == ENOMEM) __archive_errx(1, "No memory"); if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) return (NULL); p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); if (p == NULL) return (NULL); archive_mstring_copy_mbs(&entry->ae_fflags_text, p); free(p); if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) return (f); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } la_int64_t archive_entry_gid(struct archive_entry *entry) { return (entry->ae_stat.aest_gid); } const char * archive_entry_gname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_gname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_gname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc)); } const char * archive_entry_hardlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_hardlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_hardlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_HARDLINK) == 0) { *p = NULL; *len = 0; return (0); } return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc)); } la_int64_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } int archive_entry_ino_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_INO); } la_int64_t archive_entry_ino64(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->acl.mode); } time_t archive_entry_mtime(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime); } long archive_entry_mtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime_nsec); } int archive_entry_mtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_MTIME); } unsigned int archive_entry_nlink(struct archive_entry *entry) { return (entry->ae_stat.aest_nlink); } const char * archive_entry_pathname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_pathname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_pathname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc)); } mode_t archive_entry_perm(struct archive_entry *entry) { return (~AE_IFMT & entry->acl.mode); } dev_t archive_entry_rdev(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return ae_makedev(entry->ae_stat.aest_rdevmajor, entry->ae_stat.aest_rdevminor); else return (entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevmajor); else return major(entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevminor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevminor); else return minor(entry->ae_stat.aest_rdev); } la_int64_t archive_entry_size(struct archive_entry *entry) { return (entry->ae_stat.aest_size); } int archive_entry_size_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_SIZE); } const char * archive_entry_sourcepath(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_sourcepath_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); return (NULL); } const char * archive_entry_symlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_symlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_symlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_SYMLINK) == 0) { *p = NULL; *len = 0; return (0); } return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc)); } la_int64_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_uid); } const char * archive_entry_uname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_uname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_uname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc)); } int archive_entry_is_data_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA); } int archive_entry_is_metadata_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA); } int archive_entry_is_encrypted(struct archive_entry *entry) { return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA)); } /* * Functions to set archive_entry properties. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->acl.mode &= ~AE_IFMT; entry->acl.mode |= AE_IFMT & type; } void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { archive_mstring_clean(&entry->ae_fflags_text); entry->ae_fflags_set = set; entry->ae_fflags_clear = clear; } const char * archive_entry_copy_fflags_text(struct archive_entry *entry, const char *flags) { archive_mstring_copy_mbs(&entry->ae_fflags_text, flags); return (ae_strtofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) { archive_mstring_copy_wcs(&entry->ae_fflags_text, flags); return (ae_wcstofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } void archive_entry_set_gid(struct archive_entry *entry, la_int64_t g) { entry->stat_valid = 0; entry->ae_stat.aest_gid = g; } void archive_entry_set_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_gname, name); } void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_gname, name); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_gname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_gname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc)); } void archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target) { archive_mstring_copy_utf8(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { archive_mstring_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } int archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target) { if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_hardlink_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); if (target != NULL && r == 0) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; return (r); } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_ATIME; entry->ae_stat.aest_atime = t; entry->ae_stat.aest_atime_nsec = ns; } void archive_entry_unset_atime(struct archive_entry *entry) { archive_entry_set_atime(entry, 0, 0); entry->ae_set &= ~AE_SET_ATIME; } void archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = t; entry->ae_stat.aest_birthtime_nsec = ns; } void archive_entry_unset_birthtime(struct archive_entry *entry) { archive_entry_set_birthtime(entry, 0, 0); entry->ae_set &= ~AE_SET_BIRTHTIME; } void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_CTIME; entry->ae_stat.aest_ctime = t; entry->ae_stat.aest_ctime_nsec = ns; } void archive_entry_unset_ctime(struct archive_entry *entry) { archive_entry_set_ctime(entry, 0, 0); entry->ae_set &= ~AE_SET_CTIME; } void archive_entry_set_dev(struct archive_entry *entry, dev_t d) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 0; entry->ae_stat.aest_dev = d; } void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devmajor = m; } void archive_entry_set_devminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devminor = m; } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_set_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } void archive_entry_set_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_utf8(&entry->ae_symlink, target); else archive_mstring_copy_utf8(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_wcs(&entry->ae_symlink, target); else archive_mstring_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, target); else r = archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target); if (r == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_link_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, target, len, sc); else r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); return (r); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->acl.mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = t; entry->ae_stat.aest_mtime_nsec = ns; } void archive_entry_unset_mtime(struct archive_entry *entry) { archive_entry_set_mtime(entry, 0, 0); entry->ae_set &= ~AE_SET_MTIME; } void archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) { entry->stat_valid = 0; entry->ae_stat.aest_nlink = nlink; } void archive_entry_set_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_pathname, name); } void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_pathname, name); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_pathname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_pathname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname, name, len, sc)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; entry->acl.mode |= ~AE_IFMT & p; } void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; } void archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevmajor = m; } void archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevminor = m; } void archive_entry_set_size(struct archive_entry *entry, la_int64_t s) { entry->stat_valid = 0; entry->ae_stat.aest_size = s; entry->ae_set |= AE_SET_SIZE; } void archive_entry_unset_size(struct archive_entry *entry) { archive_entry_set_size(entry, 0); entry->ae_set &= ~AE_SET_SIZE; } void archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) { archive_mstring_copy_mbs(&entry->ae_sourcepath, path); } void archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path) { archive_mstring_copy_wcs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_utf8(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { archive_mstring_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } int archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname) { if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, linkname) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_symlink_l(struct archive_entry *entry, const char *linkname, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, linkname, len, sc); if (linkname != NULL && r == 0) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; return (r); } void archive_entry_set_uid(struct archive_entry *entry, la_int64_t u) { entry->stat_valid = 0; entry->ae_stat.aest_uid = u; } void archive_entry_set_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_uname, name); } void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_uname, name); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_uname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } void archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_DATA; } else { entry->encryption &= ~AE_ENCRYPTION_DATA; } } void archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_METADATA; } else { entry->encryption &= ~AE_ENCRYPTION_METADATA; } } int _archive_entry_copy_uname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_uname, name, len, sc)); } const void * archive_entry_mac_metadata(struct archive_entry *entry, size_t *s) { *s = entry->mac_metadata_size; return entry->mac_metadata; } void archive_entry_copy_mac_metadata(struct archive_entry *entry, const void *p, size_t s) { free(entry->mac_metadata); if (p == NULL || s == 0) { entry->mac_metadata = NULL; entry->mac_metadata_size = 0; } else { entry->mac_metadata_size = s; entry->mac_metadata = malloc(s); if (entry->mac_metadata == NULL) abort(); memcpy(entry->mac_metadata, p, s); } } /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and * complete standard that addressed the needs of ACL archiving and 2) * everyone followed it faithfully. Alas, neither is true, so the * following is a lot more complex than might seem necessary to the * uninitiated. */ struct archive_acl * archive_entry_acl(struct archive_entry *entry) { return &entry->acl; } void archive_entry_acl_clear(struct archive_entry *entry) { archive_acl_clear(&entry->acl); } /* * Add a single ACL entry to the internal list of ACL data. */ int archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name); } /* * As above, but with a wide-character name. */ int archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { return archive_acl_add_entry_w_len(&entry->acl, type, permset, tag, id, name, wcslen(name)); } /* * Return a bitmask of ACL types in an archive entry ACL list */ int archive_entry_acl_types(struct archive_entry *entry) { return (archive_acl_types(&entry->acl)); } /* * Return a count of entries matching "want_type". */ int archive_entry_acl_count(struct archive_entry *entry, int want_type) { return archive_acl_count(&entry->acl, want_type); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_entry_acl_reset(struct archive_entry *entry, int want_type) { return archive_acl_reset(&entry->acl, want_type); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { int r; r = archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name); if (r == ARCHIVE_FATAL && errno == ENOMEM) __archive_errx(1, "No memory"); return (r); } /* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ wchar_t * archive_entry_acl_to_text_w(struct archive_entry *entry, ssize_t *len, int flags) { return (archive_acl_to_text_w(&entry->acl, len, flags, entry->archive)); } char * archive_entry_acl_to_text(struct archive_entry *entry, ssize_t *len, int flags) { return (archive_acl_to_text_l(&entry->acl, len, flags, NULL)); } char * _archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len, int flags, struct archive_string_conv *sc) { return (archive_acl_to_text_l(&entry->acl, len, flags, sc)); } /* * ACL text parser. */ int archive_entry_acl_from_text_w(struct archive_entry *entry, const wchar_t *wtext, int type) { return (archive_acl_from_text_w(&entry->acl, wtext, type)); } int archive_entry_acl_from_text(struct archive_entry *entry, const char *text, int type) { return (archive_acl_from_text_l(&entry->acl, text, type, NULL)); } int _archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text, int type, struct archive_string_conv *sc) { return (archive_acl_from_text_l(&entry->acl, text, type, sc)); } /* Deprecated */ static int archive_entry_acl_text_compat(int *flags) { if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0) return (1); /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) *flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID; /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) *flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; *flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA; return (0); } /* Deprecated */ const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { if (entry->acl.acl_text_w != NULL) { free(entry->acl.acl_text_w); entry->acl.acl_text_w = NULL; } if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl, NULL, flags, entry->archive); return (entry->acl.acl_text_w); } /* Deprecated */ const char * archive_entry_acl_text(struct archive_entry *entry, int flags) { if (entry->acl.acl_text != NULL) { free(entry->acl.acl_text); entry->acl.acl_text = NULL; } if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL, flags, NULL); return (entry->acl.acl_text); } /* Deprecated */ int _archive_entry_acl_text_l(struct archive_entry *entry, int flags, const char **acl_text, size_t *len, struct archive_string_conv *sc) { if (entry->acl.acl_text != NULL) { free(entry->acl.acl_text); entry->acl.acl_text = NULL; } if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, (ssize_t *)len, flags, sc); *acl_text = entry->acl.acl_text; return (0); } /* * Following code is modified from UC Berkeley sources, and * is subject to the following copyright notice. */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -static struct flag { +static const struct flag { const char *name; const wchar_t *wname; unsigned long set; unsigned long clear; } flags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND { "nosappnd", L"nosappnd", SF_APPEND, 0 }, { "nosappend", L"nosappend", SF_APPEND, 0 }, #endif #if defined(FS_APPEND_FL) /* 'a' */ { "nosappnd", L"nosappnd", FS_APPEND_FL, 0 }, { "nosappend", L"nosappend", FS_APPEND_FL, 0 }, #elif defined(EXT2_APPEND_FL) /* 'a' */ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, #endif #ifdef SF_ARCHIVED { "noarch", L"noarch", SF_ARCHIVED, 0 }, { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, #endif #ifdef SF_IMMUTABLE { "noschg", L"noschg", SF_IMMUTABLE, 0 }, { "noschange", L"noschange", SF_IMMUTABLE, 0 }, { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, #endif #if defined(FS_IMMUTABLE_FL) /* 'i' */ { "noschg", L"noschg", FS_IMMUTABLE_FL, 0 }, { "noschange", L"noschange", FS_IMMUTABLE_FL, 0 }, { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0 }, #elif defined(EXT2_IMMUTABLE_FL) /* 'i' */ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, #endif #ifdef SF_NOUNLINK { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, #endif #ifdef SF_SNAPSHOT { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, #endif #ifdef UF_APPEND { "nouappnd", L"nouappnd", UF_APPEND, 0 }, { "nouappend", L"nouappend", UF_APPEND, 0 }, #endif #ifdef UF_IMMUTABLE { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #if defined(FS_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, FS_NODUMP_FL}, #elif defined(EXT2_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE { "noopaque", L"noopaque", UF_OPAQUE, 0 }, #endif #ifdef UF_NOUNLINK { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, #endif #ifdef UF_COMPRESSED { "nocompressed",L"nocompressed", UF_COMPRESSED, 0 }, #endif +#ifdef UF_HIDDEN + { "nohidden", L"nohidden", UF_HIDDEN, 0 }, +#endif #if defined(FS_UNRM_FL) { "nouunlink", L"nouunlink", FS_UNRM_FL, 0}, #elif defined(EXT2_UNRM_FL) { "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0}, #endif #if defined(FS_BTREE_FL) { "nobtree", L"nobtree", FS_BTREE_FL, 0 }, #elif defined(EXT2_BTREE_FL) { "nobtree", L"nobtree", EXT2_BTREE_FL, 0 }, #endif #if defined(FS_ECOMPR_FL) { "nocomperr", L"nocomperr", FS_ECOMPR_FL, 0 }, #elif defined(EXT2_ECOMPR_FL) { "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 }, #endif #if defined(FS_COMPR_FL) /* 'c' */ { "nocompress", L"nocompress", FS_COMPR_FL, 0 }, #elif defined(EXT2_COMPR_FL) /* 'c' */ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, #endif #if defined(FS_NOATIME_FL) /* 'A' */ { "noatime", L"noatime", 0, FS_NOATIME_FL}, #elif defined(EXT2_NOATIME_FL) /* 'A' */ { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif #if defined(FS_DIRTY_FL) { "nocompdirty",L"nocompdirty", FS_DIRTY_FL, 0}, #elif defined(EXT2_DIRTY_FL) { "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0}, #endif #if defined(FS_COMPRBLK_FL) #if defined(FS_NOCOMPR_FL) { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, FS_NOCOMPR_FL}, #else { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, 0}, #endif #elif defined(EXT2_COMPRBLK_FL) #if defined(EXT2_NOCOMPR_FL) { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL}, #else { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0}, #endif #endif #if defined(FS_DIRSYNC_FL) { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, #elif defined(EXT2_DIRSYNC_FL) { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, #endif #if defined(FS_INDEX_FL) { "nohashidx", L"nohashidx", FS_INDEX_FL, 0}, #elif defined(EXT2_INDEX_FL) { "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0}, #endif #if defined(FS_IMAGIC_FL) { "noimagic", L"noimagic", FS_IMAGIC_FL, 0}, #elif defined(EXT2_IMAGIC_FL) { "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0}, #endif #if defined(FS_JOURNAL_DATA_FL) { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, #elif defined(EXT3_JOURNAL_DATA_FL) { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, #endif #if defined(FS_SECRM_FL) { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, #elif defined(EXT2_SECRM_FL) { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, #endif #if defined(FS_SYNC_FL) { "nosync", L"nosync", FS_SYNC_FL, 0}, #elif defined(EXT2_SYNC_FL) { "nosync", L"nosync", EXT2_SYNC_FL, 0}, #endif #if defined(FS_NOTAIL_FL) { "notail", L"notail", 0, FS_NOTAIL_FL}, #elif defined(EXT2_NOTAIL_FL) { "notail", L"notail", 0, EXT2_NOTAIL_FL}, #endif #if defined(FS_TOPDIR_FL) { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, #elif defined(EXT2_TOPDIR_FL) { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, #endif #ifdef FS_ENCRYPT_FL { "noencrypt", L"noencrypt", FS_ENCRYPT_FL, 0}, #endif #ifdef FS_HUGE_FILE_FL { "nohugefile", L"nohugefile", FS_HUGE_FILE_FL, 0}, #endif #ifdef FS_EXTENT_FL { "noextent", L"noextent", FS_EXTENT_FL, 0}, #endif #ifdef FS_EA_INODE_FL { "noeainode", L"noeainode", FS_EA_INODE_FL, 0}, #endif #ifdef FS_EOFBLOCKS_FL { "noeofblocks",L"noeofblocks", FS_EOFBLOCKS_FL, 0}, #endif #ifdef FS_NOCOW_FL { "nocow", L"nocow", FS_NOCOW_FL, 0}, #endif #ifdef FS_INLINE_DATA_FL { "noinlinedata",L"noinlinedata", FS_INLINE_DATA_FL, 0}, #endif #ifdef FS_PROJINHERIT_FL { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, #endif #if defined(FS_RESERVED_FL) { "noreserved", L"noreserved", FS_RESERVED_FL, 0}, #elif defined(EXT2_RESERVED_FL) { "noreserved", L"noreserved", EXT2_RESERVED_FL, 0}, #endif { NULL, NULL, 0, 0 } }; /* * fflagstostr -- * Convert file flags to a comma-separated string. If no flags * are set, return the empty string. */ static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; const char *sp; unsigned long bits; - struct flag *flag; + const struct flag *flag; size_t length; bits = bitset | bitclear; length = 0; for (flag = flags; flag->name != NULL; flag++) if (bits & (flag->set | flag->clear)) { length += strlen(flag->name) + 1; bits &= ~(flag->set | flag->clear); } if (length == 0) return (NULL); string = (char *)malloc(length); if (string == NULL) return (NULL); dp = string; for (flag = flags; flag->name != NULL; flag++) { if (bitset & flag->set || bitclear & flag->clear) { sp = flag->name + 2; } else if (bitset & flag->clear || bitclear & flag->set) { sp = flag->name; } else continue; bitset &= ~(flag->set | flag->clear); bitclear &= ~(flag->set | flag->clear); if (dp > string) *dp++ = ','; while ((*dp++ = *sp++) != '\0') ; dp--; } *dp = '\0'; return (string); } /* * strtofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const char * ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) { const char *start, *end; - struct flag *flag; + const struct flag *flag; unsigned long set, clear; const char *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { size_t length; /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; length = end - start; for (flag = flags; flag->name != NULL; flag++) { size_t flag_length = strlen(flag->name); if (length == flag_length && memcmp(start, flag->name, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && memcmp(start, flag->name + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->name == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == '\t' || *start == ' ' || *start == ',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } /* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const wchar_t * ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) { const wchar_t *start, *end; - struct flag *flag; + const struct flag *flag; unsigned long set, clear; const wchar_t *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == L'\t' || *start == L' ' || *start == L',') start++; while (*start != L'\0') { size_t length; /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; length = end - start; for (flag = flags; flag->wname != NULL; flag++) { size_t flag_length = wcslen(flag->wname); if (length == flag_length && wmemcmp(start, flag->wname, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && wmemcmp(start, flag->wname + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->wname == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == L'\t' || *start == L' ' || *start == L',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } #ifdef TEST #include int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); } #endif Index: stable/10/contrib/libarchive/libarchive/archive_entry_acl.3 =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_entry_acl.3 (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_entry_acl.3 (revision 316338) @@ -1,466 +1,466 @@ .\" Copyright (c) 2010 Joerg Sonnenberger .\" 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 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. .\" .Dd February 15, 2017 .Dt ARCHIVE_ENTRY_ACL 3 .Os .Sh NAME .Nm archive_entry_acl_add_entry , .Nm archive_entry_acl_add_entry_w , .Nm archive_entry_acl_clear , .Nm archive_entry_acl_count , .Nm archive_entry_acl_from_text , .Nm archive_entry_acl_from_text_w , .Nm archive_entry_acl_next , .Nm archive_entry_acl_next_w , .Nm archive_entry_acl_reset , .Nm archive_entry_acl_to_text , .Nm archive_entry_acl_to_text_w , .Nm archive_entry_acl_types .Nd functions for manipulating Access Control Lists in archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive_entry.h .Ft void .Fo archive_entry_acl_add_entry .Fa "struct archive_entry *a" .Fa "int type" .Fa "int permset" .Fa "int tag" .Fa "int qualifier" .Fa "const char *name" .Fc .Ft void .Fo archive_entry_acl_add_entry_w .Fa "struct archive_entry *a" .Fa "int type" .Fa "int permset" .Fa "int tag" .Fa "int qualifier" .Fa "const wchar_t *name" .Fc .Ft void .Fn archive_entry_acl_clear "struct archive_entry *a" .Ft int .Fn archive_entry_acl_count "struct archive_entry *a" "int type" .Ft int .Fo archive_entry_acl_from_text .Fa "struct archive_entry *a" .Fa "const char *text" .Fa "int type" .Fc .Ft int .Fo archive_entry_acl_from_text_w .Fa "struct archive_entry *a" .Fa "const wchar_t *text" .Fa "int type" .Fc .Ft int .Fo archive_entry_acl_next .Fa "struct archive_entry *a" .Fa "int type" .Fa "int *ret_type" .Fa "int *ret_permset" .Fa "int *ret_tag" .Fa "int *ret_qual" .Fa "const char **ret_name" .Fc .Ft int .Fo archive_entry_acl_next_w .Fa "struct archive_entry *a" .Fa "int type" .Fa "int *ret_type" .Fa "int *ret_permset" .Fa "int *ret_tag" .Fa "int *ret_qual" .Fa "const wchar_t **ret_name" .Fc .Ft int .Fn archive_entry_acl_reset "struct archive_entry *a" "int type" .Ft char * .Fo archive_entry_acl_to_text .Fa "struct archive_entry *a" .Fa "ssize_t *len_p" .Fa "int flags" .Fc .Ft wchar_t * .Fo archive_entry_acl_to_text_w .Fa "struct archive_entry *a" .Fa "ssize_t *len_p" .Fa "int flags" .Fc .Ft int .Fn archive_entry_acl_types "struct archive_entry *a" .\" enum? .Sh DESCRIPTION The .Dq Access Control Lists (ACLs) extend the standard Unix perssion model. The ACL interface of .Nm libarchive supports both POSIX.1e and NFSv4 style ACLs. Use of ACLs is restricted by various levels of ACL support in operating systems, file systems and archive formats. .Ss POSIX.1e Access Control Lists A POSIX.1e ACL consists of a number of independent entries. Each entry specifies the permission set as bitmask of basic permissions. Valid permissions in the .Fa permset are: .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_EXECUTE" .It Dv ARCHIVE_ENTRY_ACL_READ ( Sy r ) .It Dv ARCHIVE_ENTRY_ACL_WRITE ( Sy w ) .It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x ) .El The permissions correspond to the normal Unix permissions. .Pp The .Fa tag specifies the principal to which the permission applies. Valid values are: .Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ" .It Dv ARCHIVE_ENTRY_ACL_USER The user specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ The owner of the file. .It Dv ARCHIVE_ENTRY_ACL_GROUP The group specied by the name field. .It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ The group who owns the file. .It Dv ARCHIVE_ENTRY_ACL_MASK The maximum permissions to be obtained via group permissions. .It Dv ARCHIVE_ENTRY_ACL_OTHER Any principal who is not file owner or a member of the owning group. .El .Pp The principals .Dv ARCHIVE_ENTRY_ACL_USER_OBJ , .Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ and .Dv ARCHIVE_ENTRY_ACL_OTHER are equivalent to user, group and other in the classic Unix permission model and specify non-extended ACL entries. .Pp All files with have an access ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS . This specifies the permissions required for access to the file itself. Directories have an additional ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT , which controls the initial access ACL for newly created directory entries. .Ss NFSv4 Access Control Lists A NFSv4 ACL consists of multiple individual entries called Access Control Entries (ACEs). .Pp There are four possible types of a NFSv4 ACE: .Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYE_ALLOW" .It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW Allow principal to perform actions requiring given permissions. .It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY Prevent principal from performing actions requiring given permissions. .It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT Log access attempts by principal which require given permissions. .It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM Trigger a system alarm on access attempts by principal which require given permissions. .El .Pp The .Fa tag specifies the principal to which the permission applies. Valid values are: .Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ" .It Dv ARCHIVE_ENTRY_ACL_USER The user specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ The owner of the file. .It Dv ARCHIVE_ENTRY_ACL_GROUP The group specied by the name field. .It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ The group who owns the file. .It Dv ARCHIVE_ENTRY_ACL_EVERYONE Any principal who is not file owner or a member of the owning group. .El .Pp Entries with the .Dv ARCHIVE_ENTRY_ACL_USER or .Dv ARCHIVE_ENTRY_ACL_GROUP tag store the user and group name in the .Fa name string and optionally the user or group ID in the .Fa qualifier integer. .Pp NFSv4 ACE permissions and flags are stored in the same .Fa permset bitfield. Some permissions share the same constant and permission character but have different effect on directories than on files. The following ACE permissions are supported: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_READ_DATA ( Sy r ) Read data (file). .It Dv ARCHIVE_ENTRY_ACL_LIST_DIRECTORY ( Sy r ) List entries (directory). .It ARCHIVE_ENTRY_ACL_WRITE_DATA ( Sy w ) Write data (file). .It ARCHIVE_ENTRY_ACL_ADD_FILE ( Sy w ) Create files (directory). .It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x ) Execute file or change into a directory. .It Dv ARCHIVE_ENTRY_ACL_APPEND_DATA ( Sy p ) Append data (file). .It Dv ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY ( Sy p ) Create subdirectories (directory). .It Dv ARCHIVE_ENTRY_ACL_DELETE_CHILD ( Sy D ) Remove files and subdirectories inside a directory. .It Dv ARCHIVE_ENTRY_ACL_DELETE ( Sy d ) Remove file or directory. .It Dv ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES ( Sy a ) Read file or directory attributes. .It Dv ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES ( Sy A ) Write file or directory attributes. .It Dv ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS ( Sy R ) Read named file or directory attributes. .It Dv ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS ( Sy W ) Write named file or directory attributes. .It Dv ARCHIVE_ENTRY_ACL_READ_ACL ( Sy c ) Read file or directory ACL. .It Dv ARCHIVE_ENTRY_ACL_WRITE_ACL ( Sy C ) Write file or directory ACL. .It Dv ARCHIVE_ENTRY_ACL_WRITE_OWNER ( Sy o ) Change owner of a file or directory. .It Dv ARCHIVE_ENTRY_ACL_SYNCHRONIZE ( Sy s ) Use synchronous I/O. .El .Pp The following NFSv4 ACL inheritance flags are supported: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT ( Sy f ) Inherit parent directory ACE to files. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT ( Sy d ) Inherit parent directory ACE to subdirectories. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY ( Sy i ) Only inherit, do not apply the permission on the directory itself. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n ) Do not propagate inherit flags. Only first-level entries inherit ACLs. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S ) -Trigger alarm or audit on succesful access. +Trigger alarm or audit on successful access. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F ) Trigger alarm or audit on failed access. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERITED ( Sy I ) Mark that ACE was inherited. .El .Ss Functions .Fn archive_entry_acl_add_entry and .Fn archive_entry_acl_add_entry_w add a single ACL entry. For the access ACL and non-extended principals, the classic Unix permissions -are updated. An archive enry cannot contain both POSIX.1e and NFSv4 ACL +are updated. An archive entry cannot contain both POSIX.1e and NFSv4 ACL entries. .Pp .Fn archive_entry_acl_clear removes all ACL entries and resets the enumeration pointer. .Pp .Fn archive_entry_acl_count counts the ACL entries that have the given type mask. .Fa type can be the bitwise-or of .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT" .It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS .It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT .El for POSIX.1e ACLs and .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_ALLOW" .It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW .It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY .It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT .It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM .El for NFSv4 ACLs. For POSIX.1e ACLs if .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS is included and at least one extended ACL entry is found, -the three non-extened ACLs are added. +the three non-extended ACLs are added. .Pp .Fn archive_entry_acl_from_text and .Fn archive_entry_acl_from_text_w add new .Pq or merge with existing ACL entries from .Pq wide text. The argument .Fa type may take one of the following values: .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT" .It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS .It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT .It Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4 .El Supports all formats that can be created with .Fn archive_entry_acl_to_text or respective .Fn archive_entry_acl_to_text_w . Existing ACL entries are preserved. To get a clean new ACL from text .Fn archive_entry_acl_clear must be called first. Entries prefixed with .Dq default: are treated as .Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless .Fa type is .Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4 . Invalid entries, non-parseable ACL entries and entries beginning with the .Sq # character .Pq comments are skipped. .Pp .Fn archive_entry_acl_next and .Fn archive_entry_acl_next_w return the next entry of the ACL list. This functions may only be called after .Fn archive_entry_acl_reset has indicated the presence of extended ACL entries. .Pp .Fn archive_entry_acl_reset prepare reading the list of ACL entries with .Fn archive_entry_acl_next or .Fn archive_entry_acl_next_w . The function returns either 0, if no non-extended ACLs are found. In this case, the access permissions should be obtained by .Xr archive_entry_mode 3 or set using .Xr chmod 2 . Otherwise, the function returns the same value as .Fn archive_entry_acl_count . .Pp .Fn archive_entry_acl_to_text and .Fn archive_entry_acl_to_text_w convert the ACL entries for the given type into a .Pq wide -string of ACL entries separated by newline. If the the pointer +string of ACL entries separated by newline. If the pointer .Fa len_p is not NULL, then the function shall return the length of the string .Pq not including the NULL terminator in the location pointed to by .Fa len_p . The .Fa flag argument is a bitwise-or. .Pp The following flags are effective only on POSIX.1e ACL: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS Output access ACLs. .It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT Output POSIX.1e default ACLs. .It Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT Prefix each default ACL entry with the word .Dq default: . .It Dv ARCHIVE_ENTRY_ACL_STYLE_SOLARIS The mask and other ACLs don not contain a double colon. .El .Pp The following flags are effecive only on NFSv4 ACL: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_STYLE_COMPACT Do not output minus characters for unset permissions and flags in NFSv4 ACL permission and flag fields. .El .Pp The following flags are effective on both POSIX.1e and NFSv4 ACL: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID Add an additional colon-separated field containing the user or group id. .It Dv ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA Separate ACL entries with comma instead of newline. .El .Pp If the archive entry contains NFSv4 ACLs, all types of NFSv4 ACLs are returned. It the entry contains POSIX.1e ACLs and none of the flags .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS or .Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT are specified, both access and default entries are returned and default entries are prefixed with .Dq default: . .Pp .Fn archive_entry_acl_types get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4 ACL entries cannot be mixed, this function is a very efficient way to detect if an ACL already contains POSIX.1e or NFSv4 ACL entries. .Sh RETURN VALUES .Fn archive_entry_acl_count and .Fn archive_entry_acl_reset returns the number of ACL entries that match the given type mask. For POSIX.1e ACLS if the type mask includes .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS and at least one extended ACL entry exists, the three classic Unix permissions are counted. .Pp .Fn archive_entry_acl_from_text and .Fn archive_entry_acl_from_text_w return .Dv ARCHIVE_OK if all entries were successfully parsed and .Dv ARCHIVE_WARN if one or more entries were invalid or non-parseable. .Pp .Fn archive_entry_acl_next and .Fn archive_entry_acl_next_w return .Dv ARCHIVE_OK on success, .Dv ARCHIVE_EOF if no more ACL entries exist and .Dv ARCHIVE_WARN if .Fn archive_entry_acl_reset has not been called first. .Pp .Fn archive_entry_acl_to_text returns a string representing the ACL entries matching the given type and flags on success or NULL on error. .Pp .Fn archive_entry_acl_to_text_w returns a wide string representing the ACL entries matching the given type and flags on success or NULL on error. .Pp .Fn archive_entry_acl_types returns a bitmask of ACL entry types or 0 if archive entry has no ACL entries. .Sh SEE ALSO .Xr archive_entry 3 , .Xr libarchive 3 Index: stable/10/contrib/libarchive/libarchive/archive_getdate.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_getdate.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_getdate.c (revision 316338) @@ -1,1038 +1,1038 @@ /* * This code is in the public domain and has no copyright. * * This is a plain C recursive-descent translation of an old * public-domain YACC grammar that has been used for parsing dates in * very many open-source projects. * * Since the original authors were generous enough to donate their * work to the public domain, I feel compelled to match their * generosity. * * Tim Kientzle, February 2009. */ /* * Header comment from original getdate.y: */ /* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990; ** ** This grammar has 10 shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ #ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); #endif #include #include #include #include #include #define __LIBARCHIVE_BUILD 1 #include "archive_getdate.h" /* Basic time units. */ #define EPOCH 1970 #define MINUTE (60L) #define HOUR (60L * MINUTE) #define DAY (24L * HOUR) /* Daylight-savings mode: on, off, or not yet known. */ enum DSTMODE { DSTon, DSToff, DSTmaybe }; /* Meridian: am or pm. */ enum { tAM, tPM }; /* Token types returned by nexttoken() */ enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, tUNUMBER, tZONE, tDST }; struct token { int token; time_t value; }; /* * Parser state. */ struct gdstate { struct token *tokenp; /* Pointer to next token. */ /* HaveXxxx counts how many of this kind of phrase we've seen; * it's a fatal error to have more than one time, zone, day, * or date phrase. */ int HaveYear; int HaveMonth; int HaveDay; int HaveWeekDay; /* Day of week */ int HaveTime; /* Hour/minute/second */ int HaveZone; /* timezone and/or DST info */ int HaveRel; /* time offset; we can have more than one */ /* Absolute time values. */ time_t Timezone; /* Seconds offset from GMT */ time_t Day; time_t Hour; time_t Minutes; time_t Month; time_t Seconds; time_t Year; /* DST selection */ enum DSTMODE DSTmode; /* Day of week accounting, e.g., "3rd Tuesday" */ time_t DayOrdinal; /* "3" in "3rd Tuesday" */ time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */ /* Relative time values: hour/day/week offsets are measured in * seconds, month/year are counted in months. */ time_t RelMonth; time_t RelSeconds; }; /* * A series of functions that recognize certain common time phrases. * Each function returns 1 if it managed to make sense of some of the * tokens, zero otherwise. */ /* * hour:minute or hour:minute:second with optional AM, PM, or numeric * timezone offset */ static int timephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == ':' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == ':' && gds->tokenp[4].token == tUNUMBER) { /* "12:14:18" or "22:08:07" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->tokenp[2].value; gds->Seconds = gds->tokenp[4].value; gds->tokenp += 5; } else if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == ':' && gds->tokenp[2].token == tUNUMBER) { /* "12:14" or "22:08" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->tokenp[2].value; gds->Seconds = 0; gds->tokenp += 3; } else if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tAMPM) { /* "7" is a time if it's followed by "am" or "pm" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->Seconds = 0; /* We'll handle the AM/PM below. */ gds->tokenp += 1; } else { /* We can't handle this. */ return 0; } if (gds->tokenp[0].token == tAMPM) { /* "7:12pm", "12:20:13am" */ if (gds->Hour == 12) gds->Hour = 0; if (gds->tokenp[0].value == tPM) gds->Hour += 12; gds->tokenp += 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER) { /* "7:14+0700" */ gds->HaveZone++; gds->DSTmode = DSToff; gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR + (gds->tokenp[1].value % 100) * MINUTE); gds->tokenp += 2; } if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER) { /* "19:14:12-0530" */ gds->HaveZone++; gds->DSTmode = DSToff; gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR + (gds->tokenp[1].value % 100) * MINUTE); gds->tokenp += 2; } return 1; } /* * Timezone name, possibly including DST. */ static int zonephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tZONE && gds->tokenp[1].token == tDST) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSTon; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tZONE) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSToff; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tDAYZONE) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSTon; gds->tokenp += 1; return 1; } return 0; } /* * Year/month/day in various combinations. */ static int datephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '/' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == '/' && gds->tokenp[4].token == tUNUMBER) { gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; if (gds->tokenp[0].value >= 13) { /* First number is big: 2004/01/29, 99/02/17 */ gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; } else if ((gds->tokenp[4].value >= 13) || (gds->tokenp[2].value >= 13)) { /* Last number is big: 01/07/98 */ /* Middle number is big: 01/29/04 */ gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } else { /* No significant clues: 02/03/04 */ gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '/' && gds->tokenp[2].token == tUNUMBER) { /* "1/15" */ gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '-' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == '-' && gds->tokenp[4].token == tUNUMBER) { /* ISO 8601 format. yyyy-mm-dd. */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '-' && gds->tokenp[2].token == tMONTH && gds->tokenp[3].token == '-' && gds->tokenp[4].token == tUNUMBER) { gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; if (gds->tokenp[0].value > 31) { /* e.g. 1992-Jun-17 */ gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; } else { /* e.g. 17-JUN-1992. */ gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tMONTH && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == ',' && gds->tokenp[3].token == tUNUMBER) { /* "June 17, 2001" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[1].value; gds->Year = gds->tokenp[3].value; gds->tokenp += 4; return 1; } if (gds->tokenp[0].token == tMONTH && gds->tokenp[1].token == tUNUMBER) { /* "May 3" */ gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH && gds->tokenp[2].token == tUNUMBER) { /* "12 Sept 1997" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[1].value; gds->Year = gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH) { /* "12 Sept" */ gds->HaveMonth++; gds->HaveDay++; gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[1].value; gds->tokenp += 2; return 1; } return 0; } /* * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc. */ static int relunitphrase(struct gdstate *gds) { if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tSEC_UNIT) { /* "-3 hours" */ gds->HaveRel++; gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tSEC_UNIT) { /* "+1 minute" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tSEC_UNIT) { /* "1 day" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tMONTH_UNIT) { /* "-3 months" */ gds->HaveRel++; gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tMONTH_UNIT) { /* "+5 years" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH_UNIT) { /* "2 years" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == tSEC_UNIT) { /* "now", "tomorrow" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tMONTH_UNIT) { /* "month" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[0].value; gds->tokenp += 1; return 1; } return 0; } /* * Day of the week specification. */ static int dayphrase(struct gdstate *gds) { if (gds->tokenp[0].token == tDAY) { /* "tues", "wednesday," */ gds->HaveWeekDay++; gds->DayOrdinal = 1; gds->DayNumber = gds->tokenp[0].value; gds->tokenp += 1; if (gds->tokenp[0].token == ',') gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tDAY) { /* "second tues" "3 wed" */ gds->HaveWeekDay++; gds->DayOrdinal = gds->tokenp[0].value; gds->DayNumber = gds->tokenp[1].value; gds->tokenp += 2; return 1; } return 0; } /* * Try to match a phrase using one of the above functions. * This layer also deals with a couple of generic issues. */ static int phrase(struct gdstate *gds) { if (timephrase(gds)) return 1; if (zonephrase(gds)) return 1; if (datephrase(gds)) return 1; if (dayphrase(gds)) return 1; if (relunitphrase(gds)) { if (gds->tokenp[0].token == tAGO) { gds->RelSeconds = -gds->RelSeconds; gds->RelMonth = -gds->RelMonth; gds->tokenp += 1; } return 1; } /* Bare numbers sometimes have meaning. */ if (gds->tokenp[0].token == tUNUMBER) { if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) { gds->HaveYear++; gds->Year = gds->tokenp[0].value; gds->tokenp += 1; return 1; } if(gds->tokenp[0].value > 10000) { /* "20040301" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Day= (gds->tokenp[0].value)%100; gds->Month= (gds->tokenp[0].value/100)%100; gds->Year = gds->tokenp[0].value/10000; gds->tokenp += 1; return 1; } if (gds->tokenp[0].value < 24) { gds->HaveTime++; gds->Hour = gds->tokenp[0].value; gds->Minutes = 0; gds->Seconds = 0; gds->tokenp += 1; return 1; } if ((gds->tokenp[0].value / 100 < 24) && (gds->tokenp[0].value % 100 < 60)) { /* "513" is same as "5:13" */ gds->Hour = gds->tokenp[0].value / 100; gds->Minutes = gds->tokenp[0].value % 100; gds->Seconds = 0; gds->tokenp += 1; return 1; } } return 0; } /* * A dictionary of time words. */ static struct LEXICON { size_t abbrev; const char *name; int type; time_t value; } const TimeWords[] = { /* am/pm */ { 0, "am", tAMPM, tAM }, { 0, "pm", tAMPM, tPM }, /* Month names. */ { 3, "january", tMONTH, 1 }, { 3, "february", tMONTH, 2 }, { 3, "march", tMONTH, 3 }, { 3, "april", tMONTH, 4 }, { 3, "may", tMONTH, 5 }, { 3, "june", tMONTH, 6 }, { 3, "july", tMONTH, 7 }, { 3, "august", tMONTH, 8 }, { 3, "september", tMONTH, 9 }, { 3, "october", tMONTH, 10 }, { 3, "november", tMONTH, 11 }, { 3, "december", tMONTH, 12 }, /* Days of the week. */ { 2, "sunday", tDAY, 0 }, { 3, "monday", tDAY, 1 }, { 2, "tuesday", tDAY, 2 }, { 3, "wednesday", tDAY, 3 }, { 2, "thursday", tDAY, 4 }, { 2, "friday", tDAY, 5 }, { 2, "saturday", tDAY, 6 }, /* Timezones: Offsets are in seconds. */ { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */ { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */ { 0, "utc", tZONE, 0*HOUR }, { 0, "wet", tZONE, 0*HOUR }, /* Western European */ { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */ { 0, "wat", tZONE, 1*HOUR }, /* West Africa */ { 0, "at", tZONE, 2*HOUR }, /* Azores */ /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */ /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/ { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */ { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */ { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */ { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */ { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */ { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */ { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */ { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */ { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */ { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */ { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */ { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */ { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */ { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */ { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */ { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */ { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */ { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */ { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */ { 0, "nt", tZONE, 11*HOUR }, /* Nome */ { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */ { 0, "cet", tZONE, -1*HOUR }, /* Central European */ { 0, "met", tZONE, -1*HOUR }, /* Middle European */ { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */ { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */ { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */ { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */ { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */ { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */ { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */ { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */ { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */ { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */ { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */ { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */ { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */ /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */ /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */ { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */ { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */ { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/ { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */ { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */ { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */ { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */ { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */ { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */ { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */ { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */ { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */ { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */ { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */ { 0, "dst", tDST, 0 }, /* Time units. */ { 4, "years", tMONTH_UNIT, 12 }, { 5, "months", tMONTH_UNIT, 1 }, { 9, "fortnights", tSEC_UNIT, 14 * DAY }, { 4, "weeks", tSEC_UNIT, 7 * DAY }, { 3, "days", tSEC_UNIT, DAY }, { 4, "hours", tSEC_UNIT, HOUR }, { 3, "minutes", tSEC_UNIT, MINUTE }, { 3, "seconds", tSEC_UNIT, 1 }, /* Relative-time words. */ { 0, "tomorrow", tSEC_UNIT, DAY }, { 0, "yesterday", tSEC_UNIT, -DAY }, { 0, "today", tSEC_UNIT, 0 }, { 0, "now", tSEC_UNIT, 0 }, { 0, "last", tUNUMBER, -1 }, { 0, "this", tSEC_UNIT, 0 }, { 0, "next", tUNUMBER, 2 }, { 0, "first", tUNUMBER, 1 }, { 0, "1st", tUNUMBER, 1 }, /* { 0, "second", tUNUMBER, 2 }, */ { 0, "2nd", tUNUMBER, 2 }, { 0, "third", tUNUMBER, 3 }, { 0, "3rd", tUNUMBER, 3 }, { 0, "fourth", tUNUMBER, 4 }, { 0, "4th", tUNUMBER, 4 }, { 0, "fifth", tUNUMBER, 5 }, { 0, "5th", tUNUMBER, 5 }, { 0, "sixth", tUNUMBER, 6 }, { 0, "seventh", tUNUMBER, 7 }, { 0, "eighth", tUNUMBER, 8 }, { 0, "ninth", tUNUMBER, 9 }, { 0, "tenth", tUNUMBER, 10 }, { 0, "eleventh", tUNUMBER, 11 }, { 0, "twelfth", tUNUMBER, 12 }, { 0, "ago", tAGO, 1 }, /* Military timezones. */ { 0, "a", tZONE, 1*HOUR }, { 0, "b", tZONE, 2*HOUR }, { 0, "c", tZONE, 3*HOUR }, { 0, "d", tZONE, 4*HOUR }, { 0, "e", tZONE, 5*HOUR }, { 0, "f", tZONE, 6*HOUR }, { 0, "g", tZONE, 7*HOUR }, { 0, "h", tZONE, 8*HOUR }, { 0, "i", tZONE, 9*HOUR }, { 0, "k", tZONE, 10*HOUR }, { 0, "l", tZONE, 11*HOUR }, { 0, "m", tZONE, 12*HOUR }, { 0, "n", tZONE, -1*HOUR }, { 0, "o", tZONE, -2*HOUR }, { 0, "p", tZONE, -3*HOUR }, { 0, "q", tZONE, -4*HOUR }, { 0, "r", tZONE, -5*HOUR }, { 0, "s", tZONE, -6*HOUR }, { 0, "t", tZONE, -7*HOUR }, { 0, "u", tZONE, -8*HOUR }, { 0, "v", tZONE, -9*HOUR }, { 0, "w", tZONE, -10*HOUR }, { 0, "x", tZONE, -11*HOUR }, { 0, "y", tZONE, -12*HOUR }, { 0, "z", tZONE, 0*HOUR }, /* End of table. */ { 0, NULL, 0, 0 } }; /* * Year is either: * = A number from 0 to 99, which means a year from 1970 to 2069, or * = The actual year (>=100). */ static time_t Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes, time_t Seconds, time_t Timezone, enum DSTMODE DSTmode) { - static int DaysInMonth[12] = { + int DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t Julian; int i; if (Year < 69) Year += 2000; else if (Year < 100) Year += 1900; DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 29 : 28; /* Checking for 2038 bogusly assumes that time_t is 32 bits. But I'm too lazy to try to check for time_t overflow in another way. */ if (Year < EPOCH || Year > 2038 || Month < 1 || Month > 12 /* Lint fluff: "conversion from long may lose accuracy" */ || Day < 1 || Day > DaysInMonth[(int)--Month] || Hours < 0 || Hours > 23 || Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) return -1; Julian = Day - 1; for (i = 0; i < Month; i++) Julian += DaysInMonth[i]; for (i = EPOCH; i < Year; i++) Julian += 365 + (i % 4 == 0); Julian *= DAY; Julian += Timezone; Julian += Hours * HOUR + Minutes * MINUTE + Seconds; if (DSTmode == DSTon || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) Julian -= HOUR; return Julian; } static time_t DSTcorrect(time_t Start, time_t Future) { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * HOUR; } static time_t RelativeDate(time_t Start, time_t zone, int dstmode, time_t DayOrdinal, time_t DayNumber) { struct tm *tm; time_t t, now; t = Start - zone; tm = gmtime(&t); now = Start; now += DAY * ((DayNumber - tm->tm_wday + 7) % 7); now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); if (dstmode == DSTmaybe) return DSTcorrect(Start, now); return now - Start; } static time_t RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) { struct tm *tm; time_t Month; time_t Year; if (RelMonth == 0) return 0; tm = localtime(&Start); Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, Timezone, DSTmaybe)); } /* * Tokenizer. */ static int nexttoken(const char **in, time_t *value) { char c; char buff[64]; for ( ; ; ) { while (isspace((unsigned char)**in)) ++*in; /* Skip parenthesized comments. */ if (**in == '(') { int Count = 0; do { c = *(*in)++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); continue; } /* Try the next token in the word table first. */ /* This allows us to match "2nd", for example. */ { const char *src = *in; const struct LEXICON *tp; unsigned i = 0; /* Force to lowercase and strip '.' characters. */ while (*src != '\0' && (isalnum((unsigned char)*src) || *src == '.') && i < sizeof(buff)-1) { if (*src != '.') { if (isupper((unsigned char)*src)) buff[i++] = tolower((unsigned char)*src); else buff[i++] = *src; } src++; } buff[i] = '\0'; /* * Find the first match. If the word can be * abbreviated, make sure we match at least * the minimum abbreviation. */ for (tp = TimeWords; tp->name; tp++) { size_t abbrev = tp->abbrev; if (abbrev == 0) abbrev = strlen(tp->name); if (strlen(buff) >= abbrev && strncmp(tp->name, buff, strlen(buff)) == 0) { /* Skip over token. */ *in = src; /* Return the match. */ *value = tp->value; return tp->type; } } } /* * Not in the word table, maybe it's a number. Note: * Because '-' and '+' have other special meanings, I * don't deal with signed numbers here. */ if (isdigit((unsigned char)(c = **in))) { for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) *value = 10 * *value + c - '0'; (*in)--; return (tUNUMBER); } return *(*in)++; } } #define TM_YEAR_ORIGIN 1900 /* Yield A - B, measured in seconds. */ static long difftm (struct tm *a, struct tm *b) { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); int days = ( /* difference in day of year */ a->tm_yday - b->tm_yday /* + intervening leap days */ + ((ay >> 2) - (by >> 2)) - (ay/100 - by/100) + ((ay/100 >> 2) - (by/100 >> 2)) /* + difference in years * 365 */ + (long)(ay-by) * 365 ); return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR + (a->tm_min - b->tm_min) * MINUTE + (a->tm_sec - b->tm_sec)); } /* * * The public function. * * TODO: tokens[] array should be dynamically sized. */ time_t __archive_get_date(time_t now, const char *p) { struct token tokens[256]; struct gdstate _gds; struct token *lasttoken; struct gdstate *gds; struct tm local, *tm; struct tm gmt, *gmt_ptr; time_t Start; time_t tod; long tzone; /* Clear out the parsed token array. */ memset(tokens, 0, sizeof(tokens)); /* Initialize the parser state. */ memset(&_gds, 0, sizeof(_gds)); gds = &_gds; /* Look up the current time. */ memset(&local, 0, sizeof(local)); tm = localtime (&now); if (tm == NULL) return -1; local = *tm; /* Look up UTC if we can and use that to determine the current * timezone offset. */ memset(&gmt, 0, sizeof(gmt)); gmt_ptr = gmtime (&now); if (gmt_ptr != NULL) { /* Copy, in case localtime and gmtime use the same buffer. */ gmt = *gmt_ptr; } if (gmt_ptr != NULL) tzone = difftm (&gmt, &local); else /* This system doesn't understand timezones; fake it. */ tzone = 0; if(local.tm_isdst) tzone += HOUR; /* Tokenize the input string. */ lasttoken = tokens; while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) { ++lasttoken; if (lasttoken > tokens + 255) return -1; } gds->tokenp = tokens; /* Match phrases until we run out of input tokens. */ while (gds->tokenp < lasttoken) { if (!phrase(gds)) return -1; } /* Use current local timezone if none was specified. */ if (!gds->HaveZone) { gds->Timezone = tzone; gds->DSTmode = DSTmaybe; } /* If a timezone was specified, use that for generating the default * time components instead of the local timezone. */ if (gds->HaveZone && gmt_ptr != NULL) { now -= gds->Timezone; gmt_ptr = gmtime (&now); if (gmt_ptr != NULL) local = *gmt_ptr; now += gds->Timezone; } if (!gds->HaveYear) gds->Year = local.tm_year + 1900; if (!gds->HaveMonth) gds->Month = local.tm_mon + 1; if (!gds->HaveDay) gds->Day = local.tm_mday; /* Note: No default for hour/min/sec; a specifier that just * gives date always refers to 00:00 on that date. */ /* If we saw more than one time, timezone, weekday, year, month, * or day, then give up. */ if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1 || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1) return -1; /* Compute an absolute time based on whatever absolute information * we collected. */ if (gds->HaveYear || gds->HaveMonth || gds->HaveDay || gds->HaveTime || gds->HaveWeekDay) { Start = Convert(gds->Month, gds->Day, gds->Year, gds->Hour, gds->Minutes, gds->Seconds, gds->Timezone, gds->DSTmode); if (Start < 0) return -1; } else { Start = now; if (!gds->HaveRel) Start -= local.tm_hour * HOUR + local.tm_min * MINUTE + local.tm_sec; } /* Add the relative offset. */ Start += gds->RelSeconds; Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth); /* Adjust for day-of-week offsets. */ if (gds->HaveWeekDay && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) { tod = RelativeDate(Start, gds->Timezone, gds->DSTmode, gds->DayOrdinal, gds->DayNumber); Start += tod; } /* -1 is an error indicator, so return 0 instead of -1 if * that's the actual time. */ return Start == -1 ? 0 : Start; } #if defined(TEST) /* ARGSUSED */ int main(int argc, char **argv) { time_t d; time_t now = time(NULL); while (*++argv != NULL) { (void)printf("Input: %s\n", *argv); d = get_date(now, *argv); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else (void)printf("Output: %s\n", ctime(&d)); } exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ Index: stable/10/contrib/libarchive/libarchive/archive_pack_dev.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_pack_dev.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_pack_dev.c (revision 316338) @@ -1,329 +1,329 @@ /* $NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $ */ /*- * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* Originally from NetBSD's mknod(8) source. */ #include "archive_platform.h" #if HAVE_SYS_CDEFS_H #include #endif #if !defined(lint) __RCSID("$NetBSD$"); #endif /* not lint */ #ifdef HAVE_LIMITS_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive_pack_dev.h" static pack_t pack_netbsd; static pack_t pack_freebsd; static pack_t pack_8_8; static pack_t pack_12_20; static pack_t pack_14_18; static pack_t pack_8_24; static pack_t pack_bsdos; static int compare_format(const void *, const void *); static const char iMajorError[] = "invalid major number"; static const char iMinorError[] = "invalid minor number"; static const char tooManyFields[] = "too many fields for format"; /* This is blatantly stolen from libarchive/archive_entry.c, * in an attempt to get this to play nice on MinGW... */ #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) #endif /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define apd_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define apd_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define apd_makedev(maj, min) makedev((maj), (min)) #endif /* exported */ dev_t pack_native(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = apd_makedev(numbers[0], numbers[1]); if ((unsigned long)major(dev) != numbers[0]) *error = iMajorError; else if ((unsigned long)minor(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } static dev_t pack_netbsd(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_netbsd(numbers[0], numbers[1]); if ((unsigned long)major_netbsd(dev) != numbers[0]) *error = iMajorError; else if ((unsigned long)minor_netbsd(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) #define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) #define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ (((y) << 0) & 0xffff00ff))) static dev_t pack_freebsd(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_freebsd(numbers[0], numbers[1]); if ((unsigned long)major_freebsd(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_freebsd(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) #define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) #define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ (((y) << 0) & 0x000000ff))) static dev_t pack_8_8(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_8_8(numbers[0], numbers[1]); if ((unsigned long)major_8_8(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_8_8(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) #define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) #define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \ (((y) << 0) & 0x000fffff))) static dev_t pack_12_20(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_12_20(numbers[0], numbers[1]); if ((unsigned long)major_12_20(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_12_20(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) #define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) #define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \ (((y) << 0) & 0x0003ffff))) static dev_t pack_14_18(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_14_18(numbers[0], numbers[1]); if ((unsigned long)major_14_18(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_14_18(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) #define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) #define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \ (((y) << 0) & 0x00ffffff))) static dev_t pack_8_24(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_8_24(numbers[0], numbers[1]); if ((unsigned long)major_8_24(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_8_24(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) #define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) #define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) #define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \ (((y) << 8) & 0x000fff00) | \ (((z) << 0) & 0x000000ff))) static dev_t pack_bsdos(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_12_20(numbers[0], numbers[1]); if ((unsigned long)major_12_20(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_12_20(dev) != numbers[1]) *error = iMinorError; } else if (n == 3) { dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); if ((unsigned long)major_12_12_8(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)unit_12_12_8(dev) != numbers[1]) *error = "invalid unit number"; if ((unsigned long)subunit_12_12_8(dev) != numbers[2]) *error = "invalid subunit number"; } else *error = tooManyFields; return (dev); } /* list of formats and pack functions */ /* this list must be sorted lexically */ -static struct format { +static const struct format { const char *name; pack_t *pack; } formats[] = { {"386bsd", pack_8_8}, {"4bsd", pack_8_8}, {"bsdos", pack_bsdos}, {"freebsd", pack_freebsd}, {"hpux", pack_8_24}, {"isc", pack_8_8}, {"linux", pack_8_8}, {"native", pack_native}, {"netbsd", pack_netbsd}, {"osf1", pack_12_20}, {"sco", pack_8_8}, {"solaris", pack_14_18}, {"sunos", pack_8_8}, {"svr3", pack_8_8}, {"svr4", pack_14_18}, {"ultrix", pack_8_8}, }; static int compare_format(const void *key, const void *element) { const char *name; const struct format *format; name = key; format = element; return (strcmp(name, format->name)); } pack_t * pack_find(const char *name) { struct format *format; format = bsearch(name, formats, sizeof(formats)/sizeof(formats[0]), sizeof(formats[0]), compare_format); if (format == 0) return (NULL); return (format->pack); } Index: stable/10/contrib/libarchive/libarchive/archive_platform.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_platform.h (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_platform.h (revision 316338) @@ -1,217 +1,183 @@ /*- * 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$ */ /* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */ /* * This header is the first thing included in any of the libarchive * source files. As far as possible, platform-specific issues should * be dealt with here and not within individual source files. I'm * actively trying to minimize #if blocks within the main source, * since they obfuscate the code. */ #ifndef ARCHIVE_PLATFORM_H_INCLUDED #define ARCHIVE_PLATFORM_H_INCLUDED /* archive.h and archive_entry.h require this. */ #define __LIBARCHIVE_BUILD 1 #if defined(PLATFORM_CONFIG_H) /* Use hand-built config.h in environments that need it. */ #include PLATFORM_CONFIG_H #elif defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ #include "config.h" #else /* Warn if the library hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no pre-built configuration in archive_platform.h. #endif /* It should be possible to get rid of this by extending the feature-test * macros to cover Windows API functions, probably along with non-trivial * refactoring of code to find structures that sit more cleanly on top of * either Windows or Posix APIs. */ #if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) #include "archive_windows.h" #endif /* * The config files define a lot of feature macros. The following * uses those macros to select/define replacements and include key * headers as required. */ /* Get a real definition for __FBSDID or __RCSID if we can */ #if HAVE_SYS_CDEFS_H #include #endif /* If not, define them so as to avoid dangling semicolons. */ #ifndef __FBSDID #define __FBSDID(a) struct _undefined_hack #endif #ifndef __RCSID #define __RCSID(a) struct _undefined_hack #endif /* Try to get standard C99-style integer type definitions. */ #if HAVE_INTTYPES_H #include #endif #if HAVE_STDINT_H #include #endif /* Borland warns about its own constants! */ #if defined(__BORLANDC__) # if HAVE_DECL_UINT64_MAX # undef UINT64_MAX # undef HAVE_DECL_UINT64_MAX # endif # if HAVE_DECL_UINT64_MIN # undef UINT64_MIN # undef HAVE_DECL_UINT64_MIN # endif # if HAVE_DECL_INT64_MAX # undef INT64_MAX # undef HAVE_DECL_INT64_MAX # endif # if HAVE_DECL_INT64_MIN # undef INT64_MIN # undef HAVE_DECL_INT64_MIN # endif #endif /* Some platforms lack the standard *_MAX definitions. */ #if !HAVE_DECL_SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #if !HAVE_DECL_SSIZE_MAX #define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1)) #endif #if !HAVE_DECL_UINT32_MAX #define UINT32_MAX (~(uint32_t)0) #endif #if !HAVE_DECL_INT32_MAX #define INT32_MAX ((int32_t)(UINT32_MAX >> 1)) #endif #if !HAVE_DECL_INT32_MIN #define INT32_MIN ((int32_t)(~INT32_MAX)) #endif #if !HAVE_DECL_UINT64_MAX #define UINT64_MAX (~(uint64_t)0) #endif #if !HAVE_DECL_INT64_MAX #define INT64_MAX ((int64_t)(UINT64_MAX >> 1)) #endif #if !HAVE_DECL_INT64_MIN #define INT64_MIN ((int64_t)(~INT64_MAX)) #endif #if !HAVE_DECL_UINTMAX_MAX #define UINTMAX_MAX (~(uintmax_t)0) #endif #if !HAVE_DECL_INTMAX_MAX #define INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1)) #endif #if !HAVE_DECL_INTMAX_MIN #define INTMAX_MIN ((intmax_t)(~INTMAX_MAX)) #endif /* - * If this platform has , acl_create(), acl_init(), - * acl_set_file(), and ACL_USER, we assume it has the rest of the - * POSIX.1e draft functions used in archive_read_extract.c. - */ -#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE -#if HAVE_DECL_ACL_USER -#define HAVE_POSIX_ACL 1 -#elif HAVE_DECL_ACL_TYPE_EXTENDED && HAVE_MEMBERSHIP_H -#define HAVE_DARWIN_ACL 1 -#endif -#if HAVE_DECL_ACL_TYPE_NFS4 -#define HAVE_FREEBSD_NFS4_ACL 1 -#endif -#endif - -/* - * If this platform has , acl(), facl() and ACLENT_T - * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions - */ -#if HAVE_SYS_ACL_H && HAVE_ACL && HAVE_FACL && HAVE_ACLENT_T && \ - HAVE_DECL_GETACL && HAVE_DECL_GETACLCNT && HAVE_DECL_SETACL -#define HAVE_SUN_ACL 1 -#if HAVE_ACE_T && HAVE_DECL_ACE_GETACL && HAVE_DECL_ACE_GETACLCNT && \ - HAVE_DECL_ACE_SETACL -#define HAVE_SUN_NFS4_ACL 1 -#endif -#endif - -/* Define if platform supports NFSv4 ACLs */ -#if HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_NFS4_ACL || HAVE_DARWIN_ACL -#define HAVE_NFS4_ACL 1 -#endif - -/* * If we can't restore metadata using a file descriptor, then * for compatibility's sake, close files before trying to restore metadata. */ #if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN) #define CAN_RESTORE_METADATA_FD #endif /* * glibc 2.24 deprecates readdir_r */ #if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24)) #define USE_READDIR_R 1 #else #undef USE_READDIR_R #endif /* Set up defaults for internal error codes. */ #ifndef ARCHIVE_ERRNO_FILE_FORMAT #if HAVE_EFTYPE #define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE #else #if HAVE_EILSEQ #define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ #else #define ARCHIVE_ERRNO_FILE_FORMAT EINVAL #endif #endif #endif #ifndef ARCHIVE_ERRNO_PROGRAMMER #define ARCHIVE_ERRNO_PROGRAMMER EINVAL #endif #ifndef ARCHIVE_ERRNO_MISC #define ARCHIVE_ERRNO_MISC (-1) #endif #endif /* !ARCHIVE_PLATFORM_H_INCLUDED */ Index: stable/10/contrib/libarchive/libarchive/archive_platform_acl.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_platform_acl.h (nonexistent) +++ stable/10/contrib/libarchive/libarchive/archive_platform_acl.h (revision 316338) @@ -0,0 +1,49 @@ +/*- + * 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(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$ + */ + +/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */ + +#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED +#define ARCHIVE_PLATFORM_ACL_H_INCLUDED + +/* + * Determine what ACL types are supported + */ +#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_SUNOS || ARCHIVE_ACL_LIBACL +#define ARCHIVE_ACL_POSIX1E 1 +#endif + +#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_SUNOS_NFS4 || \ + ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL +#define ARCHIVE_ACL_NFS4 1 +#endif + +#if ARCHIVE_ACL_POSIX1E || ARCHIVE_ACL_NFS4 +#define ARCHIVE_ACL_SUPPORT 1 +#endif + +#endif /* ARCHIVE_PLATFORM_ACL_H_INCLUDED */ Property changes on: stable/10/contrib/libarchive/libarchive/archive_platform_acl.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/archive_platform_xattr.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_platform_xattr.h (nonexistent) +++ stable/10/contrib/libarchive/libarchive/archive_platform_xattr.h (revision 316338) @@ -0,0 +1,41 @@ +/*- + * 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(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$ + */ + +/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */ + +#ifndef ARCHIVE_PLATFORM_XATTR_H_INCLUDED +#define ARCHIVE_PLATFORM_XATTR_H_INCLUDED + +/* + * Determine if we support extended attributes + */ +#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_FREEBSD || \ + ARCHIVE_XATTR_AIX +#define ARCHIVE_XATTR_SUPPORT 1 +#endif + +#endif /* ARCHIVE_PLATFORM_XATTR_H_INCLUDED */ Property changes on: stable/10/contrib/libarchive/libarchive/archive_platform_xattr.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/archive_random.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_random.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_random.c (revision 316338) @@ -1,269 +1,272 @@ /*- * Copyright (c) 2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_STDLIB_H #include #endif #if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) #ifdef HAVE_FCNTL #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_PTHREAD_H #include #endif static void arc4random_buf(void *, size_t); #endif /* HAVE_ARC4RANDOM_BUF */ #include "archive.h" #include "archive_random_private.h" #if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) #include #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* * Random number generator function. * This simply calls arc4random_buf function if the platform provides it. */ int archive_random(void *buf, size_t nbytes) { #if defined(_WIN32) && !defined(__CYGWIN__) HCRYPTPROV hProv; BOOL success; success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) { success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET); } if (success) { success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf); CryptReleaseContext(hProv, 0); if (success) return ARCHIVE_OK; } /* TODO: Does this case really happen? */ return ARCHIVE_FAILED; #else arc4random_buf(buf, nbytes); return ARCHIVE_OK; #endif } #if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) /* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Arc4 random number generator for OpenBSD. * * This code is derived from section 17.1 of Applied Cryptography, * second edition, which describes a stream cipher allegedly * compatible with RSA Labs "RC4" cipher (the actual description of * which is a trade secret). The same algorithm is used as a stream * cipher called "arcfour" in Tatu Ylonen's ssh package. * * RC4 is a registered trademark of RSA Laboratories. */ #ifdef __GNUC__ #define inline __inline #else /* !__GNUC__ */ #define inline #endif /* !__GNUC__ */ struct arc4_stream { uint8_t i; uint8_t j; uint8_t s[256]; }; #define RANDOMDEV "/dev/urandom" #define KEYSIZE 128 #ifdef HAVE_PTHREAD_H static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; #define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx); #define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx); #else #define _ARC4_LOCK() #define _ARC4_UNLOCK() #endif static int rs_initialized; static struct arc4_stream rs; static pid_t arc4_stir_pid; static int arc4_count; static inline uint8_t arc4_getbyte(void); static void arc4_stir(void); static inline void arc4_init(void) { int n; for (n = 0; n < 256; n++) rs.s[n] = n; rs.i = 0; rs.j = 0; } static inline void arc4_addrandom(u_char *dat, int datlen) { int n; uint8_t si; rs.i--; for (n = 0; n < 256; n++) { rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si + dat[n % datlen]); rs.s[rs.i] = rs.s[rs.j]; rs.s[rs.j] = si; } rs.j = rs.i; } static void arc4_stir(void) { int done, fd, i; struct { struct timeval tv; pid_t pid; u_char rnd[KEYSIZE]; } rdat; if (!rs_initialized) { arc4_init(); rs_initialized = 1; } done = 0; fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0); if (fd >= 0) { if (read(fd, &rdat, KEYSIZE) == KEYSIZE) done = 1; (void)close(fd); } if (!done) { (void)gettimeofday(&rdat.tv, NULL); rdat.pid = getpid(); /* We'll just take whatever was on the stack too... */ } arc4_addrandom((u_char *)&rdat, KEYSIZE); /* * Discard early keystream, as per recommendations in: * "(Not So) Random Shuffles of RC4" by Ilya Mironov. + * As per the Network Operations Division, cryptographic requirements + * published on wikileaks on March 2017. */ - for (i = 0; i < 1024; i++) + + for (i = 0; i < 3072; i++) (void)arc4_getbyte(); arc4_count = 1600000; } static void arc4_stir_if_needed(void) { pid_t pid = getpid(); if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) { arc4_stir_pid = pid; arc4_stir(); } } static inline uint8_t arc4_getbyte(void) { uint8_t si, sj; rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si); sj = rs.s[rs.j]; rs.s[rs.i] = sj; rs.s[rs.j] = si; return (rs.s[(si + sj) & 0xff]); } static void arc4random_buf(void *_buf, size_t n) { u_char *buf = (u_char *)_buf; _ARC4_LOCK(); arc4_stir_if_needed(); while (n--) { if (--arc4_count <= 0) arc4_stir(); buf[n] = arc4_getbyte(); } _ARC4_UNLOCK(); } #endif /* !HAVE_ARC4RANDOM_BUF */ Index: stable/10/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (revision 316338) @@ -1,2138 +1,1028 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD"); /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H -/* Mac OSX requires sys/types.h before sys/acl.h. */ #include #endif -#ifdef HAVE_SYS_ACL_H -#include -#endif -#ifdef HAVE_DARWIN_ACL -#include -#include -#include -#endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #if defined(HAVE_SYS_XATTR_H) #include #elif defined(HAVE_ATTR_XATTR_H) #include #endif #ifdef HAVE_SYS_EA_H #include #endif -#ifdef HAVE_ACL_LIBACL_H -#include -#endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_FIEMAP_H #include #endif #ifdef HAVE_LINUX_FS_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif -/* - * Linux and FreeBSD plug this obvious hole in POSIX.1e in - * different ways. - */ -#if HAVE_ACL_GET_PERM -#define ACL_GET_PERM acl_get_perm -#elif HAVE_ACL_GET_PERM_NP -#define ACL_GET_PERM acl_get_perm_np -#endif - -/* NFSv4 platform ACL type */ -#if HAVE_DARWIN_ACL -#define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACL_TYPE_EXTENDED -#elif HAVE_FREEBSD_NFS4_ACL -#define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACL_TYPE_NFS4 -#endif - -static int setup_acls(struct archive_read_disk *, - struct archive_entry *, int *fd); static int setup_mac_metadata(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_xattrs(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_sparse(struct archive_read_disk *, struct archive_entry *, int *fd); #if defined(HAVE_LINUX_FIEMAP_H) static int setup_sparse_fiemap(struct archive_read_disk *, struct archive_entry *, int *fd); #endif +#if !ARCHIVE_ACL_SUPPORT int +archive_read_disk_entry_setup_acls(struct archive_read_disk *a, + struct archive_entry *entry, int *fd) +{ + (void)a; /* UNUSED */ + (void)entry; /* UNUSED */ + (void)fd; /* UNUSED */ + return (ARCHIVE_OK); +} +#endif + +/* + * Enter working directory and return working pathname of archive_entry. + * If a pointer to an integer is provided and its value is below zero + * open a file descriptor on this pahtname. + */ +const char * +archive_read_disk_entry_setup_path(struct archive_read_disk *a, + struct archive_entry *entry, int *fd) +{ + const char *path; + + path = archive_entry_sourcepath(entry); + + if (path == NULL || (a->tree != NULL && + a->tree_enter_working_dir(a->tree) != 0)) + path = archive_entry_pathname(entry); + if (path == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Couldn't determine path"); + } else if (fd != NULL && *fd < 0 && a->tree != NULL && + (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) { + *fd = a->open_on_current_dir(a->tree, path, + O_RDONLY | O_NONBLOCK); + } + return (path); +} + +int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; r = ioctl(fd, #if defined(FS_IOC_GETFLAGS) FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size + 1; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = 0; if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0) - r = setup_acls(a, entry, &fd); + r = archive_read_disk_entry_setup_acls(a, entry, &fd); if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) { r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; } if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); } #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) /* * The Mac OS "copyfile()" API copies the extended metadata for a * file into a separate file in AppleDouble format (see RFC 1740). * * Mac OS tar and cpio implementations store this extended * metadata as a separate entry just before the regular entry * with a "._" prefix added to the filename. * * Note that this is currently done unconditionally; the tar program has * an option to discard this information before the archive is written. * * TODO: If there's a failure, report it and return ARCHIVE_WARN. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int tempfd = -1; int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; struct stat copyfile_stat; int ret = ARCHIVE_OK; void *buff = NULL; int have_attrs; const char *name, *tempdir; struct archive_string tempfile; (void)fd; /* UNUSED */ - name = archive_entry_sourcepath(entry); + + name = archive_read_disk_entry_setup_path(a, entry, NULL); if (name == NULL) - name = archive_entry_pathname(entry); - else if (a->tree != NULL && a->tree_enter_working_dir(a->tree) != 0) { - archive_set_error(&a->archive, errno, - "Can't change dir to read extended attributes"); - return (ARCHIVE_FAILED); - } - if (name == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't open file to read extended attributes: No name"); return (ARCHIVE_WARN); - } /* Short-circuit if there's nothing to do. */ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); if (have_attrs == -1) { archive_set_error(&a->archive, errno, "Could not check extended attributes"); return (ARCHIVE_WARN); } if (have_attrs == 0) return (ARCHIVE_OK); tempdir = NULL; if (issetugid() == 0) tempdir = getenv("TMPDIR"); if (tempdir == NULL) tempdir = _PATH_TMP; archive_string_init(&tempfile); archive_strcpy(&tempfile, tempdir); archive_strcat(&tempfile, "tar.md.XXXXXX"); tempfd = mkstemp(tempfile.s); if (tempfd < 0) { archive_set_error(&a->archive, errno, "Could not open extended attribute file"); ret = ARCHIVE_WARN; goto cleanup; } __archive_ensure_cloexec_flag(tempfd); /* XXX I wish copyfile() could pack directly to a memory * buffer; that would avoid the temp file here. For that * matter, it would be nice if fcopyfile() actually worked, * that would reduce the many open/close races here. */ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { archive_set_error(&a->archive, errno, "Could not pack extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } buff = malloc(copyfile_stat.st_size); if (buff == NULL) { archive_set_error(&a->archive, errno, "Could not allocate memory for extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { archive_set_error(&a->archive, errno, "Could not read extended attributes into memory"); ret = ARCHIVE_WARN; goto cleanup; } archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); cleanup: if (tempfd >= 0) { close(tempfd); unlink(tempfile.s); } archive_string_free(&tempfile); free(buff); return (ret); } #else /* * Stub implementation for non-Mac systems. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif -#if HAVE_DARWIN_ACL -static int translate_guid(struct archive *, acl_entry_t, - int *, int *, const char **); +#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX -static void add_trivial_nfs4_acl(struct archive_entry *); -#endif - -#if HAVE_SUN_ACL -static int -sun_acl_is_trivial(void *, int, mode_t, int, int, int *); - -static void * -sunacl_get(int cmd, int *aclcnt, int fd, const char *path) -{ - int cnt, cntcmd; - size_t size; - void *aclp; - - if (cmd == GETACL) { - cntcmd = GETACLCNT; - size = sizeof(aclent_t); - } -#if HAVE_SUN_NFS4_ACL - else if (cmd == ACE_GETACL) { - cntcmd = ACE_GETACLCNT; - size = sizeof(ace_t); - } -#endif - else { - errno = EINVAL; - *aclcnt = -1; - return (NULL); - } - - aclp = NULL; - cnt = -2; - - while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) { - if (path != NULL) - cnt = acl(path, cntcmd, 0, NULL); - else - cnt = facl(fd, cntcmd, 0, NULL); - - if (cnt > 0) { - if (aclp == NULL) - aclp = malloc(cnt * size); - else - aclp = realloc(NULL, cnt * size); - if (aclp != NULL) { - if (path != NULL) - cnt = acl(path, cmd, cnt, aclp); - else - cnt = facl(fd, cmd, cnt, aclp); - } - } else { - if (aclp != NULL) { - free(aclp); - aclp = NULL; - } - break; - } - } - - *aclcnt = cnt; - return (aclp); -} -#endif /* HAVE_SUN_ACL */ - -#if HAVE_POSIX_ACL || HAVE_NFS4_ACL -static int translate_acl(struct archive_read_disk *a, - struct archive_entry *entry, -#if HAVE_SUN_ACL - void *aclp, - int aclcnt, -#else - acl_t acl, -#endif - int archive_entry_acl_type); - -static int -setup_acls(struct archive_read_disk *a, - struct archive_entry *entry, int *fd) -{ - const char *accpath; -#if HAVE_SUN_ACL - void *aclp; - int aclcnt; -#else - acl_t acl; -#endif - int r; - - accpath = NULL; - -#if HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_ACL_GET_FD_NP - if (*fd < 0) -#else - /* For default ACLs on Linux we need reachable accpath */ - if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) -#endif - { - accpath = archive_entry_sourcepath(entry); - if (accpath == NULL || (a->tree != NULL && - a->tree_enter_working_dir(a->tree) != 0)) - accpath = archive_entry_pathname(entry); - if (accpath == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Couldn't determine file path to read ACLs"); - return (ARCHIVE_WARN); - } - if (a->tree != NULL && -#if !HAVE_SUN_ACL && !HAVE_DARWIN_ACL && !HAVE_ACL_GET_FD_NP - *fd < 0 && -#endif - (a->follow_symlinks || - archive_entry_filetype(entry) != AE_IFLNK)) { - *fd = a->open_on_current_dir(a->tree, - accpath, O_RDONLY | O_NONBLOCK); - } - } - - archive_entry_acl_clear(entry); - -#if HAVE_SUN_ACL - aclp = NULL; -#else - acl = NULL; -#endif - -#if HAVE_NFS4_ACL - /* Try NFSv4 ACL first. */ - if (*fd >= 0) -#if HAVE_SUN_ACL - aclp = sunacl_get(ACE_GETACL, &aclcnt, *fd, NULL); -#elif HAVE_ACL_GET_FD_NP - acl = acl_get_fd_np(*fd, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); -#else - acl = acl_get_fd(*fd); -#endif -#if HAVE_ACL_GET_LINK_NP - else if (!a->follow_symlinks) - acl = acl_get_link_np(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); -#else - else if ((!a->follow_symlinks) - && (archive_entry_filetype(entry) == AE_IFLNK)) - /* We can't get the ACL of a symlink, so we assume it can't - have one. */ -#if HAVE_SUN_ACL - aclp = NULL; -#else - acl = NULL; -#endif -#endif /* !HAVE_ACL_GET_LINK_NP */ - else -#if HAVE_SUN_ACL - /* Solaris reads both POSIX.1e and NFSv4 ACLs here */ - aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, accpath); -#else - acl = acl_get_file(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); -#endif - - - /* Ignore "trivial" ACLs that just mirror the file mode. */ -#if HAVE_SUN_ACL - if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt, - archive_entry_mode(entry), 1, S_ISDIR(archive_entry_mode(entry)), - &r) == 0 && r == 1) { - free(aclp); - aclp = NULL; - return (ARCHIVE_OK); - } -#elif HAVE_ACL_IS_TRIVIAL_NP - if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { - acl_free(acl); - acl = NULL; - return (ARCHIVE_OK); - } -#endif - -#if HAVE_SUN_ACL - if (aclp != NULL) -#else - if (acl != NULL) -#endif - { - r = translate_acl(a, entry, -#if HAVE_SUN_ACL - aclp, aclcnt, -#else - acl, -#endif - ARCHIVE_ENTRY_ACL_TYPE_NFS4); -#if HAVE_SUN_ACL - free(aclp); - aclp = NULL; -#else - acl_free(acl); - acl = NULL; -#endif - - if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, errno, - "Couldn't translate NFSv4 ACLs"); - } -#if HAVE_DARWIN_ACL - /* - * Because Mac OS doesn't support owner@, group@ and everyone@ - * ACLs we need to add NFSv4 ACLs mirroring the file mode to - * the archive entry. Otherwise extraction on non-Mac platforms - * would lead to an invalid file mode. - */ - if ((archive_entry_acl_types(entry) & - ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) - add_trivial_nfs4_acl(entry); -#endif - return (r); - } -#endif /* HAVE_NFS4_ACL */ - -#if HAVE_POSIX_ACL || HAVE_SUN_ACL - /* This code path is skipped on MacOS */ - - /* Retrieve access ACL from file. */ - if (*fd >= 0) -#if HAVE_SUN_ACL - aclp = sunacl_get(GETACL, &aclcnt, *fd, NULL); -#else - acl = acl_get_fd(*fd); -#endif -#if HAVE_ACL_GET_LINK_NP - else if (!a->follow_symlinks) - acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); -#else - else if ((!a->follow_symlinks) - && (archive_entry_filetype(entry) == AE_IFLNK)) - /* We can't get the ACL of a symlink, so we assume it can't - have one. */ -#if HAVE_SUN_ACL - aclp = NULL; -#else - acl = NULL; -#endif -#endif /* !HAVE_ACL_GET_LINK_NP */ - else -#if HAVE_SUN_ACL - aclp = sunacl_get(GETACL, &aclcnt, 0, accpath); -#else - acl = acl_get_file(accpath, ACL_TYPE_ACCESS); -#endif - - - /* Ignore "trivial" ACLs that just mirror the file mode. */ -#if HAVE_SUN_ACL - if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt, - archive_entry_mode(entry), 0, S_ISDIR(archive_entry_mode(entry)), - &r) == 0 && r == 1) { - free(aclp); - aclp = NULL; - } -#elif HAVE_ACL_IS_TRIVIAL_NP - if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { - acl_free(acl); - acl = NULL; - } -#endif - -#if HAVE_SUN_ACL - if (aclp != NULL) -#else - if (acl != NULL) -#endif - { - r = translate_acl(a, entry, -#if HAVE_SUN_ACL - aclp, aclcnt, -#else - acl, -#endif - ARCHIVE_ENTRY_ACL_TYPE_ACCESS); -#if HAVE_SUN_ACL - free(aclp); - aclp = NULL; -#else - acl_free(acl); - acl = NULL; -#endif - - if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, errno, - "Couldn't translate access ACLs"); - return (r); - } - } - -#if !HAVE_SUN_ACL - /* Only directories can have default ACLs. */ - if (S_ISDIR(archive_entry_mode(entry))) { -#if HAVE_ACL_GET_FD_NP - if (*fd >= 0) - acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT); - else -#endif - acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); - if (acl != NULL) { - r = translate_acl(a, entry, acl, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); - acl_free(acl); - if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, errno, - "Couldn't translate default ACLs"); - return (r); - } - } - } -#endif /* !HAVE_SUN_ACL */ -#endif /* HAVE_POSIX_ACL || HAVE_SUN_ACL */ - return (ARCHIVE_OK); -} - /* - * Translate system ACL permissions into libarchive internal structure - */ -static const struct { - const int archive_perm; - const int platform_perm; -} acl_perm_map[] = { -#if HAVE_SUN_ACL /* Solaris NFSv4 ACL permissions */ - {ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE}, - {ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER}, - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE} -#elif HAVE_DARWIN_ACL /* MacOS ACL permissions */ - {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER}, -#if HAVE_DECL_ACL_SYNCHRONIZE - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} -#endif -#else /* POSIX.1e ACL permissions */ - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, - {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, -#if HAVE_FREEBSD_NFS4_ACL /* FreeBSD NFSv4 ACL permissions */ - {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} -#endif -#endif /* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */ -}; - -#if HAVE_NFS4_ACL -/* - * Translate system NFSv4 inheritance flags into libarchive internal structure - */ -static const struct { - const int archive_inherit; - const int platform_inherit; -} acl_inherit_map[] = { -#if HAVE_SUN_NFS4_ACL /* Solaris ACL inheritance flags */ - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE}, - {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG}, - {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG}, -#ifdef ACE_INHERITED_ACE - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE} -#endif -#elif HAVE_DARWIN_ACL /* MacOS NFSv4 inheritance flags */ - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}, - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT} -#else /* FreeBSD NFSv4 ACL inheritance flags */ - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, - {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, - {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} -#endif /* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */ -}; -#endif /* HAVE_NFS4_ACL */ - -#if HAVE_DARWIN_ACL -static int translate_guid(struct archive *a, acl_entry_t acl_entry, - int *ae_id, int *ae_tag, const char **ae_name) -{ - void *q; - uid_t ugid; - int r, idtype; - struct passwd *pwd; - struct group *grp; - - q = acl_get_qualifier(acl_entry); - if (q == NULL) - return (1); - r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype); - if (r != 0) { - acl_free(q); - return (1); - } - if (idtype == ID_TYPE_UID) { - *ae_tag = ARCHIVE_ENTRY_ACL_USER; - pwd = getpwuuid(q); - if (pwd == NULL) { - *ae_id = ugid; - *ae_name = NULL; - } else { - *ae_id = pwd->pw_uid; - *ae_name = archive_read_disk_uname(a, *ae_id); - } - } else if (idtype == ID_TYPE_GID) { - *ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - grp = getgruuid(q); - if (grp == NULL) { - *ae_id = ugid; - *ae_name = NULL; - } else { - *ae_id = grp->gr_gid; - *ae_name = archive_read_disk_gname(a, *ae_id); - } - } else - r = 1; - - acl_free(q); - return (r); -} - -/* - * Add trivial NFSv4 ACL entries from mode - */ -static void -add_trivial_nfs4_acl(struct archive_entry *entry) -{ - mode_t mode; - int i; - const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA; - const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA | - ARCHIVE_ENTRY_ACL_APPEND_DATA; - const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE; - const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | - ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | - ARCHIVE_ENTRY_ACL_READ_ACL | - ARCHIVE_ENTRY_ACL_SYNCHRONIZE; - const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | - ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | - ARCHIVE_ENTRY_ACL_WRITE_ACL | - ARCHIVE_ENTRY_ACL_WRITE_OWNER; - - struct { - const int type; - const int tag; - int permset; - } tacl_entry[] = { - {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0}, - {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0}, - {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0}, - {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset}, - {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset}, - {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset} - }; - - mode = archive_entry_mode(entry); - - /* Permissions for everyone@ */ - if (mode & 0004) - tacl_entry[5].permset |= rperm; - if (mode & 0002) - tacl_entry[5].permset |= wperm; - if (mode & 0001) - tacl_entry[5].permset |= eperm; - - /* Permissions for group@ */ - if (mode & 0040) - tacl_entry[4].permset |= rperm; - else if (mode & 0004) - tacl_entry[2].permset |= rperm; - if (mode & 0020) - tacl_entry[4].permset |= wperm; - else if (mode & 0002) - tacl_entry[2].permset |= wperm; - if (mode & 0010) - tacl_entry[4].permset |= eperm; - else if (mode & 0001) - tacl_entry[2].permset |= eperm; - - /* Permissions for owner@ */ - if (mode & 0400) { - tacl_entry[3].permset |= rperm; - if (!(mode & 0040) && (mode & 0004)) - tacl_entry[0].permset |= rperm; - } else if ((mode & 0040) || (mode & 0004)) - tacl_entry[1].permset |= rperm; - if (mode & 0200) { - tacl_entry[3].permset |= wperm; - if (!(mode & 0020) && (mode & 0002)) - tacl_entry[0].permset |= wperm; - } else if ((mode & 0020) || (mode & 0002)) - tacl_entry[1].permset |= wperm; - if (mode & 0100) { - tacl_entry[3].permset |= eperm; - if (!(mode & 0010) && (mode & 0001)) - tacl_entry[0].permset |= eperm; - } else if ((mode & 0010) || (mode & 0001)) - tacl_entry[1].permset |= eperm; - - for (i = 0; i < 6; i++) { - if (tacl_entry[i].permset != 0) { - archive_entry_acl_add_entry(entry, - tacl_entry[i].type, tacl_entry[i].permset, - tacl_entry[i].tag, -1, NULL); - } - } - - return; -} -#elif HAVE_SUN_ACL -/* - * Check if acl is trivial - * This is a FreeBSD acl_is_trivial_np() implementation for Solaris - */ -static int -sun_acl_is_trivial(void *aclp, int aclcnt, mode_t mode, int is_nfs4, - int is_dir, int *trivialp) -{ - int i, p; -#if HAVE_SUN_NFS4_ACL - const uint32_t rperm = ACE_READ_DATA; - const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA; - const uint32_t eperm = ACE_EXECUTE; - const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | - ACE_READ_ACL | ACE_SYNCHRONIZE; - const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES | - ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER; - - ace_t *ace; - ace_t tace[6]; -#endif - - if (aclp == NULL || trivialp == NULL) - return (-1); - - *trivialp = 0; - - /* - * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with - * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries, - * including mask. - */ - if (!is_nfs4) { - if (aclcnt == 4) - *trivialp = 1; - return (0); - } - -#if HAVE_SUN_NFS4_ACL - /* - * Continue with checking NFSv4 ACLs - * - * Create list of trivial ace's to be compared - */ - - /* owner@ allow pre */ - tace[0].a_flags = ACE_OWNER; - tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; - tace[0].a_access_mask = 0; - - /* owner@ deny */ - tace[1].a_flags = ACE_OWNER; - tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE; - tace[1].a_access_mask = 0; - - /* group@ deny */ - tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP; - tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE; - tace[2].a_access_mask = 0; - - /* owner@ allow */ - tace[3].a_flags = ACE_OWNER; - tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; - tace[3].a_access_mask = ownset; - - /* group@ allow */ - tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP; - tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; - tace[4].a_access_mask = pubset; - - /* everyone@ allow */ - tace[5].a_flags = ACE_EVERYONE; - tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; - tace[5].a_access_mask = pubset; - - /* Permissions for everyone@ */ - if (mode & 0004) - tace[5].a_access_mask |= rperm; - if (mode & 0002) - tace[5].a_access_mask |= wperm; - if (mode & 0001) - tace[5].a_access_mask |= eperm; - - /* Permissions for group@ */ - if (mode & 0040) - tace[4].a_access_mask |= rperm; - else if (mode & 0004) - tace[2].a_access_mask |= rperm; - if (mode & 0020) - tace[4].a_access_mask |= wperm; - else if (mode & 0002) - tace[2].a_access_mask |= wperm; - if (mode & 0010) - tace[4].a_access_mask |= eperm; - else if (mode & 0001) - tace[2].a_access_mask |= eperm; - - /* Permissions for owner@ */ - if (mode & 0400) { - tace[3].a_access_mask |= rperm; - if (!(mode & 0040) && (mode & 0004)) - tace[0].a_access_mask |= rperm; - } else if ((mode & 0040) || (mode & 0004)) - tace[1].a_access_mask |= rperm; - if (mode & 0200) { - tace[3].a_access_mask |= wperm; - if (!(mode & 0020) && (mode & 0002)) - tace[0].a_access_mask |= wperm; - } else if ((mode & 0020) || (mode & 0002)) - tace[1].a_access_mask |= wperm; - if (mode & 0100) { - tace[3].a_access_mask |= eperm; - if (!(mode & 0010) && (mode & 0001)) - tace[0].a_access_mask |= eperm; - } else if ((mode & 0010) || (mode & 0001)) - tace[1].a_access_mask |= eperm; - - /* Check if the acl count matches */ - p = 3; - for (i = 0; i < 3; i++) { - if (tace[i].a_access_mask != 0) - p++; - } - if (aclcnt != p) - return (0); - - p = 0; - for (i = 0; i < 6; i++) { - if (tace[i].a_access_mask != 0) { - ace = &((ace_t *)aclp)[p]; - /* - * Illumos added ACE_DELETE_CHILD to write perms for - * directories. We have to check against that, too. - */ - if (ace->a_flags != tace[i].a_flags || - ace->a_type != tace[i].a_type || - (ace->a_access_mask != tace[i].a_access_mask && - (!is_dir || (tace[i].a_access_mask & wperm) == 0 || - ace->a_access_mask != - (tace[i].a_access_mask | ACE_DELETE_CHILD)))) - return (0); - p++; - } - } - - *trivialp = 1; -#else /* !HAVE_SUN_NFS4_ACL */ - (void)aclp; /* UNUSED */ -#endif /* !HAVE_SUN_NFS4_ACL */ - return (0); -} -#endif /* HAVE_SUN_ACL */ - -#if HAVE_SUN_ACL -/* - * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL - */ -static int -translate_acl(struct archive_read_disk *a, - struct archive_entry *entry, void *aclp, int aclcnt, - int default_entry_acl_type) -{ - int e, i; - int ae_id, ae_tag, ae_perm; - int entry_acl_type; - const char *ae_name; - aclent_t *aclent; -#if HAVE_SUN_NFS4_ACL - ace_t *ace; -#endif - - if (aclcnt <= 0) - return (ARCHIVE_OK); - - for (e = 0; e < aclcnt; e++) { - ae_name = NULL; - ae_tag = 0; - ae_perm = 0; - -#if HAVE_SUN_NFS4_ACL - if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - ace = &((ace_t *)aclp)[e]; - ae_id = ace->a_who; - - switch(ace->a_type) { - case ACE_ACCESS_ALLOWED_ACE_TYPE: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; - break; - case ACE_ACCESS_DENIED_ACE_TYPE: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; - break; - case ACE_SYSTEM_AUDIT_ACE_TYPE: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - break; - case ACE_SYSTEM_ALARM_ACE_TYPE: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; - break; - default: - /* Unknown entry type, skip */ - continue; - } - - if ((ace->a_flags & ACE_OWNER) != 0) - ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - else if ((ace->a_flags & ACE_GROUP) != 0) - ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - else if ((ace->a_flags & ACE_EVERYONE) != 0) - ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; - else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) { - ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - ae_name = archive_read_disk_gname(&a->archive, - ae_id); - } else { - ae_tag = ARCHIVE_ENTRY_ACL_USER; - ae_name = archive_read_disk_uname(&a->archive, - ae_id); - } - - for (i = 0; i < (int)(sizeof(acl_inherit_map) / - sizeof(acl_inherit_map[0])); ++i) { - if ((ace->a_flags & - acl_inherit_map[i].platform_inherit) != 0) - ae_perm |= - acl_inherit_map[i].archive_inherit; - } - - for (i = 0; i < (int)(sizeof(acl_perm_map) / - sizeof(acl_perm_map[0])); ++i) { - if ((ace->a_access_mask & - acl_perm_map[i].platform_perm) != 0) - ae_perm |= - acl_perm_map[i].archive_perm; - } - } else -#endif /* HAVE_SUN_NFS4_ACL */ - if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { - aclent = &((aclent_t *)aclp)[e]; - if ((aclent->a_type & ACL_DEFAULT) != 0) - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - else - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - ae_id = aclent->a_id; - - switch(aclent->a_type) { - case DEF_USER: - case USER: - ae_name = archive_read_disk_uname(&a->archive, - ae_id); - ae_tag = ARCHIVE_ENTRY_ACL_USER; - break; - case DEF_GROUP: - case GROUP: - ae_name = archive_read_disk_gname(&a->archive, - ae_id); - ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - break; - case DEF_CLASS_OBJ: - case CLASS_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_MASK; - break; - case DEF_USER_OBJ: - case USER_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - break; - case DEF_GROUP_OBJ: - case GROUP_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - break; - case DEF_OTHER_OBJ: - case OTHER_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_OTHER; - break; - default: - /* Unknown tag type, skip */ - continue; - } - - if ((aclent->a_perm & 1) != 0) - ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; - if ((aclent->a_perm & 2) != 0) - ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; - if ((aclent->a_perm & 4) != 0) - ae_perm |= ARCHIVE_ENTRY_ACL_READ; - } else - return (ARCHIVE_WARN); - - archive_entry_acl_add_entry(entry, entry_acl_type, - ae_perm, ae_tag, ae_id, ae_name); - } - return (ARCHIVE_OK); -} -#else /* !HAVE_SUN_ACL */ -/* - * Translate POSIX.1e (Linux), FreeBSD (both POSIX.1e and NFSv4) and - * MacOS (NFSv4 only) ACLs into libarchive internal structure - */ -static int -translate_acl(struct archive_read_disk *a, - struct archive_entry *entry, acl_t acl, int default_entry_acl_type) -{ - acl_tag_t acl_tag; -#if HAVE_FREEBSD_NFS4_ACL - acl_entry_type_t acl_type; - int brand; -#endif -#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL - acl_flagset_t acl_flagset; -#endif - acl_entry_t acl_entry; - acl_permset_t acl_permset; - int i, entry_acl_type; - int r, s, ae_id, ae_tag, ae_perm; -#if !HAVE_DARWIN_ACL - void *q; -#endif - const char *ae_name; - -#if HAVE_FREEBSD_NFS4_ACL - // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 - // Make sure the "brand" on this ACL is consistent - // with the default_entry_acl_type bits provided. - if (acl_get_brand_np(acl, &brand) != 0) { - archive_set_error(&a->archive, errno, - "Failed to read ACL brand"); - return (ARCHIVE_WARN); - } - switch (brand) { - case ACL_BRAND_POSIX: - switch (default_entry_acl_type) { - case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: - case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid ACL entry type for POSIX.1e ACL"); - return (ARCHIVE_WARN); - } - break; - case ACL_BRAND_NFS4: - if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid ACL entry type for NFSv4 ACL"); - return (ARCHIVE_WARN); - } - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unknown ACL brand"); - return (ARCHIVE_WARN); - } -#endif - - s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); - if (s == -1) { - archive_set_error(&a->archive, errno, - "Failed to get first ACL entry"); - return (ARCHIVE_WARN); - } - -#if HAVE_DARWIN_ACL - while (s == 0) -#else /* FreeBSD, Linux */ - while (s == 1) -#endif - { - ae_id = -1; - ae_name = NULL; - ae_perm = 0; - - if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { - archive_set_error(&a->archive, errno, - "Failed to get ACL tag type"); - return (ARCHIVE_WARN); - } - switch (acl_tag) { -#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ - case ACL_USER: - q = acl_get_qualifier(acl_entry); - if (q != NULL) { - ae_id = (int)*(uid_t *)q; - acl_free(q); - ae_name = archive_read_disk_uname(&a->archive, - ae_id); - } - ae_tag = ARCHIVE_ENTRY_ACL_USER; - break; - case ACL_GROUP: - q = acl_get_qualifier(acl_entry); - if (q != NULL) { - ae_id = (int)*(gid_t *)q; - acl_free(q); - ae_name = archive_read_disk_gname(&a->archive, - ae_id); - } - ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - break; - case ACL_MASK: - ae_tag = ARCHIVE_ENTRY_ACL_MASK; - break; - case ACL_USER_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - break; - case ACL_GROUP_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - break; - case ACL_OTHER: - ae_tag = ARCHIVE_ENTRY_ACL_OTHER; - break; -#if HAVE_FREEBSD_NFS4_ACL - case ACL_EVERYONE: - ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; - break; -#endif -#else /* HAVE_DARWIN_ACL */ - case ACL_EXTENDED_ALLOW: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; - r = translate_guid(&a->archive, acl_entry, &ae_id, - &ae_tag, &ae_name); - break; - case ACL_EXTENDED_DENY: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; - r = translate_guid(&a->archive, acl_entry, &ae_id, - &ae_tag, &ae_name); - break; -#endif /* HAVE_DARWIN_ACL */ - default: - /* Skip types that libarchive can't support. */ - s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); - continue; - } - -#if HAVE_DARWIN_ACL - /* Skip if translate_guid() above failed */ - if (r != 0) { - s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); - continue; - } -#endif - -#if !HAVE_DARWIN_ACL - // XXX acl_type maps to allow/deny/audit/YYYY bits - entry_acl_type = default_entry_acl_type; -#endif -#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL - if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { -#if HAVE_FREEBSD_NFS4_ACL - /* - * acl_get_entry_type_np() fails with non-NFSv4 ACLs - */ - if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { - archive_set_error(&a->archive, errno, "Failed " - "to get ACL type from a NFSv4 ACL entry"); - return (ARCHIVE_WARN); - } - switch (acl_type) { - case ACL_ENTRY_TYPE_ALLOW: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; - break; - case ACL_ENTRY_TYPE_DENY: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; - break; - case ACL_ENTRY_TYPE_AUDIT: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; - break; - case ACL_ENTRY_TYPE_ALARM: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; - break; - default: - archive_set_error(&a->archive, errno, - "Invalid NFSv4 ACL entry type"); - return (ARCHIVE_WARN); - } -#endif /* HAVE_FREEBSD_NFS4_ACL */ - - /* - * Libarchive stores "flag" (NFSv4 inheritance bits) - * in the ae_perm bitmap. - * - * acl_get_flagset_np() fails with non-NFSv4 ACLs - */ - if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { - archive_set_error(&a->archive, errno, - "Failed to get flagset from a NFSv4 ACL entry"); - return (ARCHIVE_WARN); - } - for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { - r = acl_get_flag_np(acl_flagset, - acl_inherit_map[i].platform_inherit); - if (r == -1) { - archive_set_error(&a->archive, errno, - "Failed to check flag in a NFSv4 " - "ACL flagset"); - return (ARCHIVE_WARN); - } else if (r) - ae_perm |= acl_inherit_map[i].archive_inherit; - } - } -#endif /* HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL */ - - if (acl_get_permset(acl_entry, &acl_permset) != 0) { - archive_set_error(&a->archive, errno, - "Failed to get ACL permission set"); - return (ARCHIVE_WARN); - } - for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { - /* - * acl_get_perm() is spelled differently on different - * platforms; see above. - */ - r = ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm); - if (r == -1) { - archive_set_error(&a->archive, errno, - "Failed to check permission in an ACL permission set"); - return (ARCHIVE_WARN); - } else if (r) - ae_perm |= acl_perm_map[i].archive_perm; - } - -#if HAVE_DARWIN_ACL && !HAVE_DECL_ACL_SYNCHRONIZE - /* On Mac OS X without ACL_SYNCHRONIZE assume it is set */ - ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; -#endif - - archive_entry_acl_add_entry(entry, entry_acl_type, - ae_perm, ae_tag, - ae_id, ae_name); - - s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); -#if !HAVE_DARWIN_ACL - if (s == -1) { - archive_set_error(&a->archive, errno, - "Failed to get next ACL entry"); - return (ARCHIVE_WARN); - } -#endif - } - return (ARCHIVE_OK); -} -#endif /* !HAVE_SUN_ACL */ -#else /* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */ -static int -setup_acls(struct archive_read_disk *a, - struct archive_entry *entry, int *fd) -{ - (void)a; /* UNUSED */ - (void)entry; /* UNUSED */ - (void)fd; /* UNUSED */ - return (ARCHIVE_OK); -} -#endif /* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */ - -#if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \ - HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \ - (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA) - -/* - * Linux and AIX extended attribute support. + * Linux, Darwin and AIX extended attribute support. * * TODO: By using a stack-allocated buffer for the first * call to getxattr(), we might be able to avoid the second * call entirely. We only need the second call if the * stack-allocated buffer is too small. But a modest buffer * of 1024 bytes or so will often be big enough. Same applies * to listxattr(). */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, const char *name, int fd, const char *accpath) { ssize_t size; void *value = NULL; -#if HAVE_FGETXATTR - if (fd >= 0) + + if (fd >= 0) { +#if ARCHIVE_XATTR_LINUX size = fgetxattr(fd, name, NULL, 0); - else if (!a->follow_symlinks) - size = lgetxattr(accpath, name, NULL, 0); - else - size = getxattr(accpath, name, NULL, 0); -#elif HAVE_FGETEA - if (fd >= 0) +#elif ARCHIVE_XATTR_DARWIN + size = fgetxattr(fd, name, NULL, 0, 0, 0); +#elif ARCHIVE_XATTR_AIX size = fgetea(fd, name, NULL, 0); - else if (!a->follow_symlinks) +#endif + } else if (!a->follow_symlinks) { +#if ARCHIVE_XATTR_LINUX + size = lgetxattr(accpath, name, NULL, 0); +#elif ARCHIVE_XATTR_DARWIN + size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW); +#elif ARCHIVE_XATTR_AIX size = lgetea(accpath, name, NULL, 0); - else +#endif + } else { +#if ARCHIVE_XATTR_LINUX + size = getxattr(accpath, name, NULL, 0); +#elif ARCHIVE_XATTR_DARWIN + size = getxattr(accpath, name, NULL, 0, 0, 0); +#elif ARCHIVE_XATTR_AIX size = getea(accpath, name, NULL, 0); #endif + } if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } -#if HAVE_FGETXATTR - if (fd >= 0) + + if (fd >= 0) { +#if ARCHIVE_XATTR_LINUX size = fgetxattr(fd, name, value, size); - else if (!a->follow_symlinks) - size = lgetxattr(accpath, name, value, size); - else - size = getxattr(accpath, name, value, size); -#elif HAVE_FGETEA - if (fd >= 0) +#elif ARCHIVE_XATTR_DARWIN + size = fgetxattr(fd, name, value, size, 0, 0); +#elif ARCHIVE_XATTR_AIX size = fgetea(fd, name, value, size); - else if (!a->follow_symlinks) +#endif + } else if (!a->follow_symlinks) { +#if ARCHIVE_XATTR_LINUX + size = lgetxattr(accpath, name, value, size); +#elif ARCHIVE_XATTR_DARWIN + size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW); +#elif ARCHIVE_XATTR_AIX size = lgetea(accpath, name, value, size); - else +#endif + } else { +#if ARCHIVE_XATTR_LINUX + size = getxattr(accpath, name, value, size); +#elif ARCHIVE_XATTR_DARWIN + size = getxattr(accpath, name, value, size, 0, 0); +#elif ARCHIVE_XATTR_AIX size = getea(accpath, name, value, size); #endif + } if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, name, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char *list, *p; const char *path; ssize_t list_size; path = NULL; if (*fd < 0) { - path = archive_entry_sourcepath(entry); - if (path == NULL || (a->tree != NULL && - a->tree_enter_working_dir(a->tree) != 0)) - path = archive_entry_pathname(entry); - if (path == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Couldn't determine file path to read " - "extended attributes"); + path = archive_read_disk_entry_setup_path(a, entry, fd); + if (path == NULL) return (ARCHIVE_WARN); - } - if (a->tree != NULL && (a->follow_symlinks || - archive_entry_filetype(entry) != AE_IFLNK)) { - *fd = a->open_on_current_dir(a->tree, - path, O_RDONLY | O_NONBLOCK); - } } -#if HAVE_FLISTXATTR - if (*fd >= 0) + if (*fd >= 0) { +#if ARCHIVE_XATTR_LINUX list_size = flistxattr(*fd, NULL, 0); - else if (!a->follow_symlinks) - list_size = llistxattr(path, NULL, 0); - else - list_size = listxattr(path, NULL, 0); -#elif HAVE_FLISTEA - if (*fd >= 0) +#elif ARCHIVE_XATTR_DARWIN + list_size = flistxattr(*fd, NULL, 0, 0); +#elif ARCHIVE_XATTR_AIX list_size = flistea(*fd, NULL, 0); - else if (!a->follow_symlinks) +#endif + } else if (!a->follow_symlinks) { +#if ARCHIVE_XATTR_LINUX + list_size = llistxattr(path, NULL, 0); +#elif ARCHIVE_XATTR_DARWIN + list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW); +#elif ARCHIVE_XATTR_AIX list_size = llistea(path, NULL, 0); - else +#endif + } else { +#if ARCHIVE_XATTR_LINUX + list_size = listxattr(path, NULL, 0); +#elif ARCHIVE_XATTR_DARWIN + list_size = listxattr(path, NULL, 0, 0); +#elif ARCHIVE_XATTR_AIX list_size = listea(path, NULL, 0); #endif + } if (list_size == -1) { if (errno == ENOTSUP || errno == ENOSYS) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } -#if HAVE_FLISTXATTR - if (*fd >= 0) + if (*fd >= 0) { +#if ARCHIVE_XATTR_LINUX list_size = flistxattr(*fd, list, list_size); - else if (!a->follow_symlinks) - list_size = llistxattr(path, list, list_size); - else - list_size = listxattr(path, list, list_size); -#elif HAVE_FLISTEA - if (*fd >= 0) +#elif ARCHIVE_XATTR_DARWIN + list_size = flistxattr(*fd, list, list_size, 0); +#elif ARCHIVE_XATTR_AIX list_size = flistea(*fd, list, list_size); - else if (!a->follow_symlinks) +#endif + } else if (!a->follow_symlinks) { +#if ARCHIVE_XATTR_LINUX + list_size = llistxattr(path, list, list_size); +#elif ARCHIVE_XATTR_DARWIN + list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW); +#elif ARCHIVE_XATTR_AIX list_size = llistea(path, list, list_size); - else +#endif + } else { +#if ARCHIVE_XATTR_LINUX + list_size = listxattr(path, list, list_size); +#elif ARCHIVE_XATTR_DARWIN + list_size = listxattr(path, list, list_size, 0); +#elif ARCHIVE_XATTR_AIX list_size = listea(path, list, list_size); #endif + } if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } for (p = list; (p - list) < list_size; p += strlen(p) + 1) { if (strncmp(p, "system.", 7) == 0 || strncmp(p, "xfsroot.", 8) == 0) continue; setup_xattr(a, entry, p, *fd, path); } free(list); return (ARCHIVE_OK); } -#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \ - HAVE_DECL_EXTATTR_NAMESPACE_USER +#elif ARCHIVE_XATTR_FREEBSD /* * FreeBSD extattr interface. */ /* TODO: Implement this. Follow the Linux model above, but * with FreeBSD-specific system calls, of course. Be careful * to not include the system extattrs that hold ACLs; we handle * those separately. */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd, const char *path); static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd, const char *accpath) { ssize_t size; void *value = NULL; if (fd >= 0) size = extattr_get_fd(fd, namespace, name, NULL, 0); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, NULL, 0); else size = extattr_get_file(accpath, namespace, name, NULL, 0); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (fd >= 0) size = extattr_get_fd(fd, namespace, name, value, size); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, value, size); else size = extattr_get_file(accpath, namespace, name, value, size); if (size == -1) { free(value); archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, fullname, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[512]; char *list, *p; ssize_t list_size; const char *path; int namespace = EXTATTR_NAMESPACE_USER; path = NULL; if (*fd < 0) { - path = archive_entry_sourcepath(entry); - if (path == NULL || (a->tree != NULL && - a->tree_enter_working_dir(a->tree) != 0)) - path = archive_entry_pathname(entry); - if (path == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Couldn't determine file path to read " - "extended attributes"); + path = archive_read_disk_entry_setup_path(a, entry, fd); + if (path == NULL) return (ARCHIVE_WARN); - } - if (a->tree != NULL && (a->follow_symlinks || - archive_entry_filetype(entry) != AE_IFLNK)) { - *fd = a->open_on_current_dir(a->tree, - path, O_RDONLY | O_NONBLOCK); - } } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, NULL, 0); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, NULL, 0); else list_size = extattr_list_file(path, namespace, NULL, 0); if (list_size == -1 && errno == EOPNOTSUPP) return (ARCHIVE_OK); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, list, list_size); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, list, list_size); else list_size = extattr_list_file(path, namespace, list, list_size); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } p = list; while ((p - list) < list_size) { size_t len = 255 & (int)*p; char *name; strcpy(buff, "user."); name = buff + strlen(buff); memcpy(name, p + 1, len); name[len] = '\0'; setup_xattr(a, entry, namespace, name, buff, *fd, path); p += 1 + len; } free(list); return (ARCHIVE_OK); } #else /* * Generic (stub) extended attribute support. */ static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if defined(HAVE_LINUX_FIEMAP_H) /* * Linux FIEMAP sparse interface. * * The FIEMAP ioctl returns an "extent" for each physical allocation * on disk. We need to process those to generate a more compact list * of logical file blocks. We also need to be very careful to use * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes * does not report allocations for newly-written data that hasn't * been synced to disk. * * It's important to return a minimal sparse file list because we want * to not trigger sparse file extensions if we don't have to, since * not all readers support them. */ static int setup_sparse_fiemap(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[4096]; struct fiemap *fm; struct fiemap_extent *fe; int64_t size; int count, do_fiemap, iters; int exit_sts = ARCHIVE_OK; + const char *path; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); if (*fd < 0) { - const char *path; - - path = archive_entry_sourcepath(entry); + path = archive_read_disk_entry_setup_path(a, entry, NULL); if (path == NULL) - path = archive_entry_pathname(entry); + return (ARCHIVE_FAILED); + if (a->tree != NULL) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); } /* Initialize buffer to avoid the error valgrind complains about. */ memset(buff, 0, sizeof(buff)); count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); fm = (struct fiemap *)buff; fm->fm_start = 0; fm->fm_length = ~0ULL;; fm->fm_flags = FIEMAP_FLAG_SYNC; fm->fm_extent_count = count; do_fiemap = 1; size = archive_entry_size(entry); for (iters = 0; ; ++iters) { int i, r; r = ioctl(*fd, FS_IOC_FIEMAP, fm); if (r < 0) { /* When something error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ goto exit_setup_sparse_fiemap; } if (fm->fm_mapped_extents == 0) { if (iters == 0) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } break; } fe = fm->fm_extents; for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { /* The fe_length of the last block does not * adjust itself to its size files. */ int64_t length = fe->fe_length; if (fe->fe_logical + length > (uint64_t)size) length -= fe->fe_logical + length - size; if (fe->fe_logical == 0 && length == size) { /* This is not sparse. */ do_fiemap = 0; break; } if (length > 0) archive_entry_sparse_add_entry(entry, fe->fe_logical, length); } if (fe->fe_flags & FIEMAP_EXTENT_LAST) do_fiemap = 0; } if (do_fiemap) { fe = fm->fm_extents + fm->fm_mapped_extents -1; fm->fm_start = fe->fe_logical + fe->fe_length; } else break; } exit_setup_sparse_fiemap: return (exit_sts); } #if !defined(SEEK_HOLE) || !defined(SEEK_DATA) static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { return setup_sparse_fiemap(a, entry, fd); } #endif #endif /* defined(HAVE_LINUX_FIEMAP_H) */ #if defined(SEEK_HOLE) && defined(SEEK_DATA) /* * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int64_t size; off_t initial_off; off_t off_s, off_e; int exit_sts = ARCHIVE_OK; int check_fully_sparse = 0; + const char *path; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); /* Does filesystem support the reporting of hole ? */ - if (*fd < 0 && a->tree != NULL) { - const char *path; - - path = archive_entry_sourcepath(entry); + if (*fd < 0) { + path = archive_read_disk_entry_setup_path(a, entry, fd); if (path == NULL) - path = archive_entry_pathname(entry); - *fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); - if (*fd < 0) { - archive_set_error(&a->archive, errno, - "Can't open `%s'", path); return (ARCHIVE_FAILED); - } } if (*fd >= 0) { #ifdef _PC_MIN_HOLE_SIZE if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); #endif initial_off = lseek(*fd, 0, SEEK_CUR); if (initial_off != 0) lseek(*fd, 0, SEEK_SET); } else { - const char *path; - - path = archive_entry_sourcepath(entry); - if (path == NULL) - path = archive_entry_pathname(entry); - #ifdef _PC_MIN_HOLE_SIZE if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); #endif *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); initial_off = 0; } #ifndef _PC_MIN_HOLE_SIZE /* Check if the underlying filesystem supports seek hole */ off_s = lseek(*fd, 0, SEEK_HOLE); if (off_s < 0) #if defined(HAVE_LINUX_FIEMAP_H) return setup_sparse_fiemap(a, entry, fd); #else goto exit_setup_sparse; #endif else if (off_s > 0) lseek(*fd, 0, SEEK_SET); #endif off_s = 0; size = archive_entry_size(entry); while (off_s < size) { off_s = lseek(*fd, off_s, SEEK_DATA); if (off_s == (off_t)-1) { if (errno == ENXIO) { /* no more hole */ if (archive_entry_sparse_count(entry) == 0) { /* Potentially a fully-sparse file. */ check_fully_sparse = 1; } break; } archive_set_error(&a->archive, errno, "lseek(SEEK_HOLE) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } off_e = lseek(*fd, off_s, SEEK_HOLE); if (off_e == (off_t)-1) { if (errno == ENXIO) { off_e = lseek(*fd, 0, SEEK_END); if (off_e != (off_t)-1) break;/* no more data */ } archive_set_error(&a->archive, errno, "lseek(SEEK_DATA) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } if (off_s == 0 && off_e == size) break;/* This is not sparse. */ archive_entry_sparse_add_entry(entry, off_s, off_e - off_s); off_s = off_e; } if (check_fully_sparse) { if (lseek(*fd, 0, SEEK_HOLE) == 0 && lseek(*fd, 0, SEEK_END) == size) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } } exit_setup_sparse: lseek(*fd, initial_off, SEEK_SET); return (exit_sts); } #elif !defined(HAVE_LINUX_FIEMAP_H) /* * Generic (stub) sparse support. */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ Index: stable/10/contrib/libarchive/libarchive/archive_read_disk_private.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_disk_private.h (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_disk_private.h (revision 316338) @@ -1,89 +1,98 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * 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. * * $FreeBSD$ */ #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif #ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED #define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED +#include "archive_platform_acl.h" + struct tree; struct archive_entry; struct archive_read_disk { struct archive archive; /* Reused by archive_read_next_header() */ struct archive_entry *entry; /* * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid, * following an old BSD convention. 'L' follows all symlinks, * 'P' follows none, 'H' follows symlinks only for the first * item. */ char symlink_mode; /* * Since symlink interaction changes, we need to track whether * we're following symlinks for the current item. 'L' mode above * sets this true, 'P' sets it false, 'H' changes it as we traverse. */ char follow_symlinks; /* Either 'L' or 'P'. */ /* Directory traversals. */ struct tree *tree; int (*open_on_current_dir)(struct tree*, const char *, int); int (*tree_current_dir_fd)(struct tree*); int (*tree_enter_working_dir)(struct tree*); /* Bitfield with ARCHIVE_READDISK_* tunables */ int flags; const char * (*lookup_gname)(void *private, int64_t gid); void (*cleanup_gname)(void *private); void *lookup_gname_data; const char * (*lookup_uname)(void *private, int64_t uid); void (*cleanup_uname)(void *private); void *lookup_uname_data; int (*metadata_filter_func)(struct archive *, void *, struct archive_entry *); void *metadata_filter_data; /* ARCHIVE_MATCH object. */ struct archive *matching; /* Callback function, this will be invoked when ARCHIVE_MATCH * archive_match_*_excluded_ae return true. */ void (*excluded_cb_func)(struct archive *, void *, struct archive_entry *); void *excluded_cb_data; }; +const char * +archive_read_disk_entry_setup_path(struct archive_read_disk *, + struct archive_entry *, int *); + +int +archive_read_disk_entry_setup_acls(struct archive_read_disk *, + struct archive_entry *, int *); #endif Index: stable/10/contrib/libarchive/libarchive/archive_read_open.3 =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_open.3 (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_open.3 (revision 316338) @@ -1,233 +1,233 @@ .\" Copyright (c) 2003-2011 Tim Kientzle .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd February 2, 2012 .Dt ARCHIVE_READ_OPEN 3 .Os .Sh NAME .Nm archive_read_open , .Nm archive_read_open2 , .Nm archive_read_open_fd , .Nm archive_read_open_FILE , .Nm archive_read_open_filename , .Nm archive_read_open_memory .Nd functions for reading streaming archives .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive.h .Ft int .Fo archive_read_open .Fa "struct archive *" .Fa "void *client_data" .Fa "archive_open_callback *" .Fa "archive_read_callback *" .Fa "archive_close_callback *" .Fc .Ft int .Fo archive_read_open2 .Fa "struct archive *" .Fa "void *client_data" .Fa "archive_open_callback *" .Fa "archive_read_callback *" .Fa "archive_skip_callback *" .Fa "archive_close_callback *" .Fc .Ft int .Fn archive_read_open_FILE "struct archive *" "FILE *file" .Ft int .Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size" .Ft int .Fo archive_read_open_filename .Fa "struct archive *" .Fa "const char *filename" .Fa "size_t block_size" .Fc .Ft int -.Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size" +.Fn archive_read_open_memory "struct archive *" "const void *buff" "size_t size" .Sh DESCRIPTION .Bl -tag -compact -width indent .It Fn archive_read_open The same as .Fn archive_read_open2 , except that the skip callback is assumed to be .Dv NULL . .It Fn archive_read_open2 Freeze the settings, open the archive, and prepare for reading entries. This is the most generic version of this call, which accepts four callback functions. Most clients will want to use .Fn archive_read_open_filename , .Fn archive_read_open_FILE , .Fn archive_read_open_fd , or .Fn archive_read_open_memory instead. The library invokes the client-provided functions to obtain raw bytes from the archive. .It Fn archive_read_open_FILE Like .Fn archive_read_open , except that it accepts a .Ft "FILE *" pointer. This function should not be used with tape drives or other devices that require strict I/O blocking. .It Fn archive_read_open_fd Like .Fn archive_read_open , except that it accepts a file descriptor and block size rather than a set of function pointers. Note that the file descriptor will not be automatically closed at end-of-archive. This function is safe for use with tape drives or other blocked devices. .It Fn archive_read_open_file This is a deprecated synonym for .Fn archive_read_open_filename . .It Fn archive_read_open_filename Like .Fn archive_read_open , except that it accepts a simple filename and a block size. A NULL filename represents standard input. This function is safe for use with tape drives or other blocked devices. .It Fn archive_read_open_memory Like .Fn archive_read_open , except that it accepts a pointer and size of a block of memory containing the archive data. .El .Pp A complete description of the .Tn struct archive and .Tn struct archive_entry objects can be found in the overview manual page for .Xr libarchive 3 . .Sh CLIENT CALLBACKS The callback functions must match the following prototypes: .Bl -item -offset indent .It .Ft typedef la_ssize_t .Fo archive_read_callback .Fa "struct archive *" .Fa "void *client_data" .Fa "const void **buffer" .Fc .It .Ft typedef la_int64_t .Fo archive_skip_callback .Fa "struct archive *" .Fa "void *client_data" .Fa "off_t request" .Fc .It .Ft typedef int .Fn archive_open_callback "struct archive *" "void *client_data" .It .Ft typedef int .Fn archive_close_callback "struct archive *" "void *client_data" .El .Pp The open callback is invoked by .Fn archive_open . It should return .Cm ARCHIVE_OK if the underlying file or data source is successfully opened. If the open fails, it should call .Fn archive_set_error to register an error code and message and return .Cm ARCHIVE_FATAL . .Pp The read callback is invoked whenever the library requires raw bytes from the archive. The read callback should read data into a buffer, set the .Li const void **buffer argument to point to the available data, and return a count of the number of bytes available. The library will invoke the read callback again only after it has consumed this data. The library imposes no constraints on the size of the data blocks returned. On end-of-file, the read callback should return zero. On error, the read callback should invoke .Fn archive_set_error to register an error code and message and return -1. .Pp The skip callback is invoked when the library wants to ignore a block of data. The return value is the number of bytes actually skipped, which may differ from the request. If the callback cannot skip data, it should return zero. If the skip callback is not provided (the function pointer is .Dv NULL ), the library will invoke the read function instead and simply discard the result. A skip callback can provide significant performance gains when reading uncompressed archives from slow disk drives or other media that can skip quickly. .Pp The close callback is invoked by archive_close when the archive processing is complete. The callback should return .Cm ARCHIVE_OK on success. On failure, the callback should invoke .Fn archive_set_error to register an error code and message and return .Cm ARCHIVE_FATAL. .\" .Sh EXAMPLE .\" .Sh RETURN VALUES These functions return .Cm ARCHIVE_OK on success, or .Cm ARCHIVE_FATAL . .\" .Sh ERRORS Detailed error codes and textual descriptions are available from the .Fn archive_errno and .Fn archive_error_string functions. .\" .Sh SEE ALSO .Xr tar 1 , .Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , .Xr tar 5 Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_cab.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_cab.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_cab.c (revision 316338) @@ -1,3353 +1,3353 @@ /*- * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" struct lzx_dec { /* Decoding status. */ int state; /* * Window to see last decoded data, from 32KBi to 2MBi. */ int w_size; int w_mask; /* Window buffer, which is a loop buffer. */ unsigned char *w_buff; /* The insert position to the window. */ int w_pos; /* The position where we can copy decoded code from the window. */ int copy_pos; /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; /* Translation reversal for x86 processor CALL byte sequence(E8). * This is used for LZX only. */ uint32_t translation_size; char translation; char block_type; #define VERBATIM_BLOCK 1 #define ALIGNED_OFFSET_BLOCK 2 #define UNCOMPRESSED_BLOCK 3 size_t block_size; size_t block_bytes_avail; /* Repeated offset. */ int r0, r1, r2; unsigned char rbytes[4]; int rbytes_avail; int length_header; int position_slot; int offset_bits; struct lzx_pos_tbl { int base; int footer_bits; } *pos_tbl; /* * Bit stream reader. */ struct lzx_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; unsigned char odd; char have_odd; } br; /* * Huffman coding. */ struct huffman { int len_size; int freq[17]; unsigned char *bitlen; /* * Use a index table. It's faster than searching a huffman * coding tree, which is a binary tree. But a use of a large * index table causes L1 cache read miss many times. */ #define HTBL_BITS 10 int max_bits; int shift_bits; int tbl_bits; int tree_used; int tree_avail; /* Direct access table. */ uint16_t *tbl; /* Binary tree table for extra bits over the direct access. */ struct htree_t { uint16_t left; uint16_t right; } *tree; } at, lt, mt, pt; int loop; int error; }; static const int slots[] = { 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 }; #define SLOT_BASE 15 #define SLOT_MAX 21/*->25*/ struct lzx_stream { const unsigned char *next_in; int64_t avail_in; int64_t total_in; unsigned char *next_out; int64_t avail_out; int64_t total_out; struct lzx_dec *ds; }; /* * Cabinet file definitions. */ /* CFHEADER offset */ #define CFHEADER_signature 0 #define CFHEADER_cbCabinet 8 #define CFHEADER_coffFiles 16 #define CFHEADER_versionMinor 24 #define CFHEADER_versionMajor 25 #define CFHEADER_cFolders 26 #define CFHEADER_cFiles 28 #define CFHEADER_flags 30 #define CFHEADER_setID 32 #define CFHEADER_iCabinet 34 #define CFHEADER_cbCFHeader 36 #define CFHEADER_cbCFFolder 38 #define CFHEADER_cbCFData 39 /* CFFOLDER offset */ #define CFFOLDER_coffCabStart 0 #define CFFOLDER_cCFData 4 #define CFFOLDER_typeCompress 6 #define CFFOLDER_abReserve 8 /* CFFILE offset */ #define CFFILE_cbFile 0 #define CFFILE_uoffFolderStart 4 #define CFFILE_iFolder 8 #define CFFILE_date_time 10 #define CFFILE_attribs 14 /* CFDATA offset */ #define CFDATA_csum 0 #define CFDATA_cbData 4 #define CFDATA_cbUncomp 6 -static const char *compression_name[] = { +static const char * const compression_name[] = { "NONE", "MSZIP", "Quantum", "LZX", }; struct cfdata { /* Sum value of this CFDATA. */ uint32_t sum; uint16_t compressed_size; uint16_t compressed_bytes_remaining; uint16_t uncompressed_size; uint16_t uncompressed_bytes_remaining; /* To know how many bytes we have decompressed. */ uint16_t uncompressed_avail; /* Offset from the beginning of compressed data of this CFDATA */ uint16_t read_offset; int64_t unconsumed; /* To keep memory image of this CFDATA to compute the sum. */ size_t memimage_size; unsigned char *memimage; /* Result of calculation of sum. */ uint32_t sum_calculated; unsigned char sum_extra[4]; int sum_extra_avail; const void *sum_ptr; }; struct cffolder { uint32_t cfdata_offset_in_cab; uint16_t cfdata_count; uint16_t comptype; #define COMPTYPE_NONE 0x0000 #define COMPTYPE_MSZIP 0x0001 #define COMPTYPE_QUANTUM 0x0002 #define COMPTYPE_LZX 0x0003 uint16_t compdata; const char *compname; /* At the time reading CFDATA */ struct cfdata cfdata; int cfdata_index; /* Flags to mark progress of decompression. */ char decompress_init; }; struct cffile { uint32_t uncompressed_size; uint32_t offset; time_t mtime; uint16_t folder; #define iFoldCONTINUED_FROM_PREV 0xFFFD #define iFoldCONTINUED_TO_NEXT 0xFFFE #define iFoldCONTINUED_PREV_AND_NEXT 0xFFFF unsigned char attr; #define ATTR_RDONLY 0x01 #define ATTR_NAME_IS_UTF 0x80 struct archive_string pathname; }; struct cfheader { /* Total bytes of all file size in a Cabinet. */ uint32_t total_bytes; uint32_t files_offset; uint16_t folder_count; uint16_t file_count; uint16_t flags; #define PREV_CABINET 0x0001 #define NEXT_CABINET 0x0002 #define RESERVE_PRESENT 0x0004 uint16_t setid; uint16_t cabinet; /* Version number. */ unsigned char major; unsigned char minor; unsigned char cffolder; unsigned char cfdata; /* All folders in a cabinet. */ struct cffolder *folder_array; /* All files in a cabinet. */ struct cffile *file_array; int file_index; }; struct cab { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; struct cffolder *entry_cffolder; struct cffile *entry_cffile; struct cfdata *entry_cfdata; /* Offset from beginning of a cabinet file. */ int64_t cab_offset; struct cfheader cfheader; struct archive_wstring ws; /* Flag to mark progress that an archive was read their first header.*/ char found_header; char end_of_archive; char end_of_entry; char end_of_entry_cleanup; char read_data_invoked; int64_t bytes_skipped; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; int init_default_conversion; struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; char format_name[64]; #ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif struct lzx_stream xstrm; }; static int archive_read_format_cab_bid(struct archive_read *, int); static int archive_read_format_cab_options(struct archive_read *, const char *, const char *); static int archive_read_format_cab_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cab_read_data_skip(struct archive_read *); static int archive_read_format_cab_cleanup(struct archive_read *); static int cab_skip_sfx(struct archive_read *); static time_t cab_dos_time(const unsigned char *); static int cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int cab_read_header(struct archive_read *); static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t); static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t); static void cab_checksum_update(struct archive_read *, size_t); static int cab_checksum_finish(struct archive_read *); static int cab_next_cfdata(struct archive_read *); static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_deflate(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_lzx(struct archive_read *, ssize_t *); static int64_t cab_consume_cfdata(struct archive_read *, int64_t); static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t); static int lzx_decode_init(struct lzx_stream *, int); static int lzx_read_blocks(struct lzx_stream *, int); static int lzx_decode_blocks(struct lzx_stream *, int); static void lzx_decode_free(struct lzx_stream *); static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t); static void lzx_cleanup_bitstream(struct lzx_stream *); static int lzx_decode(struct lzx_stream *, int); static int lzx_read_pre_tree(struct lzx_stream *); static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int); static int lzx_huffman_init(struct huffman *, size_t, int); static void lzx_huffman_free(struct huffman *); static int lzx_make_huffman_table(struct huffman *); static inline int lzx_decode_huffman(struct huffman *, unsigned); static int lzx_decode_huffman_tree(struct huffman *, unsigned, int); int archive_read_support_format_cab(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cab *cab; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cab"); cab = (struct cab *)calloc(1, sizeof(*cab)); if (cab == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate CAB data"); return (ARCHIVE_FATAL); } archive_string_init(&cab->ws); archive_wstring_ensure(&cab->ws, 256); r = __archive_read_register_format(a, cab, "cab", archive_read_format_cab_bid, archive_read_format_cab_options, archive_read_format_cab_read_header, archive_read_format_cab_read_data, archive_read_format_cab_read_data_skip, NULL, archive_read_format_cab_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cab); return (ARCHIVE_OK); } static int find_cab_magic(const char *p) { switch (p[4]) { case 0: /* * Note: Self-Extraction program has 'MSCF' string in their * program. If we were finding 'MSCF' string only, we got * wrong place for Cabinet header, thus, we have to check * following four bytes which are reserved and must be set * to zero. */ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) return 0; return 5; case 'F': return 1; case 'C': return 2; case 'S': return 3; case 'M': return 4; default: return 5; } } static int archive_read_format_cab_bid(struct archive_read *a, int best_bid) { const char *p; ssize_t bytes_avail, offset, window; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 64) return (-1); if ((p = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) return (64); /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward * up to 128k for a 'MSCF' marker. */ if (p[0] == 'M' && p[1] == 'Z') { offset = 0; window = 4096; while (offset < (1024 * 128)) { const char *h = __archive_read_ahead(a, offset + window, &bytes_avail); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 128) return (0); continue; } p = h + offset; while (p + 8 < h + bytes_avail) { int next; if ((next = find_cab_magic(p)) == 0) return (64); p += next; } offset = p - h; } } return (0); } static int archive_read_format_cab_options(struct archive_read *a, const char *key, const char *val) { struct cab *cab; int ret = ARCHIVE_FAILED; cab = (struct cab *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "cab: hdrcharset option needs a character-set name"); else { cab->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cab->sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int cab_skip_sfx(struct archive_read *a) { const char *p, *q; size_t skip; ssize_t bytes, window; window = 4096; for (;;) { const char *h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining size are less than window. */ window >>= 1; if (window < 128) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } continue; } p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the cab header. */ while (p + 8 < q) { int next; if ((next = find_cab_magic(p)) == 0) { skip = p - h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += next; } skip = p - h; __archive_read_consume(a, skip); } } static int truncated_error(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB header"); return (ARCHIVE_FATAL); } static ssize_t cab_strnlen(const unsigned char *p, size_t maxlen) { size_t i; for (i = 0; i <= maxlen; i++) { if (p[i] == 0) break; } if (i > maxlen) return (-1);/* invalid */ return ((ssize_t)i); } /* Read bytes as much as remaining. */ static const void * cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail) { const void *p; while (min > 0) { p = __archive_read_ahead(a, min, avail); if (p != NULL) return (p); min--; } return (NULL); } /* Convert a path separator '\' -> '/' */ static int cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr) { size_t i; int mb; /* Easy check if we have '\' in multi-byte string. */ mb = 0; for (i = 0; i < archive_strlen(fn); i++) { if (fn->s[i] == '\\') { if (mb) { /* This may be second byte of multi-byte * character. */ break; } fn->s[i] = '/'; mb = 0; } else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF)) mb = 1; else mb = 0; } if (i == archive_strlen(fn)) return (0); return (-1); } /* * Replace a character '\' with '/' in wide character. */ static void cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry) { const wchar_t *wp; size_t i; /* If a conversion to wide character failed, force the replacement. */ if ((wp = archive_entry_pathname_w(entry)) != NULL) { archive_wstrcpy(&(cab->ws), wp); for (i = 0; i < archive_strlen(&(cab->ws)); i++) { if (cab->ws.s[i] == L'\\') cab->ws.s[i] = L'/'; } archive_entry_copy_pathname_w(entry, cab->ws.s); } } /* * Read CFHEADER, CFFOLDER and CFFILE. */ static int cab_read_header(struct archive_read *a) { const unsigned char *p; struct cab *cab; struct cfheader *hd; size_t bytes, used; ssize_t len; int64_t skip; int err, i; int cur_folder, prev_folder; uint32_t offset32; a->archive.archive_format = ARCHIVE_FORMAT_CAB; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "CAB"; if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); cab = (struct cab *)(a->format->data); if (cab->found_header == 0 && p[0] == 'M' && p[1] == 'Z') { /* This is an executable? Must be self-extracting... */ err = cab_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); /* Re-read header after processing the SFX. */ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); } cab->cab_offset = 0; /* * Read CFHEADER. */ hd = &cab->cfheader; if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' || p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet); hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles); hd->minor = p[CFHEADER_versionMinor]; hd->major = p[CFHEADER_versionMajor]; hd->folder_count = archive_le16dec(p + CFHEADER_cFolders); if (hd->folder_count == 0) goto invalid; hd->file_count = archive_le16dec(p + CFHEADER_cFiles); if (hd->file_count == 0) goto invalid; hd->flags = archive_le16dec(p + CFHEADER_flags); hd->setid = archive_le16dec(p + CFHEADER_setID); hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet); used = CFHEADER_iCabinet + 2; if (hd->flags & RESERVE_PRESENT) { uint16_t cfheader; cfheader = archive_le16dec(p + CFHEADER_cbCFHeader); if (cfheader > 60000U) goto invalid; hd->cffolder = p[CFHEADER_cbCFFolder]; hd->cfdata = p[CFHEADER_cbCFData]; used += 4;/* cbCFHeader, cbCFFolder and cbCFData */ used += cfheader;/* abReserve */ } else hd->cffolder = 0;/* Avoid compiling warning. */ if (hd->flags & PREV_CABINET) { /* How many bytes are used for szCabinetPrev. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskPrev. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; } if (hd->flags & NEXT_CABINET) { /* How many bytes are used for szCabinetNext. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskNext. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; } __archive_read_consume(a, used); cab->cab_offset += used; used = 0; /* * Read CFFOLDER. */ hd->folder_array = (struct cffolder *)calloc( hd->folder_count, sizeof(struct cffolder)); if (hd->folder_array == NULL) goto nomem; bytes = 8; if (hd->flags & RESERVE_PRESENT) bytes += hd->cffolder; bytes *= hd->folder_count; if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL) return (truncated_error(a)); offset32 = 0; for (i = 0; i < hd->folder_count; i++) { struct cffolder *folder = &(hd->folder_array[i]); folder->cfdata_offset_in_cab = archive_le32dec(p + CFFOLDER_coffCabStart); folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData); folder->comptype = archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F; folder->compdata = archive_le16dec(p+CFFOLDER_typeCompress) >> 8; /* Get a compression name. */ if (folder->comptype < sizeof(compression_name) / sizeof(compression_name[0])) folder->compname = compression_name[folder->comptype]; else folder->compname = "UNKNOWN"; p += 8; used += 8; if (hd->flags & RESERVE_PRESENT) { p += hd->cffolder;/* abReserve */ used += hd->cffolder; } /* * Sanity check if each data is acceptable. */ if (offset32 >= folder->cfdata_offset_in_cab) goto invalid; offset32 = folder->cfdata_offset_in_cab; /* Set a request to initialize zlib for the CFDATA of * this folder. */ folder->decompress_init = 0; } __archive_read_consume(a, used); cab->cab_offset += used; /* * Read CFFILE. */ /* Seek read pointer to the offset of CFFILE if needed. */ skip = (int64_t)hd->files_offset - cab->cab_offset; if (skip < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFFILE %jd < %jd", (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip) { __archive_read_consume(a, skip); cab->cab_offset += skip; } /* Allocate memory for CFDATA */ hd->file_array = (struct cffile *)calloc( hd->file_count, sizeof(struct cffile)); if (hd->file_array == NULL) goto nomem; prev_folder = -1; for (i = 0; i < hd->file_count; i++) { struct cffile *file = &(hd->file_array[i]); ssize_t avail; if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) return (truncated_error(a)); file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile); file->offset = archive_le32dec(p + CFFILE_uoffFolderStart); file->folder = archive_le16dec(p + CFFILE_iFolder); file->mtime = cab_dos_time(p + CFFILE_date_time); file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs); __archive_read_consume(a, 16); cab->cab_offset += 16; if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p, avail-1)) <= 0) goto invalid; /* Copy a pathname. */ archive_string_init(&(file->pathname)); archive_strncpy(&(file->pathname), p, len); __archive_read_consume(a, len + 1); cab->cab_offset += len + 1; /* * Sanity check if each data is acceptable. */ if (file->uncompressed_size > 0x7FFF8000) goto invalid;/* Too large */ if ((int64_t)file->offset + (int64_t)file->uncompressed_size > ARCHIVE_LITERAL_LL(0x7FFF8000)) goto invalid;/* Too large */ switch (file->folder) { case iFoldCONTINUED_TO_NEXT: /* This must be last file in a folder. */ if (i != hd->file_count -1) goto invalid; cur_folder = hd->folder_count -1; break; case iFoldCONTINUED_PREV_AND_NEXT: /* This must be only one file in a folder. */ if (hd->file_count != 1) goto invalid; /* FALL THROUGH */ case iFoldCONTINUED_FROM_PREV: /* This must be first file in a folder. */ if (i != 0) goto invalid; prev_folder = cur_folder = 0; offset32 = file->offset; break; default: if (file->folder >= hd->folder_count) goto invalid; cur_folder = file->folder; break; } /* Dot not back track. */ if (cur_folder < prev_folder) goto invalid; if (cur_folder != prev_folder) offset32 = 0; prev_folder = cur_folder; /* Make sure there are not any blanks from last file * contents. */ if (offset32 != file->offset) goto invalid; offset32 += file->uncompressed_size; /* CFDATA is available for file contents. */ if (file->uncompressed_size > 0 && hd->folder_array[cur_folder].cfdata_count == 0) goto invalid; } if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Multivolume cabinet file is unsupported"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CAB header"); return (ARCHIVE_FATAL); nomem: archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for CAB data"); return (ARCHIVE_FATAL); } static int archive_read_format_cab_read_header(struct archive_read *a, struct archive_entry *entry) { struct cab *cab; struct cfheader *hd; struct cffolder *prev_folder; struct cffile *file; struct archive_string_conv *sconv; int err = ARCHIVE_OK, r; cab = (struct cab *)(a->format->data); if (cab->found_header == 0) { err = cab_read_header(a); if (err < ARCHIVE_WARN) return (err); /* We've found the header. */ cab->found_header = 1; } hd = &cab->cfheader; if (hd->file_index >= hd->file_count) { cab->end_of_archive = 1; return (ARCHIVE_EOF); } file = &hd->file_array[hd->file_index++]; cab->end_of_entry = 0; cab->end_of_entry_cleanup = 0; cab->entry_compressed_bytes_read = 0; cab->entry_uncompressed_bytes_read = 0; cab->entry_unconsumed = 0; cab->entry_cffile = file; /* * Choose a proper folder. */ prev_folder = cab->entry_cffolder; switch (file->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_PREV_AND_NEXT: cab->entry_cffolder = &hd->folder_array[0]; break; case iFoldCONTINUED_TO_NEXT: cab->entry_cffolder = &hd->folder_array[hd->folder_count-1]; break; default: cab->entry_cffolder = &hd->folder_array[file->folder]; break; } /* If a cffolder of this file is changed, reset a cfdata to read * file contents from next cfdata. */ if (prev_folder != cab->entry_cffolder) cab->entry_cfdata = NULL; /* If a pathname is UTF-8, prepare a string conversion object * for UTF-8 and use it. */ if (file->attr & ATTR_NAME_IS_UTF) { if (cab->sconv_utf8 == NULL) { cab->sconv_utf8 = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (cab->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = cab->sconv_utf8; } else if (cab->sconv != NULL) { /* Choose the conversion specified by the option. */ sconv = cab->sconv; } else { /* Choose the default conversion. */ if (!cab->init_default_conversion) { cab->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cab->init_default_conversion = 1; } sconv = cab->sconv_default; } /* * Set a default value and common data */ r = cab_convert_path_separator_1(&(file->pathname), file->attr); if (archive_entry_copy_pathname_l(entry, file->pathname.s, archive_strlen(&(file->pathname)), sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(sconv)); err = ARCHIVE_WARN; } if (r < 0) { /* Convert a path separator '\' -> '/' */ cab_convert_path_separator_2(cab, entry); } archive_entry_set_size(entry, file->uncompressed_size); if (file->attr & ATTR_RDONLY) archive_entry_set_mode(entry, AE_IFREG | 0555); else archive_entry_set_mode(entry, AE_IFREG | 0666); archive_entry_set_mtime(entry, file->mtime, 0); cab->entry_bytes_remaining = file->uncompressed_size; cab->entry_offset = 0; /* We don't need compress data. */ if (file->uncompressed_size == 0) cab->end_of_entry_cleanup = cab->end_of_entry = 1; /* Set up a more descriptive format name. */ sprintf(cab->format_name, "CAB %d.%d (%s)", hd->major, hd->minor, cab->entry_cffolder->compname); a->archive.archive_format_name = cab->format_name; return (err); } static int archive_read_format_cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct cab *cab = (struct cab *)(a->format->data); int r; switch (cab->entry_cffile->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_PREV_AND_NEXT: *buff = NULL; *size = 0; *offset = 0; archive_clear_error(&a->archive); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore this file split in multivolume."); return (ARCHIVE_FAILED); default: break; } if (cab->read_data_invoked == 0) { if (cab->bytes_skipped) { if (cab->entry_cfdata == NULL) { r = cab_next_cfdata(a); if (r < 0) return (r); } if (cab_consume_cfdata(a, cab->bytes_skipped) < 0) return (ARCHIVE_FATAL); cab->bytes_skipped = 0; } cab->read_data_invoked = 1; } if (cab->entry_unconsumed) { /* Consume as much as the compressor actually used. */ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed); cab->entry_unconsumed = 0; if (r < 0) return (r); } if (cab->end_of_archive || cab->end_of_entry) { if (!cab->end_of_entry_cleanup) { /* End-of-entry cleanup done. */ cab->end_of_entry_cleanup = 1; } *offset = cab->entry_offset; *size = 0; *buff = NULL; return (ARCHIVE_EOF); } return (cab_read_data(a, buff, size, offset)); } static uint32_t cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; unsigned u32num; uint32_t sum; u32num = (unsigned)bytes / 4; sum = seed; b = p; for (;u32num > 0; --u32num) { sum ^= archive_le32dec(b); b += 4; } return (sum); } static uint32_t cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; uint32_t sum; uint32_t t; sum = cab_checksum_cfdata_4(p, bytes, seed); b = p; b += bytes & ~3; t = 0; switch (bytes & 3) { case 3: t |= ((uint32_t)(*b++)) << 16; /* FALL THROUGH */ case 2: t |= ((uint32_t)(*b++)) << 8; /* FALL THROUGH */ case 1: t |= *b; /* FALL THROUGH */ default: break; } sum ^= t; return (sum); } static void cab_checksum_update(struct archive_read *a, size_t bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; const unsigned char *p; size_t sumbytes; if (cfdata->sum == 0 || cfdata->sum_ptr == NULL) return; /* * Calculate the sum of this CFDATA. * Make sure CFDATA must be calculated in four bytes. */ p = cfdata->sum_ptr; sumbytes = bytes; if (cfdata->sum_extra_avail) { while (cfdata->sum_extra_avail < 4 && sumbytes > 0) { cfdata->sum_extra[ cfdata->sum_extra_avail++] = *p++; sumbytes--; } if (cfdata->sum_extra_avail == 4) { cfdata->sum_calculated = cab_checksum_cfdata_4( cfdata->sum_extra, 4, cfdata->sum_calculated); cfdata->sum_extra_avail = 0; } } if (sumbytes) { int odd = sumbytes & 3; if (sumbytes - odd > 0) cfdata->sum_calculated = cab_checksum_cfdata_4( p, sumbytes - odd, cfdata->sum_calculated); if (odd) memcpy(cfdata->sum_extra, p + sumbytes - odd, odd); cfdata->sum_extra_avail = odd; } cfdata->sum_ptr = NULL; } static int cab_checksum_finish(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; int l; /* Do not need to compute a sum. */ if (cfdata->sum == 0) return (ARCHIVE_OK); /* * Calculate the sum of remaining CFDATA. */ if (cfdata->sum_extra_avail) { cfdata->sum_calculated = cab_checksum_cfdata(cfdata->sum_extra, cfdata->sum_extra_avail, cfdata->sum_calculated); cfdata->sum_extra_avail = 0; } l = 4; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; cfdata->sum_calculated = cab_checksum_cfdata( cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated); if (cfdata->sum_calculated != cfdata->sum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error CFDATA[%d] %x:%x in %d bytes", cab->entry_cffolder->cfdata_index -1, cfdata->sum, cfdata->sum_calculated, cfdata->compressed_size); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } /* * Read CFDATA if needed. */ static int cab_next_cfdata(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; /* There are remaining bytes in current CFDATA, use it first. */ if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0) return (ARCHIVE_OK); if (cfdata == NULL) { int64_t skip; cab->entry_cffolder->cfdata_index = 0; /* Seek read pointer to the offset of CFDATA if needed. */ skip = cab->entry_cffolder->cfdata_offset_in_cab - cab->cab_offset; if (skip < 0) { int folder_index; switch (cab->entry_cffile->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_PREV_AND_NEXT: folder_index = 0; break; case iFoldCONTINUED_TO_NEXT: folder_index = cab->cfheader.folder_count-1; break; default: folder_index = cab->entry_cffile->folder; break; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFDATA in folder(%d) %jd < %jd", folder_index, (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip > 0) { if (__archive_read_consume(a, skip) < 0) return (ARCHIVE_FATAL); cab->cab_offset = cab->entry_cffolder->cfdata_offset_in_cab; } } /* * Read a CFDATA. */ if (cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) { const unsigned char *p; int l; cfdata = &(cab->entry_cffolder->cfdata); cab->entry_cffolder->cfdata_index++; cab->entry_cfdata = cfdata; cfdata->sum_calculated = 0; cfdata->sum_extra_avail = 0; cfdata->sum_ptr = NULL; l = 8; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; if ((p = __archive_read_ahead(a, l, NULL)) == NULL) return (truncated_error(a)); cfdata->sum = archive_le32dec(p + CFDATA_csum); cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData); cfdata->compressed_bytes_remaining = cfdata->compressed_size; cfdata->uncompressed_size = archive_le16dec(p + CFDATA_cbUncomp); cfdata->uncompressed_bytes_remaining = cfdata->uncompressed_size; cfdata->uncompressed_avail = 0; cfdata->read_offset = 0; cfdata->unconsumed = 0; /* * Sanity check if data size is acceptable. */ if (cfdata->compressed_size == 0 || cfdata->compressed_size > (0x8000+6144)) goto invalid; if (cfdata->uncompressed_size > 0x8000) goto invalid; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: break; case iFoldCONTINUED_FROM_PREV: default: goto invalid; } } /* If CFDATA is not last in a folder, an uncompressed * size must be 0x8000(32KBi) */ if ((cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) && cfdata->uncompressed_size != 0x8000) goto invalid; /* A compressed data size and an uncompressed data size must * be the same in no compression mode. */ if (cab->entry_cffolder->comptype == COMPTYPE_NONE && cfdata->compressed_size != cfdata->uncompressed_size) goto invalid; /* * Save CFDATA image for sum check. */ if (cfdata->memimage_size < (size_t)l) { free(cfdata->memimage); cfdata->memimage = malloc(l); if (cfdata->memimage == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for CAB data"); return (ARCHIVE_FATAL); } cfdata->memimage_size = l; } memcpy(cfdata->memimage, p, l); /* Consume bytes as much as we used. */ __archive_read_consume(a, l); cab->cab_offset += l; } else if (cab->entry_cffolder->cfdata_count > 0) { /* Run out of all CFDATA in a folder. */ cfdata->compressed_size = 0; cfdata->uncompressed_size = 0; cfdata->compressed_bytes_remaining = 0; cfdata->uncompressed_bytes_remaining = 0; } else { /* Current folder does not have any CFDATA. */ cfdata = &(cab->entry_cffolder->cfdata); cab->entry_cfdata = cfdata; memset(cfdata, 0, sizeof(*cfdata)); } return (ARCHIVE_OK); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } /* * Read ahead CFDATA. */ static const void * cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); int err; err = cab_next_cfdata(a); if (err < ARCHIVE_OK) { *avail = err; return (NULL); } switch (cab->entry_cffolder->comptype) { case COMPTYPE_NONE: return (cab_read_ahead_cfdata_none(a, avail)); case COMPTYPE_MSZIP: return (cab_read_ahead_cfdata_deflate(a, avail)); case COMPTYPE_LZX: return (cab_read_ahead_cfdata_lzx(a, avail)); default: /* Unsupported compression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported CAB compression : %s", cab->entry_cffolder->compname); *avail = ARCHIVE_FAILED; return (NULL); } } /* * Read ahead CFDATA as uncompressed data. */ static const void * cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; cfdata = cab->entry_cfdata; /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ d = __archive_read_ahead(a, 1, avail); if (*avail <= 0) { *avail = truncated_error(a); return (NULL); } if (*avail > cfdata->uncompressed_bytes_remaining) *avail = cfdata->uncompressed_bytes_remaining; cfdata->uncompressed_avail = cfdata->uncompressed_size; cfdata->unconsumed = *avail; cfdata->sum_ptr = d; return (d); } /* * Read ahead CFDATA as deflate data. */ #ifdef HAVE_ZLIB_H static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; int r, mszip; uint16_t uavail; char eod = 0; cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { cab->uncompressed_buffer_size = 0x8000; cab->uncompressed_buffer = (unsigned char *)malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for CAB reader"); *avail = ARCHIVE_FATAL; return (NULL); } } uavail = cfdata->uncompressed_avail; if (uavail == cfdata->uncompressed_size) { d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; return (d); } if (!cab->entry_cffolder->decompress_init) { cab->stream.next_in = NULL; cab->stream.avail_in = 0; cab->stream.total_in = 0; cab->stream.next_out = NULL; cab->stream.avail_out = 0; cab->stream.total_out = 0; if (cab->stream_valid) r = inflateReset(&cab->stream); else r = inflateInit2(&cab->stream, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize deflate decompression."); *avail = ARCHIVE_FATAL; return (NULL); } /* Stream structure has been set up. */ cab->stream_valid = 1; /* We've initialized decompression for this stream. */ cab->entry_cffolder->decompress_init = 1; } if (cfdata->compressed_bytes_remaining == cfdata->compressed_size) mszip = 2; else mszip = 0; eod = 0; cab->stream.total_out = uavail; /* * We always uncompress all data in current CFDATA. */ while (!eod && cab->stream.total_out < cfdata->uncompressed_size) { ssize_t bytes_avail; cab->stream.next_out = cab->uncompressed_buffer + cab->stream.total_out; cab->stream.avail_out = cfdata->uncompressed_size - cab->stream.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } if (bytes_avail > cfdata->compressed_bytes_remaining) bytes_avail = cfdata->compressed_bytes_remaining; /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the * next_in pointer, only reads it). The result: this ugly * cast to remove 'const'. */ cab->stream.next_in = (Bytef *)(uintptr_t)d; cab->stream.avail_in = (uInt)bytes_avail; cab->stream.total_in = 0; /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */ if (mszip > 0) { if (bytes_avail <= 0) goto nomszip; if (bytes_avail <= mszip) { if (mszip == 2) { if (cab->stream.next_in[0] != 0x43) goto nomszip; if (bytes_avail > 1 && cab->stream.next_in[1] != 0x4b) goto nomszip; } else if (cab->stream.next_in[0] != 0x4b) goto nomszip; cfdata->unconsumed = bytes_avail; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata( a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } mszip -= (int)bytes_avail; continue; } if (mszip == 1 && cab->stream.next_in[0] != 0x4b) goto nomszip; else if (cab->stream.next_in[0] != 0x43 || cab->stream.next_in[1] != 0x4b) goto nomszip; cab->stream.next_in += mszip; cab->stream.avail_in -= mszip; cab->stream.total_in += mszip; mszip = 0; } r = inflate(&cab->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: eod = 1; break; default: goto zlibfailed; } cfdata->unconsumed = cab->stream.total_in; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } uavail = (uint16_t)cab->stream.total_out; if (uavail < cfdata->uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid uncompressed size (%d < %d)", uavail, cfdata->uncompressed_size); *avail = ARCHIVE_FATAL; return (NULL); } /* * Note: I suspect there is a bug in makecab.exe because, in rare * case, compressed bytes are still remaining regardless we have * gotten all uncompressed bytes, which size is recorded in CFDATA, * as much as we need, and we have to use the garbage so as to * correctly compute the sum of CFDATA accordingly. */ if (cfdata->compressed_bytes_remaining > 0) { ssize_t bytes_avail; d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } cfdata->unconsumed = cfdata->compressed_bytes_remaining; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } /* * Set dictionary data for decompressing of next CFDATA, which * in the same folder. This is why we always do decompress CFDATA * even if beginning CFDATA or some of CFDATA are not used in * skipping file data. */ if (cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) { r = inflateReset(&cab->stream); if (r != Z_OK) goto zlibfailed; r = inflateSetDictionary(&cab->stream, cab->uncompressed_buffer, cfdata->uncompressed_size); if (r != Z_OK) goto zlibfailed; } d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; cfdata->uncompressed_avail = uavail; return (d); zlibfailed: switch (r) { case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for deflate decompression"); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Deflate decompression failed (%d)", r); break; } *avail = ARCHIVE_FATAL; return (NULL); nomszip: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "CFDATA incorrect(no MSZIP signature)"); *avail = ARCHIVE_FATAL; return (NULL); } #else /* HAVE_ZLIB_H */ static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { *avail = ARCHIVE_FATAL; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "libarchive compiled without deflate support (no libz)"); return (NULL); } #endif /* HAVE_ZLIB_H */ static const void * cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; int r; uint16_t uavail; cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { cab->uncompressed_buffer_size = 0x8000; cab->uncompressed_buffer = (unsigned char *)malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for CAB reader"); *avail = ARCHIVE_FATAL; return (NULL); } } uavail = cfdata->uncompressed_avail; if (uavail == cfdata->uncompressed_size) { d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; return (d); } if (!cab->entry_cffolder->decompress_init) { r = lzx_decode_init(&cab->xstrm, cab->entry_cffolder->compdata); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize LZX decompression."); *avail = ARCHIVE_FATAL; return (NULL); } /* We've initialized decompression for this stream. */ cab->entry_cffolder->decompress_init = 1; } /* Clean up remaining bits of previous CFDATA. */ lzx_cleanup_bitstream(&cab->xstrm); cab->xstrm.total_out = uavail; while (cab->xstrm.total_out < cfdata->uncompressed_size) { ssize_t bytes_avail; cab->xstrm.next_out = cab->uncompressed_buffer + cab->xstrm.total_out; cab->xstrm.avail_out = cfdata->uncompressed_size - cab->xstrm.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB file data"); *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_avail > cfdata->compressed_bytes_remaining) bytes_avail = cfdata->compressed_bytes_remaining; cab->xstrm.next_in = d; cab->xstrm.avail_in = bytes_avail; cab->xstrm.total_in = 0; r = lzx_decode(&cab->xstrm, cfdata->compressed_bytes_remaining == bytes_avail); switch (r) { case ARCHIVE_OK: case ARCHIVE_EOF: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LZX decompression failed (%d)", r); *avail = ARCHIVE_FATAL; return (NULL); } cfdata->unconsumed = cab->xstrm.total_in; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } uavail = (uint16_t)cab->xstrm.total_out; /* * Make sure a read pointer advances to next CFDATA. */ if (cfdata->compressed_bytes_remaining > 0) { ssize_t bytes_avail; d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } cfdata->unconsumed = cfdata->compressed_bytes_remaining; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } /* * Translation reversal of x86 processor CALL byte sequence(E8). */ lzx_translation(&cab->xstrm, cab->uncompressed_buffer, cfdata->uncompressed_size, (cab->entry_cffolder->cfdata_index-1) * 0x8000); d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; cfdata->uncompressed_avail = uavail; return (d); } /* * Consume CFDATA. * We always decompress CFDATA to consume CFDATA as much as we need * in uncompressed bytes because all CFDATA in a folder are related * so we do not skip any CFDATA without decompressing. * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for * the CFFILE is remaining bytes of previous Multivolume CAB file. */ static int64_t cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; int64_t cbytes, rbytes; int err; rbytes = cab_minimum_consume_cfdata(a, consumed_bytes); if (rbytes < 0) return (ARCHIVE_FATAL); cfdata = cab->entry_cfdata; while (rbytes > 0) { ssize_t avail; if (cfdata->compressed_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } cbytes = cfdata->uncompressed_bytes_remaining; if (cbytes > rbytes) cbytes = rbytes; rbytes -= cbytes; if (cfdata->uncompressed_avail == 0 && (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT || cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) { /* We have not read any data yet. */ if (cbytes == cfdata->uncompressed_bytes_remaining) { /* Skip whole current CFDATA. */ __archive_read_consume(a, cfdata->compressed_size); cab->cab_offset += cfdata->compressed_size; cfdata->compressed_bytes_remaining = 0; cfdata->uncompressed_bytes_remaining = 0; err = cab_next_cfdata(a); if (err < 0) return (err); cfdata = cab->entry_cfdata; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_FROM_PREV: rbytes = 0; break; default: break; } } continue; } cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; break; } else if (cbytes == 0) { err = cab_next_cfdata(a); if (err < 0) return (err); cfdata = cab->entry_cfdata; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_FROM_PREV: return (ARCHIVE_FATAL); default: break; } } continue; } while (cbytes > 0) { (void)cab_read_ahead_cfdata(a, &avail); if (avail <= 0) return (ARCHIVE_FATAL); if (avail > cbytes) avail = (ssize_t)cbytes; if (cab_minimum_consume_cfdata(a, avail) < 0) return (ARCHIVE_FATAL); cbytes -= avail; } } return (consumed_bytes); } /* * Consume CFDATA as much as we have already gotten and * compute the sum of CFDATA. */ static int64_t cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; int64_t cbytes, rbytes; int err; cfdata = cab->entry_cfdata; rbytes = consumed_bytes; if (cab->entry_cffolder->comptype == COMPTYPE_NONE) { if (consumed_bytes < cfdata->unconsumed) cbytes = consumed_bytes; else cbytes = cfdata->unconsumed; rbytes -= cbytes; cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; cfdata->unconsumed -= cbytes; } else { cbytes = cfdata->uncompressed_avail - cfdata->read_offset; if (cbytes > 0) { if (consumed_bytes < cbytes) cbytes = consumed_bytes; rbytes -= cbytes; cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; } if (cfdata->unconsumed) { cbytes = cfdata->unconsumed; cfdata->unconsumed = 0; } else cbytes = 0; } if (cbytes) { /* Compute the sum. */ cab_checksum_update(a, (size_t)cbytes); /* Consume as much as the compressor actually used. */ __archive_read_consume(a, cbytes); cab->cab_offset += cbytes; cfdata->compressed_bytes_remaining -= (uint16_t)cbytes; if (cfdata->compressed_bytes_remaining == 0) { err = cab_checksum_finish(a); if (err < 0) return (err); } } return (rbytes); } /* * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * cab->end_of_entry if it consumes all of the data. */ static int cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct cab *cab = (struct cab *)(a->format->data); ssize_t bytes_avail; if (cab->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = cab->entry_offset; cab->end_of_entry = 1; return (ARCHIVE_OK); } *buff = cab_read_ahead_cfdata(a, &bytes_avail); if (bytes_avail <= 0) { *buff = NULL; *size = 0; *offset = 0; if (bytes_avail == 0 && cab->entry_cfdata->uncompressed_size == 0) { /* All of CFDATA in a folder has been handled. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } else return ((int)bytes_avail); } if (bytes_avail > cab->entry_bytes_remaining) bytes_avail = (ssize_t)cab->entry_bytes_remaining; *size = bytes_avail; *offset = cab->entry_offset; cab->entry_offset += bytes_avail; cab->entry_bytes_remaining -= bytes_avail; if (cab->entry_bytes_remaining == 0) cab->end_of_entry = 1; cab->entry_unconsumed = bytes_avail; if (cab->entry_cffolder->comptype == COMPTYPE_NONE) { /* Don't consume more than current entry used. */ if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed) cab->entry_cfdata->unconsumed = cab->entry_unconsumed; } return (ARCHIVE_OK); } static int archive_read_format_cab_read_data_skip(struct archive_read *a) { struct cab *cab; int64_t bytes_skipped; int r; cab = (struct cab *)(a->format->data); if (cab->end_of_archive) return (ARCHIVE_EOF); if (!cab->read_data_invoked) { cab->bytes_skipped += cab->entry_bytes_remaining; cab->entry_bytes_remaining = 0; /* This entry is finished and done. */ cab->end_of_entry_cleanup = cab->end_of_entry = 1; return (ARCHIVE_OK); } if (cab->entry_unconsumed) { /* Consume as much as the compressor actually used. */ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed); cab->entry_unconsumed = 0; if (r < 0) return (r); } else if (cab->entry_cfdata == NULL) { r = cab_next_cfdata(a); if (r < 0) return (r); } /* if we've already read to end of data, we're done. */ if (cab->end_of_entry_cleanup) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* If the compression type is none(uncompressed), we've already * consumed data as much as the current entry size. */ if (cab->entry_cffolder->comptype == COMPTYPE_NONE && cab->entry_cfdata != NULL) cab->entry_cfdata->unconsumed = 0; /* This entry is finished and done. */ cab->end_of_entry_cleanup = cab->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_cab_cleanup(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfheader *hd = &cab->cfheader; int i; if (hd->folder_array != NULL) { for (i = 0; i < hd->folder_count; i++) free(hd->folder_array[i].cfdata.memimage); free(hd->folder_array); } if (hd->file_array != NULL) { for (i = 0; i < cab->cfheader.file_count; i++) archive_string_free(&(hd->file_array[i].pathname)); free(hd->file_array); } #ifdef HAVE_ZLIB_H if (cab->stream_valid) inflateEnd(&cab->stream); #endif lzx_decode_free(&cab->xstrm); archive_wstring_free(&cab->ws); free(cab->uncompressed_buffer); free(cab); (a->format->data) = NULL; return (ARCHIVE_OK); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t cab_dos_time(const unsigned char *p) { int msTime, msDate; struct tm ts; msDate = archive_le16dec(p); msTime = archive_le16dec(p+2); memset(&ts, 0, sizeof(ts)); ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ ts.tm_mday = msDate & 0x1f; /* Day of month. */ ts.tm_hour = (msTime >> 11) & 0x1f; ts.tm_min = (msTime >> 5) & 0x3f; ts.tm_sec = (msTime << 1) & 0x3e; ts.tm_isdst = -1; return (mktime(&ts)); } /***************************************************************** * * LZX decompression code. * *****************************************************************/ /* * Initialize LZX decoder. * * Returns ARCHIVE_OK if initialization was successful. * Returns ARCHIVE_FAILED if w_bits has unsupported value. * Returns ARCHIVE_FATAL if initialization failed; memory allocation * error occurred. */ static int lzx_decode_init(struct lzx_stream *strm, int w_bits) { struct lzx_dec *ds; int slot, w_size, w_slot; int base, footer; int base_inc[18]; if (strm->ds == NULL) { strm->ds = calloc(1, sizeof(*strm->ds)); if (strm->ds == NULL) return (ARCHIVE_FATAL); } ds = strm->ds; ds->error = ARCHIVE_FAILED; /* Allow bits from 15(32KBi) up to 21(2MBi) */ if (w_bits < SLOT_BASE || w_bits > SLOT_MAX) return (ARCHIVE_FAILED); ds->error = ARCHIVE_FATAL; /* * Alloc window */ w_size = ds->w_size; w_slot = slots[w_bits - SLOT_BASE]; ds->w_size = 1U << w_bits; ds->w_mask = ds->w_size -1; if (ds->w_buff == NULL || w_size != ds->w_size) { free(ds->w_buff); ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); free(ds->pos_tbl); ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot); if (ds->pos_tbl == NULL) return (ARCHIVE_FATAL); lzx_huffman_free(&(ds->mt)); } for (footer = 0; footer < 18; footer++) base_inc[footer] = 1 << footer; base = footer = 0; for (slot = 0; slot < w_slot; slot++) { int n; if (footer == 0) base = slot; else base += base_inc[footer]; if (footer < 17) { footer = -2; for (n = base; n; n >>= 1) footer++; if (footer <= 0) footer = 0; } ds->pos_tbl[slot].base = base; ds->pos_tbl[slot].footer_bits = footer; } ds->w_pos = 0; ds->state = 0; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; ds->r0 = ds->r1 = ds->r2 = 1; /* Initialize aligned offset tree. */ if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize pre-tree. */ if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize Main tree. */ if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize Length tree. */ if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->error = 0; return (ARCHIVE_OK); } /* * Release LZX decoder. */ static void lzx_decode_free(struct lzx_stream *strm) { if (strm->ds == NULL) return; free(strm->ds->w_buff); free(strm->ds->pos_tbl); lzx_huffman_free(&(strm->ds->at)); lzx_huffman_free(&(strm->ds->pt)); lzx_huffman_free(&(strm->ds->mt)); lzx_huffman_free(&(strm->ds->lt)); free(strm->ds); strm->ds = NULL; } /* * E8 Call Translation reversal. */ static void lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset) { struct lzx_dec *ds = strm->ds; unsigned char *b, *end; if (!ds->translation || size <= 10) return; b = p; end = b + size - 10; while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) { size_t i = b - (unsigned char *)p; int32_t cp, displacement, value; cp = (int32_t)(offset + (uint32_t)i); value = archive_le32dec(&b[1]); if (value >= -cp && value < (int32_t)ds->translation_size) { if (value >= 0) displacement = value - cp; else displacement = value + ds->translation_size; archive_le32enc(&b[1], (uint32_t)displacement); } b += 5; } } /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define lzx_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define lzx_br_bits(br, n) \ (((uint32_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define lzx_br_bits_forced(br, n) \ (((uint32_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : we met that strm->next_in is empty, we have to get following * bytes. */ #define lzx_br_read_ahead_0(strm, br, n) \ (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br)) /* True : the cache buffer has some bits as much as we need. * False : there are no enough bits in the cache buffer to be used, * we have to get following bytes if we could. */ #define lzx_br_read_ahead(strm, br, n) \ (lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n))) /* Notify how many bits we consumed. */ #define lzx_br_consume(br, n) ((br)->cache_avail -= (n)) #define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f) #define lzx_br_is_unaligned(br) ((br)->cache_avail & 0x0f) static const uint32_t cache_masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) { /* * x86 processor family can read misaligned data without an access error. */ int n = CACHE_BITS - br->cache_avail; for (;;) { switch (n >> 4) { case 4: if (strm->avail_in >= 8) { br->cache_buffer = ((uint64_t)strm->next_in[1]) << 56 | ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[3]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | ((uint32_t)strm->next_in[5]) << 24 | ((uint32_t)strm->next_in[4]) << 16 | ((uint32_t)strm->next_in[7]) << 8 | (uint32_t)strm->next_in[6]; strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); } break; case 3: if (strm->avail_in >= 6) { br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[0]) << 32 | ((uint32_t)strm->next_in[3]) << 24 | ((uint32_t)strm->next_in[2]) << 16 | ((uint32_t)strm->next_in[5]) << 8 | (uint32_t)strm->next_in[4]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); } break; case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } if (strm->avail_in < 2) { /* There is not enough compressed data to * fill up the cache buffer. */ if (strm->avail_in == 1) { br->odd = *strm->next_in++; strm->avail_in--; br->have_odd = 1; } return (0); } br->cache_buffer = (br->cache_buffer << 16) | archive_le16dec(strm->next_in); strm->next_in += 2; strm->avail_in -= 2; br->cache_avail += 16; n -= 16; } } static void lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br) { int n = CACHE_BITS - br->cache_avail; if (br->have_odd && n >= 16 && strm->avail_in > 0) { br->cache_buffer = (br->cache_buffer << 16) | ((uint16_t)(*strm->next_in)) << 8 | br->odd; strm->next_in++; strm->avail_in--; br->cache_avail += 16; br->have_odd = 0; } } static void lzx_cleanup_bitstream(struct lzx_stream *strm) { strm->ds->br.cache_avail = 0; strm->ds->br.have_odd = 0; } /* * Decode LZX. * * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty. * Please set available buffer and call this function again. * 2. Returns ARCHIVE_EOF if decompression has been completed. * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data * is broken or you do not set 'last' flag properly. */ #define ST_RD_TRANSLATION 0 #define ST_RD_TRANSLATION_SIZE 1 #define ST_RD_BLOCK_TYPE 2 #define ST_RD_BLOCK_SIZE 3 #define ST_RD_ALIGNMENT 4 #define ST_RD_R0 5 #define ST_RD_R1 6 #define ST_RD_R2 7 #define ST_COPY_UNCOMP1 8 #define ST_COPY_UNCOMP2 9 #define ST_RD_ALIGNED_OFFSET 10 #define ST_RD_VERBATIM 11 #define ST_RD_PRE_MAIN_TREE_256 12 #define ST_MAIN_TREE_256 13 #define ST_RD_PRE_MAIN_TREE_REM 14 #define ST_MAIN_TREE_REM 15 #define ST_RD_PRE_LENGTH_TREE 16 #define ST_LENGTH_TREE 17 #define ST_MAIN 18 #define ST_LENGTH 19 #define ST_OFFSET 20 #define ST_REAL_POS 21 #define ST_COPY 22 static int lzx_decode(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; int64_t avail_in; int r; if (ds->error) return (ds->error); avail_in = strm->avail_in; lzx_br_fixup(strm, &(ds->br)); do { if (ds->state < ST_MAIN) r = lzx_read_blocks(strm, last); else { int64_t bytes_written = strm->avail_out; r = lzx_decode_blocks(strm, last); bytes_written -= strm->avail_out; strm->next_out += bytes_written; strm->total_out += bytes_written; } } while (r == 100); strm->total_in += avail_in - strm->avail_in; return (r); } static int lzx_read_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int i, r; for (;;) { switch (ds->state) { case ST_RD_TRANSLATION: if (!lzx_br_read_ahead(strm, br, 1)) { ds->state = ST_RD_TRANSLATION; if (last) goto failed; return (ARCHIVE_OK); } ds->translation = lzx_br_bits(br, 1); lzx_br_consume(br, 1); /* FALL THROUGH */ case ST_RD_TRANSLATION_SIZE: if (ds->translation) { if (!lzx_br_read_ahead(strm, br, 32)) { ds->state = ST_RD_TRANSLATION_SIZE; if (last) goto failed; return (ARCHIVE_OK); } ds->translation_size = lzx_br_bits(br, 16); lzx_br_consume(br, 16); ds->translation_size <<= 16; ds->translation_size |= lzx_br_bits(br, 16); lzx_br_consume(br, 16); } /* FALL THROUGH */ case ST_RD_BLOCK_TYPE: if (!lzx_br_read_ahead(strm, br, 3)) { ds->state = ST_RD_BLOCK_TYPE; if (last) goto failed; return (ARCHIVE_OK); } ds->block_type = lzx_br_bits(br, 3); lzx_br_consume(br, 3); /* Check a block type. */ switch (ds->block_type) { case VERBATIM_BLOCK: case ALIGNED_OFFSET_BLOCK: case UNCOMPRESSED_BLOCK: break; default: goto failed;/* Invalid */ } /* FALL THROUGH */ case ST_RD_BLOCK_SIZE: if (!lzx_br_read_ahead(strm, br, 24)) { ds->state = ST_RD_BLOCK_SIZE; if (last) goto failed; return (ARCHIVE_OK); } ds->block_size = lzx_br_bits(br, 8); lzx_br_consume(br, 8); ds->block_size <<= 16; ds->block_size |= lzx_br_bits(br, 16); lzx_br_consume(br, 16); if (ds->block_size == 0) goto failed; ds->block_bytes_avail = ds->block_size; if (ds->block_type != UNCOMPRESSED_BLOCK) { if (ds->block_type == VERBATIM_BLOCK) ds->state = ST_RD_VERBATIM; else ds->state = ST_RD_ALIGNED_OFFSET; break; } /* FALL THROUGH */ case ST_RD_ALIGNMENT: /* * Handle an Uncompressed Block. */ /* Skip padding to align following field on * 16-bit boundary. */ if (lzx_br_is_unaligned(br)) lzx_br_consume_unaligned_bits(br); else { if (lzx_br_read_ahead(strm, br, 16)) lzx_br_consume(br, 16); else { ds->state = ST_RD_ALIGNMENT; if (last) goto failed; return (ARCHIVE_OK); } } /* Preparation to read repeated offsets R0,R1 and R2. */ ds->rbytes_avail = 0; ds->state = ST_RD_R0; /* FALL THROUGH */ case ST_RD_R0: case ST_RD_R1: case ST_RD_R2: do { uint16_t u16; /* Drain bits in the cache buffer of * bit-stream. */ if (lzx_br_has(br, 32)) { u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes, u16); u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes+2, u16); ds->rbytes_avail = 4; } else if (lzx_br_has(br, 16)) { u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes, u16); ds->rbytes_avail = 2; } if (ds->rbytes_avail < 4 && ds->br.have_odd) { ds->rbytes[ds->rbytes_avail++] = ds->br.odd; ds->br.have_odd = 0; } while (ds->rbytes_avail < 4) { if (strm->avail_in <= 0) { if (last) goto failed; return (ARCHIVE_OK); } ds->rbytes[ds->rbytes_avail++] = *strm->next_in++; strm->avail_in--; } ds->rbytes_avail = 0; if (ds->state == ST_RD_R0) { ds->r0 = archive_le32dec(ds->rbytes); if (ds->r0 < 0) goto failed; ds->state = ST_RD_R1; } else if (ds->state == ST_RD_R1) { ds->r1 = archive_le32dec(ds->rbytes); if (ds->r1 < 0) goto failed; ds->state = ST_RD_R2; } else if (ds->state == ST_RD_R2) { ds->r2 = archive_le32dec(ds->rbytes); if (ds->r2 < 0) goto failed; /* We've gotten all repeated offsets. */ ds->state = ST_COPY_UNCOMP1; } } while (ds->state != ST_COPY_UNCOMP1); /* FALL THROUGH */ case ST_COPY_UNCOMP1: /* * Copy bytes form next_in to next_out directly. */ while (ds->block_bytes_avail) { int l; if (strm->avail_out <= 0) /* Output buffer is empty. */ return (ARCHIVE_OK); if (strm->avail_in <= 0) { /* Input buffer is empty. */ if (last) goto failed; return (ARCHIVE_OK); } l = (int)ds->block_bytes_avail; if (l > ds->w_size - ds->w_pos) l = ds->w_size - ds->w_pos; if (l > strm->avail_out) l = (int)strm->avail_out; if (l > strm->avail_in) l = (int)strm->avail_in; memcpy(strm->next_out, strm->next_in, l); memcpy(&(ds->w_buff[ds->w_pos]), strm->next_in, l); strm->next_in += l; strm->avail_in -= l; strm->next_out += l; strm->avail_out -= l; strm->total_out += l; ds->w_pos = (ds->w_pos + l) & ds->w_mask; ds->block_bytes_avail -= l; } /* FALL THROUGH */ case ST_COPY_UNCOMP2: /* Re-align; skip padding byte. */ if (ds->block_size & 1) { if (strm->avail_in <= 0) { /* Input buffer is empty. */ ds->state = ST_COPY_UNCOMP2; if (last) goto failed; return (ARCHIVE_OK); } strm->next_in++; strm->avail_in --; } /* This block ended. */ ds->state = ST_RD_BLOCK_TYPE; return (ARCHIVE_EOF); /********************/ case ST_RD_ALIGNED_OFFSET: /* * Read Aligned offset tree. */ if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) { ds->state = ST_RD_ALIGNED_OFFSET; if (last) goto failed; return (ARCHIVE_OK); } memset(ds->at.freq, 0, sizeof(ds->at.freq)); for (i = 0; i < ds->at.len_size; i++) { ds->at.bitlen[i] = lzx_br_bits(br, 3); ds->at.freq[ds->at.bitlen[i]]++; lzx_br_consume(br, 3); } if (!lzx_make_huffman_table(&ds->at)) goto failed; /* FALL THROUGH */ case ST_RD_VERBATIM: ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_256: /* * Read Pre-tree for first 256 elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_MAIN_TREE_256: /* * Get path lengths of first 256 elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, 256); if (r < 0) goto failed; else if (!r) { ds->state = ST_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_REM: /* * Read Pre-tree for remaining elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 256; /* FALL THROUGH */ case ST_MAIN_TREE_REM: /* * Get path lengths of remaining elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, -1); if (r < 0) goto failed; else if (!r) { ds->state = ST_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->mt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_LENGTH_TREE: /* * Read Pre-tree for remaining elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_LENGTH_TREE: /* * Get path lengths of remaining elements of main tree. */ r = lzx_read_bitlen(strm, &ds->lt, -1); if (r < 0) goto failed; else if (!r) { ds->state = ST_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->lt)) goto failed; ds->state = ST_MAIN; return (100); } } failed: return (ds->error = ARCHIVE_FAILED); } static int lzx_decode_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br bre = ds->br; struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt); const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl; unsigned char *noutp = strm->next_out; unsigned char *endp = noutp + strm->avail_out; unsigned char *w_buff = ds->w_buff; unsigned char *at_bitlen = at->bitlen; unsigned char *lt_bitlen = lt->bitlen; unsigned char *mt_bitlen = mt->bitlen; size_t block_bytes_avail = ds->block_bytes_avail; int at_max_bits = at->max_bits; int lt_max_bits = lt->max_bits; int mt_max_bits = mt->max_bits; int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos; int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; int length_header = ds->length_header; int offset_bits = ds->offset_bits; int position_slot = ds->position_slot; int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2; int state = ds->state; char block_type = ds->block_type; for (;;) { switch (state) { case ST_MAIN: for (;;) { if (block_bytes_avail == 0) { /* This block ended. */ ds->state = ST_RD_BLOCK_TYPE; ds->br = bre; ds->block_bytes_avail = block_bytes_avail; ds->copy_len = copy_len; ds->copy_pos = copy_pos; ds->length_header = length_header; ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->w_pos = w_pos; strm->avail_out = endp - noutp; return (ARCHIVE_EOF); } if (noutp >= endp) /* Output buffer is empty. */ goto next_data; if (!lzx_br_read_ahead(strm, &bre, mt_max_bits)) { if (!last) goto next_data; /* Remaining bits are less than * maximum bits(mt.max_bits) but maybe * it still remains as much as we need, * so we should try to use it with * dummy bits. */ c = lzx_decode_huffman(mt, lzx_br_bits_forced( &bre, mt_max_bits)); lzx_br_consume(&bre, mt_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzx_decode_huffman(mt, lzx_br_bits(&bre, mt_max_bits)); lzx_br_consume(&bre, mt_bitlen[c]); } if (c > UCHAR_MAX) break; /* * 'c' is exactly literal code. */ /* Save a decoded code to reference it * afterward. */ w_buff[w_pos] = c; w_pos = (w_pos + 1) & w_mask; /* Store the decoded code to output buffer. */ *noutp++ = c; block_bytes_avail--; } /* * Get a match code, its length and offset. */ c -= UCHAR_MAX + 1; length_header = c & 7; position_slot = c >> 3; /* FALL THROUGH */ case ST_LENGTH: /* * Get a length. */ if (length_header == 7) { if (!lzx_br_read_ahead(strm, &bre, lt_max_bits)) { if (!last) { state = ST_LENGTH; goto next_data; } c = lzx_decode_huffman(lt, lzx_br_bits_forced( &bre, lt_max_bits)); lzx_br_consume(&bre, lt_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzx_decode_huffman(lt, lzx_br_bits(&bre, lt_max_bits)); lzx_br_consume(&bre, lt_bitlen[c]); } copy_len = c + 7 + 2; } else copy_len = length_header + 2; if ((size_t)copy_len > block_bytes_avail) goto failed; /* * Get an offset. */ switch (position_slot) { case 0: /* Use repeated offset 0. */ copy_pos = r0; state = ST_REAL_POS; continue; case 1: /* Use repeated offset 1. */ copy_pos = r1; /* Swap repeated offset. */ r1 = r0; r0 = copy_pos; state = ST_REAL_POS; continue; case 2: /* Use repeated offset 2. */ copy_pos = r2; /* Swap repeated offset. */ r2 = r0; r0 = copy_pos; state = ST_REAL_POS; continue; default: offset_bits = pos_tbl[position_slot].footer_bits; break; } /* FALL THROUGH */ case ST_OFFSET: /* * Get the offset, which is a distance from * current window position. */ if (block_type == ALIGNED_OFFSET_BLOCK && offset_bits >= 3) { int offbits = offset_bits - 3; if (!lzx_br_read_ahead(strm, &bre, offbits)) { state = ST_OFFSET; if (last) goto failed; goto next_data; } copy_pos = lzx_br_bits(&bre, offbits) << 3; /* Get an aligned number. */ if (!lzx_br_read_ahead(strm, &bre, offbits + at_max_bits)) { if (!last) { state = ST_OFFSET; goto next_data; } lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits_forced(&bre, at_max_bits)); lzx_br_consume(&bre, at_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits(&bre, at_max_bits)); lzx_br_consume(&bre, at_bitlen[c]); } /* Add an aligned number. */ copy_pos += c; } else { if (!lzx_br_read_ahead(strm, &bre, offset_bits)) { state = ST_OFFSET; if (last) goto failed; goto next_data; } copy_pos = lzx_br_bits(&bre, offset_bits); lzx_br_consume(&bre, offset_bits); } copy_pos += pos_tbl[position_slot].base -2; /* Update repeated offset LRU queue. */ r2 = r1; r1 = r0; r0 = copy_pos; /* FALL THROUGH */ case ST_REAL_POS: /* * Compute a real position in window. */ copy_pos = (w_pos - copy_pos) & w_mask; /* FALL THROUGH */ case ST_COPY: /* * Copy several bytes as extracted data from the window * into the output buffer. */ for (;;) { const unsigned char *s; int l; l = copy_len; if (copy_pos > w_pos) { if (l > w_size - copy_pos) l = w_size - copy_pos; } else { if (l > w_size - w_pos) l = w_size - w_pos; } if (noutp + l >= endp) l = (int)(endp - noutp); s = w_buff + copy_pos; if (l >= 8 && ((copy_pos + l < w_pos) || (w_pos + l < copy_pos))) { memcpy(w_buff + w_pos, s, l); memcpy(noutp, s, l); } else { unsigned char *d; int li; d = w_buff + w_pos; for (li = 0; li < l; li++) noutp[li] = d[li] = s[li]; } noutp += l; copy_pos = (copy_pos + l) & w_mask; w_pos = (w_pos + l) & w_mask; block_bytes_avail -= l; if (copy_len <= l) /* A copy of current pattern ended. */ break; copy_len -= l; if (noutp >= endp) { /* Output buffer is empty. */ state = ST_COPY; goto next_data; } } state = ST_MAIN; break; } } failed: return (ds->error = ARCHIVE_FAILED); next_data: ds->br = bre; ds->block_bytes_avail = block_bytes_avail; ds->copy_len = copy_len; ds->copy_pos = copy_pos; ds->length_header = length_header; ds->offset_bits = offset_bits; ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->state = state; ds->w_pos = w_pos; strm->avail_out = endp - noutp; return (ARCHIVE_OK); } static int lzx_read_pre_tree(struct lzx_stream *strm) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int i; if (ds->loop == 0) memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); for (i = ds->loop; i < ds->pt.len_size; i++) { if (!lzx_br_read_ahead(strm, br, 4)) { ds->loop = i; return (0); } ds->pt.bitlen[i] = lzx_br_bits(br, 4); ds->pt.freq[ds->pt.bitlen[i]]++; lzx_br_consume(br, 4); } ds->loop = i; return (1); } /* * Read a bunch of bit-lengths from pre-tree. */ static int lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int c, i, j, ret, same; unsigned rbits; i = ds->loop; if (i == 0) memset(d->freq, 0, sizeof(d->freq)); ret = 0; if (end < 0) end = d->len_size; while (i < end) { ds->loop = i; if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits)) goto getdata; rbits = lzx_br_bits(br, ds->pt.max_bits); c = lzx_decode_huffman(&(ds->pt), rbits); switch (c) { case 17:/* several zero lengths, from 4 to 19. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 4) + 4; if (i + same > end) return (-1);/* Invalid */ lzx_br_consume(br, 4); for (j = 0; j < same; j++) d->bitlen[i++] = 0; break; case 18:/* many zero lengths, from 20 to 51. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 5) + 20; if (i + same > end) return (-1);/* Invalid */ lzx_br_consume(br, 5); memset(d->bitlen + i, 0, same); i += same; break; case 19:/* a few same lengths. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+1+ds->pt.max_bits)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 1) + 4; if (i + same > end) return (-1); lzx_br_consume(br, 1); rbits = lzx_br_bits(br, ds->pt.max_bits); c = lzx_decode_huffman(&(ds->pt), rbits); lzx_br_consume(br, ds->pt.bitlen[c]); c = (d->bitlen[i] - c + 17) % 17; if (c < 0) return (-1);/* Invalid */ for (j = 0; j < same; j++) d->bitlen[i++] = c; d->freq[c] += same; break; default: lzx_br_consume(br, ds->pt.bitlen[c]); c = (d->bitlen[i] - c + 17) % 17; if (c < 0) return (-1);/* Invalid */ d->freq[c]++; d->bitlen[i++] = c; break; } } ret = 1; getdata: ds->loop = i; return (ret); } static int lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) { int bits; if (hf->bitlen == NULL || hf->len_size != (int)len_size) { free(hf->bitlen); hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); hf->len_size = (int)len_size; } else memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0])); if (hf->tbl == NULL) { if (tbl_bits < HTBL_BITS) bits = tbl_bits; else bits = HTBL_BITS; hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); hf->tbl_bits = tbl_bits; } if (hf->tree == NULL && tbl_bits > HTBL_BITS) { hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4); hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0])); if (hf->tree == NULL) return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static void lzx_huffman_free(struct huffman *hf) { free(hf->bitlen); free(hf->tbl); free(hf->tree); } /* * Make a huffman coding table. */ static int lzx_make_huffman_table(struct huffman *hf) { uint16_t *tbl; const unsigned char *bitlen; int bitptn[17], weight[17]; int i, maxbits = 0, ptn, tbl_size, w; int diffbits, len_avail; /* * Initialize bit patterns. */ ptn = 0; for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) { bitptn[i] = ptn; weight[i] = w; if (hf->freq[i]) { ptn += hf->freq[i] * w; maxbits = i; } } if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits) return (0);/* Invalid */ hf->max_bits = maxbits; /* * Cut out extra bits which we won't house in the table. * This preparation reduces the same calculation in the for-loop * making the table. */ if (maxbits < 16) { int ebits = 16 - maxbits; for (i = 1; i <= maxbits; i++) { bitptn[i] >>= ebits; weight[i] >>= ebits; } } if (maxbits > HTBL_BITS) { int htbl_max; uint16_t *p; diffbits = maxbits - HTBL_BITS; for (i = 1; i <= HTBL_BITS; i++) { bitptn[i] >>= diffbits; weight[i] >>= diffbits; } htbl_max = bitptn[HTBL_BITS] + weight[HTBL_BITS] * hf->freq[HTBL_BITS]; p = &(hf->tbl[htbl_max]); while (p < &hf->tbl[1U<shift_bits = diffbits; /* * Make the table. */ tbl_size = 1 << HTBL_BITS; tbl = hf->tbl; bitlen = hf->bitlen; len_avail = hf->len_size; hf->tree_used = 0; for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; uint16_t bit; int extlen; struct htree_t *ht; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; ptn = bitptn[len]; cnt = weight[len]; if (len <= HTBL_BITS) { /* Calculate next bit pattern */ if ((bitptn[len] = ptn + cnt) > tbl_size) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); while (--cnt >= 0) p[cnt] = (uint16_t)i; continue; } /* * A bit length is too big to be housed to a direct table, * so we use a tree model for its extra bits. */ bitptn[len] = ptn + cnt; bit = 1U << (diffbits -1); extlen = len - HTBL_BITS; p = &(tbl[ptn >> diffbits]); if (*p == 0) { *p = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { if (*p < len_avail || *p >= (len_avail + hf->tree_used)) return (0);/* Invalid */ ht = &(hf->tree[*p - len_avail]); } while (--extlen > 0) { if (ptn & bit) { if (ht->left < len_avail) { ht->left = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->left - len_avail]); } } else { if (ht->right < len_avail) { ht->right = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->right - len_avail]); } } bit >>= 1; } if (ptn & bit) { if (ht->left != 0) return (0);/* Invalid */ ht->left = (uint16_t)i; } else { if (ht->right != 0) return (0);/* Invalid */ ht->right = (uint16_t)i; } } return (1); } static int lzx_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c) { struct htree_t *ht; int extlen; ht = hf->tree; extlen = hf->shift_bits; while (c >= hf->len_size) { c -= hf->len_size; if (extlen-- <= 0 || c >= hf->tree_used) return (0); if (rbits & (1U << extlen)) c = ht[c].left; else c = ht[c].right; } return (c); } static inline int lzx_decode_huffman(struct huffman *hf, unsigned rbits) { int c; /* * At first search an index table for a bit pattern. * If it fails, search a huffman tree for. */ c = hf->tbl[rbits >> hf->shift_bits]; if (c < hf->len_size) return (c); /* This bit pattern needs to be found out at a huffman tree. */ return (lzx_decode_huffman_tree(hf, rbits, c)); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_lha.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_lha.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_lha.c (revision 316338) @@ -1,2814 +1,2814 @@ /*- * Copyright (c) 2008-2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" #define MAXMATCH 256 /* Maximum match length. */ #define MINMATCH 3 /* Minimum match length. */ /* * Literal table format: * +0 +256 +510 * +---------------+-------------------------+ * | literal code | match length | * | 0 ... 255 | MINMATCH ... MAXMATCH | * +---------------+-------------------------+ * <--- LT_BITLEN_SIZE ---> */ /* Literal table size. */ #define LT_BITLEN_SIZE (UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1) /* Position table size. * Note: this used for both position table and pre literal table.*/ #define PT_BITLEN_SIZE (3 + 16) struct lzh_dec { /* Decoding status. */ int state; /* * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded * data. */ int w_size; int w_mask; /* Window buffer, which is a loop buffer. */ unsigned char *w_buff; /* The insert position to the window. */ int w_pos; /* The position where we can copy decoded code from the window. */ int copy_pos; /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; /* * Bit stream reader. */ struct lzh_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; } br; /* * Huffman coding. */ struct huffman { int len_size; int len_avail; int len_bits; int freq[17]; unsigned char *bitlen; /* * Use a index table. It's faster than searching a huffman * coding tree, which is a binary tree. But a use of a large * index table causes L1 cache read miss many times. */ #define HTBL_BITS 10 int max_bits; int shift_bits; int tbl_bits; int tree_used; int tree_avail; /* Direct access table. */ uint16_t *tbl; /* Binary tree table for extra bits over the direct access. */ struct htree_t { uint16_t left; uint16_t right; } *tree; } lt, pt; int blocks_avail; int pos_pt_len_size; int pos_pt_len_bits; int literal_pt_len_size; int literal_pt_len_bits; int reading_position; int loop; int error; }; struct lzh_stream { const unsigned char *next_in; int avail_in; int64_t total_in; const unsigned char *ref_ptr; int avail_out; int64_t total_out; struct lzh_dec *ds; }; struct lha { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; uint16_t entry_crc_calculated; size_t header_size; /* header size */ unsigned char level; /* header level */ char method[3]; /* compress type */ int64_t compsize; /* compressed data size */ int64_t origsize; /* original file size */ int setflag; #define BIRTHTIME_IS_SET 1 #define ATIME_IS_SET 2 #define UNIX_MODE_IS_SET 4 #define CRC_IS_SET 8 time_t birthtime; long birthtime_tv_nsec; time_t mtime; long mtime_tv_nsec; time_t atime; long atime_tv_nsec; mode_t mode; int64_t uid; int64_t gid; struct archive_string uname; struct archive_string gname; uint16_t header_crc; uint16_t crc; struct archive_string_conv *sconv; struct archive_string_conv *opt_sconv; struct archive_string dirname; struct archive_string filename; struct archive_wstring ws; unsigned char dos_attr; /* Flag to mark progress that an archive was read their first header.*/ char found_first_header; /* Flag to mark that indicates an empty directory. */ char directory; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; char end_of_entry_cleanup; char entry_is_compressed; char format_name[64]; struct lzh_stream strm; }; /* * LHA header common member offset. */ #define H_METHOD_OFFSET 2 /* Compress type. */ #define H_ATTR_OFFSET 19 /* DOS attribute. */ #define H_LEVEL_OFFSET 20 /* Header Level. */ #define H_SIZE 22 /* Minimum header size. */ static int archive_read_format_lha_bid(struct archive_read *, int); static int archive_read_format_lha_options(struct archive_read *, const char *, const char *); static int archive_read_format_lha_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_lha_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_lha_read_data_skip(struct archive_read *); static int archive_read_format_lha_cleanup(struct archive_read *); static void lha_replace_path_separator(struct lha *, struct archive_entry *); static int lha_read_file_header_0(struct archive_read *, struct lha *); static int lha_read_file_header_1(struct archive_read *, struct lha *); static int lha_read_file_header_2(struct archive_read *, struct lha *); static int lha_read_file_header_3(struct archive_read *, struct lha *); static int lha_read_file_extended_header(struct archive_read *, struct lha *, uint16_t *, int, size_t, size_t *); static size_t lha_check_header_format(const void *); static int lha_skip_sfx(struct archive_read *); static time_t lha_dos_time(const unsigned char *); static time_t lha_win_time(uint64_t, long *); static unsigned char lha_calcsum(unsigned char, const void *, int, size_t); static int lha_parse_linkname(struct archive_string *, struct archive_string *); static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, size_t *, int64_t *); static void lha_crc16_init(void); static uint16_t lha_crc16(uint16_t, const void *, size_t); static int lzh_decode_init(struct lzh_stream *, const char *); static void lzh_decode_free(struct lzh_stream *); static int lzh_decode(struct lzh_stream *, int); static int lzh_br_fillup(struct lzh_stream *, struct lzh_br *); static int lzh_huffman_init(struct huffman *, size_t, int); static void lzh_huffman_free(struct huffman *); static int lzh_read_pt_bitlen(struct lzh_stream *, int start, int end); static int lzh_make_fake_table(struct huffman *, uint16_t); static int lzh_make_huffman_table(struct huffman *); static inline int lzh_decode_huffman(struct huffman *, unsigned); static int lzh_decode_huffman_tree(struct huffman *, unsigned, int); int archive_read_support_format_lha(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct lha *lha; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_lha"); lha = (struct lha *)calloc(1, sizeof(*lha)); if (lha == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate lha data"); return (ARCHIVE_FATAL); } archive_string_init(&lha->ws); r = __archive_read_register_format(a, lha, "lha", archive_read_format_lha_bid, archive_read_format_lha_options, archive_read_format_lha_read_header, archive_read_format_lha_read_data, archive_read_format_lha_read_data_skip, NULL, archive_read_format_lha_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(lha); return (ARCHIVE_OK); } static size_t lha_check_header_format(const void *h) { const unsigned char *p = h; size_t next_skip_bytes; switch (p[H_METHOD_OFFSET+3]) { /* * "-lh0-" ... "-lh7-" "-lhd-" * "-lzs-" "-lz5-" */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case 'd': case 's': next_skip_bytes = 4; /* b0 == 0 means the end of an LHa archive file. */ if (p[0] == 0) break; if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l' || p[H_METHOD_OFFSET+4] != '-') break; if (p[H_METHOD_OFFSET+2] == 'h') { /* "-lh?-" */ if (p[H_METHOD_OFFSET+3] == 's') break; if (p[H_LEVEL_OFFSET] == 0) return (0); if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20) return (0); } if (p[H_METHOD_OFFSET+2] == 'z') { /* LArc extensions: -lzs-,-lz4- and -lz5- */ if (p[H_LEVEL_OFFSET] != 0) break; if (p[H_METHOD_OFFSET+3] == 's' || p[H_METHOD_OFFSET+3] == '4' || p[H_METHOD_OFFSET+3] == '5') return (0); } break; case 'h': next_skip_bytes = 1; break; case 'z': next_skip_bytes = 1; break; case 'l': next_skip_bytes = 2; break; case '-': next_skip_bytes = 3; break; default : next_skip_bytes = 4; break; } return (next_skip_bytes); } static int archive_read_format_lha_bid(struct archive_read *a, int best_bid) { const char *p; const void *buff; ssize_t bytes_avail, offset, window; size_t next; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 30) return (-1); if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) return (-1); if (lha_check_header_format(p) == 0) return (30); if (p[0] == 'M' && p[1] == 'Z') { /* PE file */ offset = 0; window = 4096; while (offset < (1024 * 20)) { buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < (H_SIZE + 3)) return (0); continue; } p = (const char *)buff + offset; while (p + H_SIZE < (const char *)buff + bytes_avail) { if ((next = lha_check_header_format(p)) == 0) return (30); p += next; } offset = p - (const char *)buff; } } return (0); } static int archive_read_format_lha_options(struct archive_read *a, const char *key, const char *val) { struct lha *lha; int ret = ARCHIVE_FAILED; lha = (struct lha *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lha: hdrcharset option needs a character-set name"); else { lha->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (lha->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int lha_skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; size_t next, skip; ssize_t bytes, window; window = 4096; for (;;) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < (H_SIZE + 3)) goto fatal; continue; } if (bytes < H_SIZE) goto fatal; p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the lha header. */ while (p + H_SIZE < q) { if ((next = lha_check_header_format(p)) == 0) { skip = p - (const char *)h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += next; } skip = p - (const char *)h; __archive_read_consume(a, skip); } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out LHa header"); return (ARCHIVE_FATAL); } static int truncated_error(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa header"); return (ARCHIVE_FATAL); } static int archive_read_format_lha_read_header(struct archive_read *a, struct archive_entry *entry) { struct archive_string linkname; struct archive_string pathname; struct lha *lha; const unsigned char *p; const char *signature; int err; lha_crc16_init(); a->archive.archive_format = ARCHIVE_FORMAT_LHA; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "lha"; lha = (struct lha *)(a->format->data); lha->decompress_init = 0; lha->end_of_entry = 0; lha->end_of_entry_cleanup = 0; lha->entry_unconsumed = 0; if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) { /* * LHa archiver added 0 to the tail of its archive file as * the mark of the end of the archive. */ signature = __archive_read_ahead(a, sizeof(signature[0]), NULL); if (signature == NULL || signature[0] == 0) return (ARCHIVE_EOF); return (truncated_error(a)); } signature = (const char *)p; if (lha->found_first_header == 0 && signature[0] == 'M' && signature[1] == 'Z') { /* This is an executable? Must be self-extracting... */ err = lha_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL) return (truncated_error(a)); signature = (const char *)p; } /* signature[0] == 0 means the end of an LHa archive file. */ if (signature[0] == 0) return (ARCHIVE_EOF); /* * Check the header format and method type. */ if (lha_check_header_format(p) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad LHa file"); return (ARCHIVE_FATAL); } /* We've found the first header. */ lha->found_first_header = 1; /* Set a default value and common data */ lha->header_size = 0; lha->level = p[H_LEVEL_OFFSET]; lha->method[0] = p[H_METHOD_OFFSET+1]; lha->method[1] = p[H_METHOD_OFFSET+2]; lha->method[2] = p[H_METHOD_OFFSET+3]; if (memcmp(lha->method, "lhd", 3) == 0) lha->directory = 1; else lha->directory = 0; if (memcmp(lha->method, "lh0", 3) == 0 || memcmp(lha->method, "lz4", 3) == 0) lha->entry_is_compressed = 0; else lha->entry_is_compressed = 1; lha->compsize = 0; lha->origsize = 0; lha->setflag = 0; lha->birthtime = 0; lha->birthtime_tv_nsec = 0; lha->mtime = 0; lha->mtime_tv_nsec = 0; lha->atime = 0; lha->atime_tv_nsec = 0; lha->mode = (lha->directory)? 0777 : 0666; lha->uid = 0; lha->gid = 0; archive_string_empty(&lha->dirname); archive_string_empty(&lha->filename); lha->dos_attr = 0; if (lha->opt_sconv != NULL) lha->sconv = lha->opt_sconv; else lha->sconv = NULL; switch (p[H_LEVEL_OFFSET]) { case 0: err = lha_read_file_header_0(a, lha); break; case 1: err = lha_read_file_header_1(a, lha); break; case 2: err = lha_read_file_header_2(a, lha); break; case 3: err = lha_read_file_header_3(a, lha); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]); err = ARCHIVE_FATAL; break; } if (err < ARCHIVE_WARN) return (err); if (!lha->directory && archive_strlen(&lha->filename) == 0) /* The filename has not been set */ return (truncated_error(a)); /* * Make a pathname from a dirname and a filename. */ archive_string_concat(&lha->dirname, &lha->filename); archive_string_init(&pathname); archive_string_init(&linkname); archive_string_copy(&pathname, &lha->dirname); if ((lha->mode & AE_IFMT) == AE_IFLNK) { /* * Extract the symlink-name if it's included in the pathname. */ if (!lha_parse_linkname(&linkname, &pathname)) { /* We couldn't get the symlink-name. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown symlink-name"); archive_string_free(&pathname); archive_string_free(&linkname); return (ARCHIVE_FAILED); } } else { /* * Make sure a file-type is set. * The mode has been overridden if it is in the extended data. */ lha->mode = (lha->mode & ~AE_IFMT) | ((lha->directory)? AE_IFDIR: AE_IFREG); } if ((lha->setflag & UNIX_MODE_IS_SET) == 0 && (lha->dos_attr & 1) != 0) lha->mode &= ~(0222);/* read only. */ /* * Set basic file parameters. */ if (archive_entry_copy_pathname_l(entry, pathname.s, pathname.length, lha->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(lha->sconv)); err = ARCHIVE_WARN; } archive_string_free(&pathname); if (archive_strlen(&linkname) > 0) { if (archive_entry_copy_symlink_l(entry, linkname.s, linkname.length, lha->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(lha->sconv)); err = ARCHIVE_WARN; } } else archive_entry_set_symlink(entry, NULL); archive_string_free(&linkname); /* * When a header level is 0, there is a possibility that * a pathname and a symlink has '\' character, a directory * separator in DOS/Windows. So we should convert it to '/'. */ if (p[H_LEVEL_OFFSET] == 0) lha_replace_path_separator(lha, entry); archive_entry_set_mode(entry, lha->mode); archive_entry_set_uid(entry, lha->uid); archive_entry_set_gid(entry, lha->gid); if (archive_strlen(&lha->uname) > 0) archive_entry_set_uname(entry, lha->uname.s); if (archive_strlen(&lha->gname) > 0) archive_entry_set_gname(entry, lha->gname.s); if (lha->setflag & BIRTHTIME_IS_SET) { archive_entry_set_birthtime(entry, lha->birthtime, lha->birthtime_tv_nsec); archive_entry_set_ctime(entry, lha->birthtime, lha->birthtime_tv_nsec); } else { archive_entry_unset_birthtime(entry); archive_entry_unset_ctime(entry); } archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec); if (lha->setflag & ATIME_IS_SET) archive_entry_set_atime(entry, lha->atime, lha->atime_tv_nsec); else archive_entry_unset_atime(entry); if (lha->directory || archive_entry_symlink(entry) != NULL) archive_entry_unset_size(entry); else archive_entry_set_size(entry, lha->origsize); /* * Prepare variables used to read a file content. */ lha->entry_bytes_remaining = lha->compsize; lha->entry_offset = 0; lha->entry_crc_calculated = 0; /* * This file does not have a content. */ if (lha->directory || lha->compsize == 0) lha->end_of_entry = 1; sprintf(lha->format_name, "lha -%c%c%c-", lha->method[0], lha->method[1], lha->method[2]); a->archive.archive_format_name = lha->format_name; return (err); } /* * Replace a DOS path separator '\' by a character '/'. * Some multi-byte character set have a character '\' in its second byte. */ static void lha_replace_path_separator(struct lha *lha, struct archive_entry *entry) { const wchar_t *wp; size_t i; if ((wp = archive_entry_pathname_w(entry)) != NULL) { archive_wstrcpy(&(lha->ws), wp); for (i = 0; i < archive_strlen(&(lha->ws)); i++) { if (lha->ws.s[i] == L'\\') lha->ws.s[i] = L'/'; } archive_entry_copy_pathname_w(entry, lha->ws.s); } if ((wp = archive_entry_symlink_w(entry)) != NULL) { archive_wstrcpy(&(lha->ws), wp); for (i = 0; i < archive_strlen(&(lha->ws)); i++) { if (lha->ws.s[i] == L'\\') lha->ws.s[i] = L'/'; } archive_entry_copy_symlink_w(entry, lha->ws.s); } } /* * Header 0 format * * +0 +1 +2 +7 +11 * +---------------+----------+----------------+-------------------+ * |header size(*1)|header sum|compression type|compressed size(*2)| * +---------------+----------+----------------+-------------------+ * <---------------------(*1)----------* * * +11 +15 +17 +19 +20 +21 * +-----------------+---------+---------+--------------+----------------+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)| * +-----------------+---------+---------+--------------+----------------+ * *--------------------------------(*1)---------------------------------* * * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+2+(*4) * +---------------+---------+----------+----------------+------------------+ * |name length(*3)|file name|file CRC16|extra header(*4)| compressed data | * +---------------+---------+----------+----------------+------------------+ * <--(*3)-> <------(*2)------> * *----------------------(*1)--------------------------> * */ #define H0_HEADER_SIZE_OFFSET 0 #define H0_HEADER_SUM_OFFSET 1 #define H0_COMP_SIZE_OFFSET 7 #define H0_ORIG_SIZE_OFFSET 11 #define H0_DOS_TIME_OFFSET 15 #define H0_NAME_LEN_OFFSET 21 #define H0_FILE_NAME_OFFSET 22 #define H0_FIXED_SIZE 24 static int lha_read_file_header_0(struct archive_read *a, struct lha *lha) { const unsigned char *p; int extdsize, namelen; unsigned char headersum, sum_calculated; if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2; headersum = p[H0_HEADER_SUM_OFFSET]; lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET); lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET); namelen = p[H0_NAME_LEN_OFFSET]; extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen; if ((namelen > 221 || extdsize < 0) && extdsize != -2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen); /* When extdsize == -2, A CRC16 value is not present in the header. */ if (extdsize >= 0) { lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; } sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); /* Read an extended header */ if (extdsize > 0) { /* This extended data is set by 'LHa for UNIX' only. * Maybe fixed size. */ p += H0_FILE_NAME_OFFSET + namelen + 2; if (p[0] == 'U' && extdsize == 12) { /* p[1] is a minor version. */ lha->mtime = archive_le32dec(&p[2]); lha->mode = archive_le16dec(&p[6]); lha->uid = archive_le16dec(&p[8]); lha->gid = archive_le16dec(&p[10]); lha->setflag |= UNIX_MODE_IS_SET; } } __archive_read_consume(a, lha->header_size); if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } /* * Header 1 format * * +0 +1 +2 +7 +11 * +---------------+----------+----------------+-------------+ * |header size(*1)|header sum|compression type|skip size(*2)| * +---------------+----------+----------------+-------------+ * <---------------(*1)----------* * * +11 +15 +17 +19 +20 +21 * +-----------------+---------+---------+--------------+----------------+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)| * +-----------------+---------+---------+--------------+----------------+ * *-------------------------------(*1)----------------------------------* * * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+3 +22+(*3)+3+(*4) * +---------------+---------+----------+-----------+-----------+ * |name length(*3)|file name|file CRC16| creator |padding(*4)| * +---------------+---------+----------+-----------+-----------+ * <--(*3)-> * *----------------------------(*1)----------------------------* * * +22+(*3)+3+(*4) +22+(*3)+3+(*4)+2 +22+(*3)+3+(*4)+2+(*5) * +----------------+---------------------+------------------------+ * |next header size| extended header(*5) | compressed data | * +----------------+---------------------+------------------------+ * *------(*1)-----> <--------------------(*2)--------------------> */ #define H1_HEADER_SIZE_OFFSET 0 #define H1_HEADER_SUM_OFFSET 1 #define H1_COMP_SIZE_OFFSET 7 #define H1_ORIG_SIZE_OFFSET 11 #define H1_DOS_TIME_OFFSET 15 #define H1_NAME_LEN_OFFSET 21 #define H1_FILE_NAME_OFFSET 22 #define H1_FIXED_SIZE 27 static int lha_read_file_header_1(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int i, err, err2; int namelen, padding; unsigned char headersum, sum_calculated; err = ARCHIVE_OK; if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2; headersum = p[H1_HEADER_SUM_OFFSET]; /* Note: An extended header size is included in a compsize. */ lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET); lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET); namelen = p[H1_NAME_LEN_OFFSET]; /* Calculate a padding size. The result will be normally 0 only(?) */ padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen; if (namelen > 230 || padding < 0) goto invalid; if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); for (i = 0; i < namelen; i++) { if (p[i + H1_FILE_NAME_OFFSET] == 0xff) goto invalid;/* Invalid filename. */ } archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen); lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); /* Consume used bytes but not include `next header size' data * since it will be consumed in lha_read_file_extended_header(). */ __archive_read_consume(a, lha->header_size - 2); /* Read extended headers */ err2 = lha_read_file_extended_header(a, lha, NULL, 2, (size_t)(lha->compsize + 2), &extdsize); if (err2 < ARCHIVE_WARN) return (err2); if (err2 < err) err = err2; /* Get a real compressed file size. */ lha->compsize -= extdsize - 2; if (lha->compsize < 0) goto invalid; /* Invalid compressed file size */ if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); return (ARCHIVE_FATAL); } return (err); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } /* * Header 2 format * * +0 +2 +7 +11 +15 * +---------------+----------------+-------------------+-----------------+ * |header size(*1)|compression type|compressed size(*2)|uncompressed size| * +---------------+----------------+-------------------+-----------------+ * <--------------------------------(*1)---------------------------------* * * +15 +19 +20 +21 +23 +24 * +-----------------+------------+----------------+----------+-----------+ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16| creator | * +-----------------+------------+----------------+----------+-----------+ * *---------------------------------(*1)---------------------------------* * * +24 +26 +26+(*3) +26+(*3)+(*4) * +----------------+-------------------+-------------+-------------------+ * |next header size|extended header(*3)| padding(*4) | compressed data | * +----------------+-------------------+-------------+-------------------+ * *--------------------------(*1)-------------------> <------(*2)-------> * */ #define H2_HEADER_SIZE_OFFSET 0 #define H2_COMP_SIZE_OFFSET 7 #define H2_ORIG_SIZE_OFFSET 11 #define H2_TIME_OFFSET 15 #define H2_CRC_OFFSET 21 #define H2_FIXED_SIZE 24 static int lha_read_file_header_2(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int err, padding; uint16_t header_crc; if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET); lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET); lha->mtime = archive_le32dec(p + H2_TIME_OFFSET); lha->crc = archive_le16dec(p + H2_CRC_OFFSET); lha->setflag |= CRC_IS_SET; if (lha->header_size < H2_FIXED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header size"); return (ARCHIVE_FATAL); } header_crc = lha_crc16(0, p, H2_FIXED_SIZE); __archive_read_consume(a, H2_FIXED_SIZE); /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 2, lha->header_size - H2_FIXED_SIZE, &extdsize); if (err < ARCHIVE_WARN) return (err); /* Calculate a padding size. The result will be normally 0 or 1. */ padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize); if (padding > 0) { if ((p = __archive_read_ahead(a, padding, NULL)) == NULL) return (truncated_error(a)); header_crc = lha_crc16(header_crc, p, padding); __archive_read_consume(a, padding); } if (header_crc != lha->header_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header CRC error"); return (ARCHIVE_FATAL); } return (err); } /* * Header 3 format * * +0 +2 +7 +11 +15 * +------------+----------------+-------------------+-----------------+ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size| * +------------+----------------+-------------------+-----------------+ * <-------------------------------(*1)-------------------------------* * * +15 +19 +20 +21 +23 +24 * +-----------------+------------+----------------+----------+-----------+ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16| creator | * +-----------------+------------+----------------+----------+-----------+ * *--------------------------------(*1)----------------------------------* * * +24 +28 +32 +32+(*3) * +---------------+----------------+-------------------+-----------------+ * |header size(*1)|next header size|extended header(*3)| compressed data | * +---------------+----------------+-------------------+-----------------+ * *------------------------(*1)-----------------------> <------(*2)-----> * */ #define H3_FIELD_LEN_OFFSET 0 #define H3_COMP_SIZE_OFFSET 7 #define H3_ORIG_SIZE_OFFSET 11 #define H3_TIME_OFFSET 15 #define H3_CRC_OFFSET 21 #define H3_HEADER_SIZE_OFFSET 24 #define H3_FIXED_SIZE 28 static int lha_read_file_header_3(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int err; uint16_t header_crc; if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4) goto invalid; lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET); lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET); lha->mtime = archive_le32dec(p + H3_TIME_OFFSET); lha->crc = archive_le16dec(p + H3_CRC_OFFSET); lha->setflag |= CRC_IS_SET; if (lha->header_size < H3_FIXED_SIZE + 4) goto invalid; header_crc = lha_crc16(0, p, H3_FIXED_SIZE); __archive_read_consume(a, H3_FIXED_SIZE); /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 4, lha->header_size - H3_FIXED_SIZE, &extdsize); if (err < ARCHIVE_WARN) return (err); if (header_crc != lha->header_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header CRC error"); return (ARCHIVE_FATAL); } return (err); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } /* * Extended header format * * +0 +2 +3 -- used in header 1 and 2 * +0 +4 +5 -- used in header 3 * +--------------+---------+-------------------+--------------+-- * |ex-header size|header id| data |ex-header size| ....... * +--------------+---------+-------------------+--------------+-- * <-------------( ex-header size)------------> <-- next extended header --* * * If the ex-header size is zero, it is the make of the end of extended * headers. * */ static int lha_read_file_extended_header(struct archive_read *a, struct lha *lha, uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size) { const void *h; const unsigned char *extdheader; size_t extdsize; size_t datasize; unsigned int i; unsigned char extdtype; #define EXT_HEADER_CRC 0x00 /* Header CRC and information*/ #define EXT_FILENAME 0x01 /* Filename */ #define EXT_DIRECTORY 0x02 /* Directory name */ #define EXT_DOS_ATTR 0x40 /* MS-DOS attribute */ #define EXT_TIMESTAMP 0x41 /* Windows time stamp */ #define EXT_FILESIZE 0x42 /* Large file size */ #define EXT_TIMEZONE 0x43 /* Time zone */ #define EXT_UTF16_FILENAME 0x44 /* UTF-16 filename */ #define EXT_UTF16_DIRECTORY 0x45 /* UTF-16 directory name */ #define EXT_CODEPAGE 0x46 /* Codepage */ #define EXT_UNIX_MODE 0x50 /* File permission */ #define EXT_UNIX_GID_UID 0x51 /* gid,uid */ #define EXT_UNIX_GNAME 0x52 /* Group name */ #define EXT_UNIX_UNAME 0x53 /* User name */ #define EXT_UNIX_MTIME 0x54 /* Modified time */ #define EXT_OS2_NEW_ATTR 0x7f /* new attribute(OS/2 only) */ #define EXT_NEW_ATTR 0xff /* new attribute */ *total_size = sizefield_length; for (;;) { /* Read an extended header size. */ if ((h = __archive_read_ahead(a, sizefield_length, NULL)) == NULL) return (truncated_error(a)); /* Check if the size is the zero indicates the end of the * extended header. */ if (sizefield_length == sizeof(uint16_t)) extdsize = archive_le16dec(h); else extdsize = archive_le32dec(h); if (extdsize == 0) { /* End of extended header */ if (crc != NULL) *crc = lha_crc16(*crc, h, sizefield_length); __archive_read_consume(a, sizefield_length); return (ARCHIVE_OK); } /* Sanity check to the extended header size. */ if (((uint64_t)*total_size + extdsize) > (uint64_t)limitsize || extdsize <= (size_t)sizefield_length) goto invalid; /* Read the extended header. */ if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL) return (truncated_error(a)); *total_size += extdsize; extdheader = (const unsigned char *)h; /* Get the extended header type. */ extdtype = extdheader[sizefield_length]; /* Calculate an extended data size. */ datasize = extdsize - (1 + sizefield_length); /* Skip an extended header size field and type field. */ extdheader += sizefield_length + 1; if (crc != NULL && extdtype != EXT_HEADER_CRC) *crc = lha_crc16(*crc, h, extdsize); switch (extdtype) { case EXT_HEADER_CRC: /* We only use a header CRC. Following data will not * be used. */ if (datasize >= 2) { lha->header_crc = archive_le16dec(extdheader); if (crc != NULL) { static const char zeros[2] = {0, 0}; *crc = lha_crc16(*crc, h, extdsize - datasize); /* CRC value itself as zero */ *crc = lha_crc16(*crc, zeros, 2); *crc = lha_crc16(*crc, extdheader+2, datasize - 2); } } break; case EXT_FILENAME: if (datasize == 0) { /* maybe directory header */ archive_string_empty(&lha->filename); break; } if (extdheader[0] == '\0') goto invalid; archive_strncpy(&lha->filename, (const char *)extdheader, datasize); break; case EXT_DIRECTORY: if (datasize == 0 || extdheader[0] == '\0') /* no directory name data. exit this case. */ goto invalid; archive_strncpy(&lha->dirname, (const char *)extdheader, datasize); /* * Convert directory delimiter from 0xFF * to '/' for local system. */ for (i = 0; i < lha->dirname.length; i++) { if ((unsigned char)lha->dirname.s[i] == 0xFF) lha->dirname.s[i] = '/'; } /* Is last character directory separator? */ if (lha->dirname.s[lha->dirname.length-1] != '/') /* invalid directory data */ goto invalid; break; case EXT_DOS_ATTR: if (datasize == 2) lha->dos_attr = (unsigned char) (archive_le16dec(extdheader) & 0xff); break; case EXT_TIMESTAMP: if (datasize == (sizeof(uint64_t) * 3)) { lha->birthtime = lha_win_time( archive_le64dec(extdheader), &lha->birthtime_tv_nsec); extdheader += sizeof(uint64_t); lha->mtime = lha_win_time( archive_le64dec(extdheader), &lha->mtime_tv_nsec); extdheader += sizeof(uint64_t); lha->atime = lha_win_time( archive_le64dec(extdheader), &lha->atime_tv_nsec); lha->setflag |= BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_FILESIZE: if (datasize == sizeof(uint64_t) * 2) { lha->compsize = archive_le64dec(extdheader); extdheader += sizeof(uint64_t); lha->origsize = archive_le64dec(extdheader); } break; case EXT_CODEPAGE: /* Get an archived filename charset from codepage. * This overwrites the charset specified by * hdrcharset option. */ if (datasize == sizeof(uint32_t)) { struct archive_string cp; const char *charset; archive_string_init(&cp); switch (archive_le32dec(extdheader)) { case 65001: /* UTF-8 */ charset = "UTF-8"; break; default: archive_string_sprintf(&cp, "CP%d", (int)archive_le32dec(extdheader)); charset = cp.s; break; } lha->sconv = archive_string_conversion_from_charset( &(a->archive), charset, 1); archive_string_free(&cp); if (lha->sconv == NULL) return (ARCHIVE_FATAL); } break; case EXT_UNIX_MODE: if (datasize == sizeof(uint16_t)) { lha->mode = archive_le16dec(extdheader); lha->setflag |= UNIX_MODE_IS_SET; } break; case EXT_UNIX_GID_UID: if (datasize == (sizeof(uint16_t) * 2)) { lha->gid = archive_le16dec(extdheader); lha->uid = archive_le16dec(extdheader+2); } break; case EXT_UNIX_GNAME: if (datasize > 0) archive_strncpy(&lha->gname, (const char *)extdheader, datasize); break; case EXT_UNIX_UNAME: if (datasize > 0) archive_strncpy(&lha->uname, (const char *)extdheader, datasize); break; case EXT_UNIX_MTIME: if (datasize == sizeof(uint32_t)) lha->mtime = archive_le32dec(extdheader); break; case EXT_OS2_NEW_ATTR: /* This extended header is OS/2 depend. */ if (datasize == 16) { lha->dos_attr = (unsigned char) (archive_le16dec(extdheader) & 0xff); lha->mode = archive_le16dec(extdheader+2); lha->gid = archive_le16dec(extdheader+4); lha->uid = archive_le16dec(extdheader+6); lha->birthtime = archive_le32dec(extdheader+8); lha->atime = archive_le32dec(extdheader+12); lha->setflag |= UNIX_MODE_IS_SET | BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_NEW_ATTR: if (datasize == 20) { lha->mode = (mode_t)archive_le32dec(extdheader); lha->gid = archive_le32dec(extdheader+4); lha->uid = archive_le32dec(extdheader+8); lha->birthtime = archive_le32dec(extdheader+12); lha->atime = archive_le32dec(extdheader+16); lha->setflag |= UNIX_MODE_IS_SET | BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_TIMEZONE: /* Not supported */ case EXT_UTF16_FILENAME: /* Not supported */ case EXT_UTF16_DIRECTORY: /* Not supported */ default: break; } __archive_read_consume(a, extdsize); } invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended LHa header"); return (ARCHIVE_FATAL); } static int lha_end_of_entry(struct archive_read *a) { struct lha *lha = (struct lha *)(a->format->data); int r = ARCHIVE_EOF; if (!lha->end_of_entry_cleanup) { if ((lha->setflag & CRC_IS_SET) && lha->crc != lha->entry_crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa data CRC error"); r = ARCHIVE_WARN; } /* End-of-entry cleanup done. */ lha->end_of_entry_cleanup = 1; } return (r); } static int archive_read_format_lha_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); int r; if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); lha->entry_unconsumed = 0; } if (lha->end_of_entry) { *offset = lha->entry_offset; *size = 0; *buff = NULL; return (lha_end_of_entry(a)); } if (lha->entry_is_compressed) r = lha_read_data_lzh(a, buff, size, offset); else /* No compression. */ r = lha_read_data_none(a, buff, size, offset); return (r); } /* * Read a file content in no compression. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * lha->end_of_entry if it consumes all of the data. */ static int lha_read_data_none(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); ssize_t bytes_avail; if (lha->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = lha->entry_offset; lha->end_of_entry = 1; return (ARCHIVE_OK); } /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ *buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa file data"); return (ARCHIVE_FATAL); } if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail); *size = bytes_avail; *offset = lha->entry_offset; lha->entry_offset += bytes_avail; lha->entry_bytes_remaining -= bytes_avail; if (lha->entry_bytes_remaining == 0) lha->end_of_entry = 1; lha->entry_unconsumed = bytes_avail; return (ARCHIVE_OK); } /* * Read a file content in LZHUFF encoding. * * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes * all of the data. */ static int lha_read_data_lzh(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); ssize_t bytes_avail; int r; /* If we haven't yet read any data, initialize the decompressor. */ if (!lha->decompress_init) { r = lzh_decode_init(&(lha->strm), lha->method); switch (r) { case ARCHIVE_OK: break; case ARCHIVE_FAILED: /* Unsupported compression. */ *buff = NULL; *size = 0; *offset = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported lzh compression method -%c%c%c-", lha->method[0], lha->method[1], lha->method[2]); /* We know compressed size; just skip it. */ archive_read_format_lha_read_data_skip(a); return (ARCHIVE_WARN); default: archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory " "for lzh decompression"); return (ARCHIVE_FATAL); } /* We've initialized decompression for this stream. */ lha->decompress_init = 1; lha->strm.avail_out = 0; lha->strm.total_out = 0; } /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa file body"); return (ARCHIVE_FATAL); } if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; lha->strm.avail_in = (int)bytes_avail; lha->strm.total_in = 0; lha->strm.avail_out = 0; r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining); switch (r) { case ARCHIVE_OK: break; case ARCHIVE_EOF: lha->end_of_entry = 1; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad lzh data"); return (ARCHIVE_FAILED); } lha->entry_unconsumed = lha->strm.total_in; lha->entry_bytes_remaining -= lha->strm.total_in; if (lha->strm.avail_out) { *offset = lha->entry_offset; *size = lha->strm.avail_out; *buff = lha->strm.ref_ptr; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, *size); lha->entry_offset += *size; } else { *offset = lha->entry_offset; *size = 0; *buff = NULL; if (lha->end_of_entry) return (lha_end_of_entry(a)); } return (ARCHIVE_OK); } /* * Skip a file content. */ static int archive_read_format_lha_read_data_skip(struct archive_read *a) { struct lha *lha; int64_t bytes_skipped; lha = (struct lha *)(a->format->data); if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); lha->entry_unconsumed = 0; } /* if we've already read to end of data, we're done. */ if (lha->end_of_entry_cleanup) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* This entry is finished and done. */ lha->end_of_entry_cleanup = lha->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_lha_cleanup(struct archive_read *a) { struct lha *lha = (struct lha *)(a->format->data); lzh_decode_free(&(lha->strm)); archive_string_free(&(lha->dirname)); archive_string_free(&(lha->filename)); archive_string_free(&(lha->uname)); archive_string_free(&(lha->gname)); archive_wstring_free(&(lha->ws)); free(lha); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * 'LHa for UNIX' utility has archived a symbolic-link name after * a pathname with '|' character. * This function extracts the symbolic-link name from the pathname. * * example. * 1. a symbolic-name is 'aaa/bb/cc' * 2. a filename is 'xxx/bbb' * then a archived pathname is 'xxx/bbb|aaa/bb/cc' */ static int lha_parse_linkname(struct archive_string *linkname, struct archive_string *pathname) { char * linkptr; size_t symlen; linkptr = strchr(pathname->s, '|'); if (linkptr != NULL) { symlen = strlen(linkptr + 1); archive_strncpy(linkname, linkptr+1, symlen); *linkptr = 0; pathname->length = strlen(pathname->s); return (1); } return (0); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t lha_dos_time(const unsigned char *p) { int msTime, msDate; struct tm ts; msTime = archive_le16dec(p); msDate = archive_le16dec(p+2); memset(&ts, 0, sizeof(ts)); ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ ts.tm_mday = msDate & 0x1f; /* Day of month. */ ts.tm_hour = (msTime >> 11) & 0x1f; ts.tm_min = (msTime >> 5) & 0x3f; ts.tm_sec = (msTime << 1) & 0x3e; ts.tm_isdst = -1; return (mktime(&ts)); } /* Convert an MS-Windows-style date/time into Unix-style time. */ static time_t lha_win_time(uint64_t wintime, long *ns) { #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) if (wintime >= EPOC_TIME) { wintime -= EPOC_TIME; /* 1970-01-01 00:00:00 (UTC) */ if (ns != NULL) *ns = (long)(wintime % 10000000) * 100; return (wintime / 10000000); } else { if (ns != NULL) *ns = 0; return (0); } } static unsigned char lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) { unsigned char const *p = (unsigned char const *)pp; p += offset; for (;size > 0; --size) sum += *p++; return (sum); } static uint16_t crc16tbl[2][256]; static void lha_crc16_init(void) { unsigned int i; static int crc16init = 0; if (crc16init) return; crc16init = 1; for (i = 0; i < 256; i++) { unsigned int j; uint16_t crc = (uint16_t)i; for (j = 8; j; j--) crc = (crc >> 1) ^ ((crc & 1) * 0xA001); crc16tbl[0][i] = crc; } for (i = 0; i < 256; i++) { crc16tbl[1][i] = (crc16tbl[0][i] >> 8) ^ crc16tbl[0][crc16tbl[0][i] & 0xff]; } } static uint16_t lha_crc16(uint16_t crc, const void *pp, size_t len) { const unsigned char *p = (const unsigned char *)pp; const uint16_t *buff; const union { uint32_t i; char c[4]; } u = { 0x01020304 }; if (len == 0) return crc; /* Process unaligned address. */ if (((uintptr_t)p) & (uintptr_t)0x1) { crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; len--; } buff = (const uint16_t *)p; /* * Modern C compiler such as GCC does not unroll automatically yet * without unrolling pragma, and Clang is so. So we should * unroll this loop for its performance. */ for (;len >= 8; len -= 8) { /* This if statement expects compiler optimization will * remove the statement which will not be executed. */ #undef bswap16 #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ # define bswap16(x) _byteswap_ushort(x) #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4) /* GCC 4.8 and later has __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #elif defined(__clang__) /* All clang versions have __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #else # define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) #endif #define CRC16W do { \ if(u.c[0] == 1) { /* Big endian */ \ crc ^= bswap16(*buff); buff++; \ } else \ crc ^= *buff++; \ crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\ } while (0) CRC16W; CRC16W; CRC16W; CRC16W; #undef CRC16W #undef bswap16 } p = (const unsigned char *)buff; for (;len; len--) { crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; } return crc; } /* * Initialize LZHUF decoder. * * Returns ARCHIVE_OK if initialization was successful. * Returns ARCHIVE_FAILED if method is unsupported. * Returns ARCHIVE_FATAL if initialization failed; memory allocation * error occurred. */ static int lzh_decode_init(struct lzh_stream *strm, const char *method) { struct lzh_dec *ds; int w_bits, w_size; if (strm->ds == NULL) { strm->ds = calloc(1, sizeof(*strm->ds)); if (strm->ds == NULL) return (ARCHIVE_FATAL); } ds = strm->ds; ds->error = ARCHIVE_FAILED; if (method == NULL || method[0] != 'l' || method[1] != 'h') return (ARCHIVE_FAILED); switch (method[2]) { case '5': w_bits = 13;/* 8KiB for window */ break; case '6': w_bits = 15;/* 32KiB for window */ break; case '7': w_bits = 16;/* 64KiB for window */ break; default: return (ARCHIVE_FAILED);/* Not supported. */ } ds->error = ARCHIVE_FATAL; /* Expand a window size up to 128 KiB for decompressing process * performance whatever its original window size is. */ ds->w_size = 1U << 17; ds->w_mask = ds->w_size -1; if (ds->w_buff == NULL) { ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); } w_size = 1U << w_bits; memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size); ds->w_pos = 0; ds->state = 0; ds->pos_pt_len_size = w_bits + 1; ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4; ds->literal_pt_len_size = PT_BITLEN_SIZE; ds->literal_pt_len_bits = 5; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->lt.len_bits = 9; if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->error = 0; return (ARCHIVE_OK); } /* * Release LZHUF decoder. */ static void lzh_decode_free(struct lzh_stream *strm) { if (strm->ds == NULL) return; free(strm->ds->w_buff); lzh_huffman_free(&(strm->ds->lt)); lzh_huffman_free(&(strm->ds->pt)); free(strm->ds); strm->ds = NULL; } /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define lzh_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define lzh_br_bits(br, n) \ (((uint16_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define lzh_br_bits_forced(br, n) \ (((uint16_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : we met that strm->next_in is empty, we have to get following * bytes. */ #define lzh_br_read_ahead_0(strm, br, n) \ (lzh_br_has(br, (n)) || lzh_br_fillup(strm, br)) /* True : the cache buffer has some bits as much as we need. * False : there are no enough bits in the cache buffer to be used, * we have to get following bytes if we could. */ #define lzh_br_read_ahead(strm, br, n) \ (lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n))) /* Notify how many bits we consumed. */ #define lzh_br_consume(br, n) ((br)->cache_avail -= (n)) #define lzh_br_unconsume(br, n) ((br)->cache_avail += (n)) static const uint16_t cache_masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) { int n = CACHE_BITS - br->cache_avail; for (;;) { const int x = n >> 3; if (strm->avail_in >= x) { switch (x) { case 8: br->cache_buffer = ((uint64_t)strm->next_in[0]) << 56 | ((uint64_t)strm->next_in[1]) << 48 | ((uint64_t)strm->next_in[2]) << 40 | ((uint64_t)strm->next_in[3]) << 32 | ((uint32_t)strm->next_in[4]) << 24 | ((uint32_t)strm->next_in[5]) << 16 | ((uint32_t)strm->next_in[6]) << 8 | (uint32_t)strm->next_in[7]; strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); case 7: br->cache_buffer = (br->cache_buffer << 56) | ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | ((uint32_t)strm->next_in[3]) << 24 | ((uint32_t)strm->next_in[4]) << 16 | ((uint32_t)strm->next_in[5]) << 8 | (uint32_t)strm->next_in[6]; strm->next_in += 7; strm->avail_in -= 7; br->cache_avail += 7 * 8; return (1); case 6: br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[0]) << 40 | ((uint64_t)strm->next_in[1]) << 32 | ((uint32_t)strm->next_in[2]) << 24 | ((uint32_t)strm->next_in[3]) << 16 | ((uint32_t)strm->next_in[4]) << 8 | (uint32_t)strm->next_in[5]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } } if (strm->avail_in == 0) { /* There is not enough compressed data to fill up the * cache buffer. */ return (0); } br->cache_buffer = (br->cache_buffer << 8) | *strm->next_in++; strm->avail_in--; br->cache_avail += 8; n -= 8; } } /* * Decode LZHUF. * * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty. * Please set available buffer and call this function again. * 2. Returns ARCHIVE_EOF if decompression has been completed. * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data * is broken or you do not set 'last' flag properly. * 4. 'last' flag is very important, you must set 1 to the flag if there * is no input data. The lha compressed data format does not provide how * to know the compressed data is really finished. * Note: lha command utility check if the total size of output bytes is * reached the uncompressed size recorded in its header. it does not mind * that the decoding process is properly finished. * GNU ZIP can decompress another compressed file made by SCO LZH compress. * it handles EOF as null to fill read buffer with zero until the decoding * process meet 2 bytes of zeros at reading a size of a next chunk, so the * zeros are treated as the mark of the end of the data although the zeros * is dummy, not the file data. */ static int lzh_read_blocks(struct lzh_stream *, int); static int lzh_decode_blocks(struct lzh_stream *, int); #define ST_RD_BLOCK 0 #define ST_RD_PT_1 1 #define ST_RD_PT_2 2 #define ST_RD_PT_3 3 #define ST_RD_PT_4 4 #define ST_RD_LITERAL_1 5 #define ST_RD_LITERAL_2 6 #define ST_RD_LITERAL_3 7 #define ST_RD_POS_DATA_1 8 #define ST_GET_LITERAL 9 #define ST_GET_POS_1 10 #define ST_GET_POS_2 11 #define ST_COPY_DATA 12 static int lzh_decode(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; int avail_in; int r; if (ds->error) return (ds->error); avail_in = strm->avail_in; do { if (ds->state < ST_GET_LITERAL) r = lzh_read_blocks(strm, last); else r = lzh_decode_blocks(strm, last); } while (r == 100); strm->total_in += avail_in - strm->avail_in; return (r); } static void lzh_emit_window(struct lzh_stream *strm, size_t s) { strm->ref_ptr = strm->ds->w_buff; strm->avail_out = (int)s; strm->total_out += s; } static int lzh_read_blocks(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; struct lzh_br *br = &(ds->br); int c = 0, i; unsigned rbits; for (;;) { switch (ds->state) { case ST_RD_BLOCK: /* * Read a block number indicates how many blocks * we will handle. The block is composed of a * literal and a match, sometimes a literal only * in particular, there are no reference data at * the beginning of the decompression. */ if (!lzh_br_read_ahead_0(strm, br, 16)) { if (!last) /* We need following data. */ return (ARCHIVE_OK); if (lzh_br_has(br, 8)) { /* * It seems there are extra bits. * 1. Compressed data is broken. * 2. `last' flag does not properly * set. */ goto failed; } if (ds->w_pos > 0) { lzh_emit_window(strm, ds->w_pos); ds->w_pos = 0; return (ARCHIVE_OK); } /* End of compressed data; we have completely * handled all compressed data. */ return (ARCHIVE_EOF); } ds->blocks_avail = lzh_br_bits(br, 16); if (ds->blocks_avail == 0) goto failed; lzh_br_consume(br, 16); /* * Read a literal table compressed in huffman * coding. */ ds->pt.len_size = ds->literal_pt_len_size; ds->pt.len_bits = ds->literal_pt_len_bits; ds->reading_position = 0; /* FALL THROUGH */ case ST_RD_PT_1: /* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are * used in reading both a literal table and a * position table. */ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_PT_1; return (ARCHIVE_OK); } ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits); lzh_br_consume(br, ds->pt.len_bits); /* FALL THROUGH */ case ST_RD_PT_2: if (ds->pt.len_avail == 0) { /* There is no bitlen. */ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) { if (last) goto failed;/* Truncated data.*/ ds->state = ST_RD_PT_2; return (ARCHIVE_OK); } if (!lzh_make_fake_table(&(ds->pt), lzh_br_bits(br, ds->pt.len_bits))) goto failed;/* Invalid data. */ lzh_br_consume(br, ds->pt.len_bits); if (ds->reading_position) ds->state = ST_GET_LITERAL; else ds->state = ST_RD_LITERAL_1; break; } else if (ds->pt.len_avail > ds->pt.len_size) goto failed;/* Invalid data. */ ds->loop = 0; memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); if (ds->pt.len_avail < 3 || ds->pt.len_size == ds->pos_pt_len_size) { ds->state = ST_RD_PT_4; break; } /* FALL THROUGH */ case ST_RD_PT_3: ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3); if (ds->loop < 3) { if (ds->loop < 0 || last) goto failed;/* Invalid data. */ /* Not completed, get following data. */ ds->state = ST_RD_PT_3; return (ARCHIVE_OK); } /* There are some null in bitlen of the literal. */ if (!lzh_br_read_ahead(strm, br, 2)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_PT_3; return (ARCHIVE_OK); } c = lzh_br_bits(br, 2); lzh_br_consume(br, 2); if (c > ds->pt.len_avail - 3) goto failed;/* Invalid data. */ for (i = 3; c-- > 0 ;) ds->pt.bitlen[i++] = 0; ds->loop = i; /* FALL THROUGH */ case ST_RD_PT_4: ds->loop = lzh_read_pt_bitlen(strm, ds->loop, ds->pt.len_avail); if (ds->loop < ds->pt.len_avail) { if (ds->loop < 0 || last) goto failed;/* Invalid data. */ /* Not completed, get following data. */ ds->state = ST_RD_PT_4; return (ARCHIVE_OK); } if (!lzh_make_huffman_table(&(ds->pt))) goto failed;/* Invalid data */ if (ds->reading_position) { ds->state = ST_GET_LITERAL; break; } /* FALL THROUGH */ case ST_RD_LITERAL_1: if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_LITERAL_1; return (ARCHIVE_OK); } ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits); lzh_br_consume(br, ds->lt.len_bits); /* FALL THROUGH */ case ST_RD_LITERAL_2: if (ds->lt.len_avail == 0) { /* There is no bitlen. */ if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) { if (last) goto failed;/* Truncated data.*/ ds->state = ST_RD_LITERAL_2; return (ARCHIVE_OK); } if (!lzh_make_fake_table(&(ds->lt), lzh_br_bits(br, ds->lt.len_bits))) goto failed;/* Invalid data */ lzh_br_consume(br, ds->lt.len_bits); ds->state = ST_RD_POS_DATA_1; break; } else if (ds->lt.len_avail > ds->lt.len_size) goto failed;/* Invalid data */ ds->loop = 0; memset(ds->lt.freq, 0, sizeof(ds->lt.freq)); /* FALL THROUGH */ case ST_RD_LITERAL_3: i = ds->loop; while (i < ds->lt.len_avail) { if (!lzh_br_read_ahead(strm, br, ds->pt.max_bits)) { if (last) goto failed;/* Truncated data.*/ ds->loop = i; ds->state = ST_RD_LITERAL_3; return (ARCHIVE_OK); } rbits = lzh_br_bits(br, ds->pt.max_bits); c = lzh_decode_huffman(&(ds->pt), rbits); if (c > 2) { /* Note: 'c' will never be more than * eighteen since it's limited by * PT_BITLEN_SIZE, which is being set * to ds->pt.len_size through * ds->literal_pt_len_size. */ lzh_br_consume(br, ds->pt.bitlen[c]); c -= 2; ds->lt.freq[c]++; ds->lt.bitlen[i++] = c; } else if (c == 0) { lzh_br_consume(br, ds->pt.bitlen[c]); ds->lt.bitlen[i++] = 0; } else { /* c == 1 or c == 2 */ int n = (c == 1)?4:9; if (!lzh_br_read_ahead(strm, br, ds->pt.bitlen[c] + n)) { if (last) /* Truncated data. */ goto failed; ds->loop = i; ds->state = ST_RD_LITERAL_3; return (ARCHIVE_OK); } lzh_br_consume(br, ds->pt.bitlen[c]); c = lzh_br_bits(br, n); lzh_br_consume(br, n); c += (n == 4)?3:20; if (i + c > ds->lt.len_avail) goto failed;/* Invalid data */ memset(&(ds->lt.bitlen[i]), 0, c); i += c; } } if (i > ds->lt.len_avail || !lzh_make_huffman_table(&(ds->lt))) goto failed;/* Invalid data */ /* FALL THROUGH */ case ST_RD_POS_DATA_1: /* * Read a position table compressed in huffman * coding. */ ds->pt.len_size = ds->pos_pt_len_size; ds->pt.len_bits = ds->pos_pt_len_bits; ds->reading_position = 1; ds->state = ST_RD_PT_1; break; case ST_GET_LITERAL: return (100); } } failed: return (ds->error = ARCHIVE_FAILED); } static int lzh_decode_blocks(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; struct lzh_br bre = ds->br; struct huffman *lt = &(ds->lt); struct huffman *pt = &(ds->pt); unsigned char *w_buff = ds->w_buff; unsigned char *lt_bitlen = lt->bitlen; unsigned char *pt_bitlen = pt->bitlen; int blocks_avail = ds->blocks_avail, c = 0; int copy_len = ds->copy_len, copy_pos = ds->copy_pos; int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits; int state = ds->state; for (;;) { switch (state) { case ST_GET_LITERAL: for (;;) { if (blocks_avail == 0) { /* We have decoded all blocks. * Let's handle next blocks. */ ds->state = ST_RD_BLOCK; ds->br = bre; ds->blocks_avail = 0; ds->w_pos = w_pos; ds->copy_pos = 0; return (100); } /* lzh_br_read_ahead() always try to fill the * cache buffer up. In specific situation we * are close to the end of the data, the cache * buffer will not be full and thus we have to * determine if the cache buffer has some bits * as much as we need after lzh_br_read_ahead() * failed. */ if (!lzh_br_read_ahead(strm, &bre, lt_max_bits)) { if (!last) goto next_data; /* Remaining bits are less than * maximum bits(lt.max_bits) but maybe * it still remains as much as we need, * so we should try to use it with * dummy bits. */ c = lzh_decode_huffman(lt, lzh_br_bits_forced(&bre, lt_max_bits)); lzh_br_consume(&bre, lt_bitlen[c]); if (!lzh_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzh_decode_huffman(lt, lzh_br_bits(&bre, lt_max_bits)); lzh_br_consume(&bre, lt_bitlen[c]); } blocks_avail--; if (c > UCHAR_MAX) /* Current block is a match data. */ break; /* * 'c' is exactly a literal code. */ /* Save a decoded code to reference it * afterward. */ w_buff[w_pos] = c; if (++w_pos >= w_size) { w_pos = 0; lzh_emit_window(strm, w_size); goto next_data; } } /* 'c' is the length of a match pattern we have * already extracted, which has be stored in * window(ds->w_buff). */ copy_len = c - (UCHAR_MAX + 1) + MINMATCH; /* FALL THROUGH */ case ST_GET_POS_1: /* * Get a reference position. */ if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) { if (!last) { state = ST_GET_POS_1; ds->copy_len = copy_len; goto next_data; } copy_pos = lzh_decode_huffman(pt, lzh_br_bits_forced(&bre, pt_max_bits)); lzh_br_consume(&bre, pt_bitlen[copy_pos]); if (!lzh_br_has(&bre, 0)) goto failed;/* Over read. */ } else { copy_pos = lzh_decode_huffman(pt, lzh_br_bits(&bre, pt_max_bits)); lzh_br_consume(&bre, pt_bitlen[copy_pos]); } /* FALL THROUGH */ case ST_GET_POS_2: if (copy_pos > 1) { /* We need an additional adjustment number to * the position. */ int p = copy_pos - 1; if (!lzh_br_read_ahead(strm, &bre, p)) { if (last) goto failed;/* Truncated data.*/ state = ST_GET_POS_2; ds->copy_len = copy_len; ds->copy_pos = copy_pos; goto next_data; } copy_pos = (1 << p) + lzh_br_bits(&bre, p); lzh_br_consume(&bre, p); } /* The position is actually a distance from the last * code we had extracted and thus we have to convert * it to a position of the window. */ copy_pos = (w_pos - copy_pos - 1) & w_mask; /* FALL THROUGH */ case ST_COPY_DATA: /* * Copy `copy_len' bytes as extracted data from * the window into the output buffer. */ for (;;) { int l; l = copy_len; if (copy_pos > w_pos) { if (l > w_size - copy_pos) l = w_size - copy_pos; } else { if (l > w_size - w_pos) l = w_size - w_pos; } if ((copy_pos + l < w_pos) || (w_pos + l < copy_pos)) { /* No overlap. */ memcpy(w_buff + w_pos, w_buff + copy_pos, l); } else { const unsigned char *s; unsigned char *d; int li; d = w_buff + w_pos; s = w_buff + copy_pos; for (li = 0; li < l-1;) { d[li] = s[li];li++; d[li] = s[li];li++; } if (li < l) d[li] = s[li]; } w_pos += l; if (w_pos == w_size) { w_pos = 0; lzh_emit_window(strm, w_size); if (copy_len <= l) state = ST_GET_LITERAL; else { state = ST_COPY_DATA; ds->copy_len = copy_len - l; ds->copy_pos = (copy_pos + l) & w_mask; } goto next_data; } if (copy_len <= l) /* A copy of current pattern ended. */ break; copy_len -= l; copy_pos = (copy_pos + l) & w_mask; } state = ST_GET_LITERAL; break; } } failed: return (ds->error = ARCHIVE_FAILED); next_data: ds->br = bre; ds->blocks_avail = blocks_avail; ds->state = state; ds->w_pos = w_pos; return (ARCHIVE_OK); } static int lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) { int bits; if (hf->bitlen == NULL) { hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); } if (hf->tbl == NULL) { if (tbl_bits < HTBL_BITS) bits = tbl_bits; else bits = HTBL_BITS; hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); } if (hf->tree == NULL && tbl_bits > HTBL_BITS) { hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4); hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0])); if (hf->tree == NULL) return (ARCHIVE_FATAL); } hf->len_size = (int)len_size; hf->tbl_bits = tbl_bits; return (ARCHIVE_OK); } static void lzh_huffman_free(struct huffman *hf) { free(hf->bitlen); free(hf->tbl); free(hf->tree); } -static char bitlen_tbl[0x400] = { +static const char bitlen_tbl[0x400] = { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 0 }; static int lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end) { struct lzh_dec *ds = strm->ds; struct lzh_br *br = &(ds->br); int c, i; for (i = start; i < end; ) { /* * bit pattern the number we need * 000 -> 0 * 001 -> 1 * 010 -> 2 * ... * 110 -> 6 * 1110 -> 7 * 11110 -> 8 * ... * 1111111111110 -> 16 */ if (!lzh_br_read_ahead(strm, br, 3)) return (i); if ((c = lzh_br_bits(br, 3)) == 7) { if (!lzh_br_read_ahead(strm, br, 13)) return (i); c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF]; if (c) lzh_br_consume(br, c - 3); else return (-1);/* Invalid data. */ } else lzh_br_consume(br, 3); ds->pt.bitlen[i++] = c; ds->pt.freq[c]++; } return (i); } static int lzh_make_fake_table(struct huffman *hf, uint16_t c) { if (c >= hf->len_size) return (0); hf->tbl[0] = c; hf->max_bits = 0; hf->shift_bits = 0; hf->bitlen[hf->tbl[0]] = 0; return (1); } /* * Make a huffman coding table. */ static int lzh_make_huffman_table(struct huffman *hf) { uint16_t *tbl; const unsigned char *bitlen; int bitptn[17], weight[17]; int i, maxbits = 0, ptn, tbl_size, w; int diffbits, len_avail; /* * Initialize bit patterns. */ ptn = 0; for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) { bitptn[i] = ptn; weight[i] = w; if (hf->freq[i]) { ptn += hf->freq[i] * w; maxbits = i; } } if (ptn != 0x10000 || maxbits > hf->tbl_bits) return (0);/* Invalid */ hf->max_bits = maxbits; /* * Cut out extra bits which we won't house in the table. * This preparation reduces the same calculation in the for-loop * making the table. */ if (maxbits < 16) { int ebits = 16 - maxbits; for (i = 1; i <= maxbits; i++) { bitptn[i] >>= ebits; weight[i] >>= ebits; } } if (maxbits > HTBL_BITS) { unsigned htbl_max; uint16_t *p; diffbits = maxbits - HTBL_BITS; for (i = 1; i <= HTBL_BITS; i++) { bitptn[i] >>= diffbits; weight[i] >>= diffbits; } htbl_max = bitptn[HTBL_BITS] + weight[HTBL_BITS] * hf->freq[HTBL_BITS]; p = &(hf->tbl[htbl_max]); while (p < &hf->tbl[1U<shift_bits = diffbits; /* * Make the table. */ tbl_size = 1 << HTBL_BITS; tbl = hf->tbl; bitlen = hf->bitlen; len_avail = hf->len_avail; hf->tree_used = 0; for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; uint16_t bit; int extlen; struct htree_t *ht; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; ptn = bitptn[len]; cnt = weight[len]; if (len <= HTBL_BITS) { /* Calculate next bit pattern */ if ((bitptn[len] = ptn + cnt) > tbl_size) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); if (cnt > 7) { uint16_t *pc; cnt -= 8; pc = &p[cnt]; pc[0] = (uint16_t)i; pc[1] = (uint16_t)i; pc[2] = (uint16_t)i; pc[3] = (uint16_t)i; pc[4] = (uint16_t)i; pc[5] = (uint16_t)i; pc[6] = (uint16_t)i; pc[7] = (uint16_t)i; if (cnt > 7) { cnt -= 8; memcpy(&p[cnt], pc, 8 * sizeof(uint16_t)); pc = &p[cnt]; while (cnt > 15) { cnt -= 16; memcpy(&p[cnt], pc, 16 * sizeof(uint16_t)); } } if (cnt) memcpy(p, pc, cnt * sizeof(uint16_t)); } else { while (cnt > 1) { p[--cnt] = (uint16_t)i; p[--cnt] = (uint16_t)i; } if (cnt) p[--cnt] = (uint16_t)i; } continue; } /* * A bit length is too big to be housed to a direct table, * so we use a tree model for its extra bits. */ bitptn[len] = ptn + cnt; bit = 1U << (diffbits -1); extlen = len - HTBL_BITS; p = &(tbl[ptn >> diffbits]); if (*p == 0) { *p = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { if (*p < len_avail || *p >= (len_avail + hf->tree_used)) return (0);/* Invalid */ ht = &(hf->tree[*p - len_avail]); } while (--extlen > 0) { if (ptn & bit) { if (ht->left < len_avail) { ht->left = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->left - len_avail]); } } else { if (ht->right < len_avail) { ht->right = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->right - len_avail]); } } bit >>= 1; } if (ptn & bit) { if (ht->left != 0) return (0);/* Invalid */ ht->left = (uint16_t)i; } else { if (ht->right != 0) return (0);/* Invalid */ ht->right = (uint16_t)i; } } return (1); } static int lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c) { struct htree_t *ht; int extlen; ht = hf->tree; extlen = hf->shift_bits; while (c >= hf->len_avail) { c -= hf->len_avail; if (extlen-- <= 0 || c >= hf->tree_used) return (0); if (rbits & (1U << extlen)) c = ht[c].left; else c = ht[c].right; } return (c); } static inline int lzh_decode_huffman(struct huffman *hf, unsigned rbits) { int c; /* * At first search an index table for a bit pattern. * If it fails, search a huffman tree for. */ c = hf->tbl[rbits >> hf->shift_bits]; if (c < hf->len_avail || hf->len_avail == 0) return (c); /* This bit pattern needs to be found out at a huffman tree. */ return (lzh_decode_huffman_tree(hf, rbits, c)); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_mtree.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_mtree.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_mtree.c (revision 316338) @@ -1,2040 +1,2045 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #include /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" #include "archive_pack_dev.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #define MTREE_HAS_DEVICE 0x0001 #define MTREE_HAS_FFLAGS 0x0002 #define MTREE_HAS_GID 0x0004 #define MTREE_HAS_GNAME 0x0008 #define MTREE_HAS_MTIME 0x0010 #define MTREE_HAS_NLINK 0x0020 #define MTREE_HAS_PERM 0x0040 #define MTREE_HAS_SIZE 0x0080 #define MTREE_HAS_TYPE 0x0100 #define MTREE_HAS_UID 0x0200 #define MTREE_HAS_UNAME 0x0400 #define MTREE_HAS_OPTIONAL 0x0800 #define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ #define MTREE_HASHTABLE_SIZE 1024 struct mtree_option { struct mtree_option *next; char *value; }; struct mtree_entry { struct mtree_entry *next; struct mtree_option *options; char *name; char full; char used; unsigned int name_hash; struct mtree_entry *hashtable_next; }; struct mtree { struct archive_string line; size_t buffsize; char *buff; int64_t offset; int fd; int archive_format; const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; struct mtree_entry *entry_hashtable[MTREE_HASHTABLE_SIZE]; struct archive_string current_dir; struct archive_string contents_name; struct archive_entry_linkresolver *resolver; int64_t cur_size; char checkfs; }; static int bid_keycmp(const char *, const char *, ssize_t); static int cleanup(struct archive_read *); static int detect_form(struct archive_read *, int *); static unsigned int hash(const char *); static int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static void parse_escapes(char *, struct mtree_entry *); static int parse_line(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static int parse_keyword(struct archive_read *, struct mtree *, struct archive_entry *, struct mtree_option *, int *); static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); static int skip(struct archive_read *a); static int read_header(struct archive_read *, struct archive_entry *); static int64_t mtree_atol10(char **); static int64_t mtree_atol8(char **); static int64_t mtree_atol(char **); /* * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them * here. TODO: Move this to configure time, but be careful * about cross-compile environments. */ static int64_t get_time_t_max(void) { #if defined(TIME_T_MAX) return TIME_T_MAX; #else /* ISO C allows time_t to be a floating-point type, but POSIX requires an integer type. The following should work on any system that follows the POSIX conventions. */ if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (~(time_t)0); } else { /* Time_t is signed. */ /* Assume it's the same as int64_t or int32_t */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MAX; } else { return (time_t)INT32_MAX; } } #endif } static int64_t get_time_t_min(void) { #if defined(TIME_T_MIN) return TIME_T_MIN; #else if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (time_t)0; } else { /* Time_t is signed. */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MIN; } else { return (time_t)INT32_MIN; } } #endif } static int archive_read_format_mtree_options(struct archive_read *a, const char *key, const char *val) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (strcmp(key, "checkfs") == 0) { /* Allows to read information missing from the mtree from the file system */ if (val == NULL || val[0] == 0) { mtree->checkfs = 0; } else { mtree->checkfs = 1; } return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static void free_options(struct mtree_option *head) { struct mtree_option *next; for (; head != NULL; head = next) { next = head->next; free(head->value); free(head); } } int archive_read_support_format_mtree(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct mtree *mtree; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_mtree"); mtree = (struct mtree *)calloc(1, sizeof(*mtree)); if (mtree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } mtree->fd = -1; r = __archive_read_register_format(a, mtree, "mtree", mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(mtree); return (ARCHIVE_OK); } static int cleanup(struct archive_read *a) { struct mtree *mtree; struct mtree_entry *p, *q; mtree = (struct mtree *)(a->format->data); p = mtree->entries; while (p != NULL) { q = p->next; free(p->name); free_options(p->options); free(p); p = q; } archive_string_free(&mtree->line); archive_string_free(&mtree->current_dir); archive_string_free(&mtree->contents_name); archive_entry_linkresolver_free(mtree->resolver); free(mtree->buff); free(mtree); (a->format->data) = NULL; return (ARCHIVE_OK); } static ssize_t get_line_size(const char *b, ssize_t avail, ssize_t *nlsize) { ssize_t len; len = 0; while (len < avail) { switch (*b) { case '\0':/* Non-ascii character or control character. */ if (nlsize != NULL) *nlsize = 0; return (-1); case '\r': if (avail-len > 1 && b[1] == '\n') { if (nlsize != NULL) *nlsize = 2; return (len+2); } /* FALL THROUGH */ case '\n': if (nlsize != NULL) *nlsize = 1; return (len+1); default: b++; len++; break; } } if (nlsize != NULL) *nlsize = 0; return (avail); } /* * <---------------- ravail ---------------------> * <-- diff ------> <--- avail -----------------> * <---- len -----------> * | Previous lines | line being parsed nl extra | * ^ * b * */ static ssize_t next_line(struct archive_read *a, const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl) { ssize_t len; int quit; quit = 0; if (*avail == 0) { *nl = 0; len = 0; } else len = get_line_size(*b, *avail, nl); /* * Read bytes more while it does not reach the end of line. */ while (*nl == 0 && len == *avail && !quit) { ssize_t diff = *ravail - *avail; size_t nbytes_req = (*ravail+1023) & ~1023U; ssize_t tested; /* Increase reading bytes if it is not enough to at least * new two lines. */ if (nbytes_req < (size_t)*ravail + 160) nbytes_req <<= 1; *b = __archive_read_ahead(a, nbytes_req, avail); if (*b == NULL) { if (*ravail >= *avail) return (0); /* Reading bytes reaches the end of file. */ *b = __archive_read_ahead(a, *avail, avail); quit = 1; } *ravail = *avail; *b += diff; *avail -= diff; tested = len;/* Skip some bytes we already determinated. */ len = get_line_size(*b + len, *avail - len, nl); if (len >= 0) len += tested; } return (len); } /* * Compare characters with a mtree keyword. * Returns the length of a mtree keyword if matched. * Returns 0 if not matched. */ static int bid_keycmp(const char *p, const char *key, ssize_t len) { int match_len = 0; while (len > 0 && *p && *key) { if (*p == *key) { --len; ++p; ++key; ++match_len; continue; } return (0);/* Not match */ } if (*key != '\0') return (0);/* Not match */ /* A following character should be specified characters */ if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' || p[0] == '\n' || p[0] == '\r' || (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))) return (match_len); return (0);/* Not match */ } /* * Test whether the characters 'p' has is mtree keyword. * Returns the length of a detected keyword. * Returns 0 if any keywords were not found. */ static int bid_keyword(const char *p, ssize_t len) { - static const char *keys_c[] = { + static const char * const keys_c[] = { "content", "contents", "cksum", NULL }; - static const char *keys_df[] = { + static const char * const keys_df[] = { "device", "flags", NULL }; - static const char *keys_g[] = { + static const char * const keys_g[] = { "gid", "gname", NULL }; - static const char *keys_il[] = { + static const char * const keys_il[] = { "ignore", "inode", "link", NULL }; - static const char *keys_m[] = { + static const char * const keys_m[] = { "md5", "md5digest", "mode", NULL }; - static const char *keys_no[] = { + static const char * const keys_no[] = { "nlink", "nochange", "optional", NULL }; - static const char *keys_r[] = { + static const char * const keys_r[] = { "resdevice", "rmd160", "rmd160digest", NULL }; - static const char *keys_s[] = { + static const char * const keys_s[] = { "sha1", "sha1digest", "sha256", "sha256digest", "sha384", "sha384digest", "sha512", "sha512digest", "size", NULL }; - static const char *keys_t[] = { + static const char * const keys_t[] = { "tags", "time", "type", NULL }; - static const char *keys_u[] = { + static const char * const keys_u[] = { "uid", "uname", NULL }; - const char **keys; + const char * const *keys; int i; switch (*p) { case 'c': keys = keys_c; break; case 'd': case 'f': keys = keys_df; break; case 'g': keys = keys_g; break; case 'i': case 'l': keys = keys_il; break; case 'm': keys = keys_m; break; case 'n': case 'o': keys = keys_no; break; case 'r': keys = keys_r; break; case 's': keys = keys_s; break; case 't': keys = keys_t; break; case 'u': keys = keys_u; break; default: return (0);/* Unknown key */ } for (i = 0; keys[i] != NULL; i++) { int l = bid_keycmp(p, keys[i], len); if (l > 0) return (l); } return (0);/* Unknown key */ } /* * Test whether there is a set of mtree keywords. * Returns the number of keyword. * Returns -1 if we got incorrect sequence. * This function expects a set of "keyword=value". * When "unset" is specified, expects a set of "keyword". */ static int bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path) { int l; int keycnt = 0; while (len > 0 && *p) { int blank = 0; /* Test whether there are blank characters in the line. */ while (len >0 && (*p == ' ' || *p == '\t')) { ++p; --len; blank = 1; } if (*p == '\n' || *p == '\r') break; if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')) break; if (!blank && !last_is_path) /* No blank character. */ return (-1); if (last_is_path && len == 0) return (keycnt); if (unset) { l = bid_keycmp(p, "all", len); if (l > 0) return (1); } /* Test whether there is a correct key in the line. */ l = bid_keyword(p, len); if (l == 0) return (-1);/* Unknown keyword was found. */ p += l; len -= l; keycnt++; /* Skip value */ if (*p == '=') { int value = 0; ++p; --len; while (len > 0 && *p != ' ' && *p != '\t') { ++p; --len; value = 1; } /* A keyword should have a its value unless * "/unset" operation. */ if (!unset && value == 0) return (-1); } } return (keycnt); } static int bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path) { int f = 0; static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ /* !"$%&'()*+,-./ EXCLUSION:( )(#) */ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ /* 0123456789:;<>? EXCLUSION:(=) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ /* PQRSTUVWXYZ[\]^_ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; ssize_t ll; const char *pp = p; const char * const pp_end = pp + len; *last_is_path = 0; /* * Skip the path-name which is quoted. */ for (;pp < pp_end; ++pp) { if (!safe_char[*(const unsigned char *)pp]) { if (*pp != ' ' && *pp != '\t' && *pp != '\r' && *pp != '\n') f = 0; break; } f = 1; } ll = pp_end - pp; /* If a path-name was not found at the first, try to check * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates, * which places the path-name at the last. */ if (f == 0) { const char *pb = p + len - nl; int name_len = 0; int slash; /* The form D accepts only a single line for an entry. */ if (pb-2 >= p && pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) return (-1); if (pb-1 >= p && pb[-1] == '\\') return (-1); slash = 0; while (p <= --pb && *pb != ' ' && *pb != '\t') { if (!safe_char[*(const unsigned char *)pb]) return (-1); name_len++; /* The pathname should have a slash in this * format. */ if (*pb == '/') slash = 1; } if (name_len == 0 || slash == 0) return (-1); /* If '/' is placed at the first in this field, this is not * a valid filename. */ if (pb[1] == '/') return (-1); ll = len - nl - name_len; pp = p; *last_is_path = 1; } return (bid_keyword_list(pp, ll, 0, *last_is_path)); } #define MAX_BID_ENTRY 3 static int mtree_bid(struct archive_read *a, int best_bid) { const char *signature = "#mtree"; const char *p; (void)best_bid; /* UNUSED */ /* Now let's look at the actual header and see if it matches. */ p = __archive_read_ahead(a, strlen(signature), NULL); if (p == NULL) return (-1); if (memcmp(p, signature, strlen(signature)) == 0) return (8 * (int)strlen(signature)); /* * There is not a mtree signature. Let's try to detect mtree format. */ return (detect_form(a, NULL)); } static int detect_form(struct archive_read *a, int *is_form_d) { const char *p; ssize_t avail, ravail; ssize_t detected_bytes = 0, len, nl; int entry_cnt = 0, multiline = 0; int form_D = 0;/* The archive is generated by `NetBSD mtree -D' * (In this source we call it `form D') . */ if (is_form_d != NULL) *is_form_d = 0; p = __archive_read_ahead(a, 1, &avail); if (p == NULL) return (-1); ravail = avail; for (;;) { len = next_line(a, &p, &avail, &ravail, &nl); /* The terminal character of the line should be * a new line character, '\r\n' or '\n'. */ if (len <= 0 || nl == 0) break; if (!multiline) { /* Leading whitespace is never significant, * ignore it. */ while (len > 0 && (*p == ' ' || *p == '\t')) { ++p; --avail; --len; } /* Skip comment or empty line. */ if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') { p += len; avail -= len; continue; } } else { /* A continuance line; the terminal * character of previous line was '\' character. */ if (bid_keyword_list(p, len, 0, 0) <= 0) break; if (multiline == 1) detected_bytes += len; if (p[len-nl-1] != '\\') { if (multiline == 1 && ++entry_cnt >= MAX_BID_ENTRY) break; multiline = 0; } p += len; avail -= len; continue; } if (p[0] != '/') { int last_is_path, keywords; keywords = bid_entry(p, len, nl, &last_is_path); if (keywords >= 0) { detected_bytes += len; if (form_D == 0) { if (last_is_path) form_D = 1; else if (keywords > 0) /* This line is not `form D'. */ form_D = -1; } else if (form_D == 1) { if (!last_is_path && keywords > 0) /* This this is not `form D' * and We cannot accept mixed * format. */ break; } if (!last_is_path && p[len-nl-1] == '\\') /* This line continues. */ multiline = 1; else { /* We've got plenty of correct lines * to assume that this file is a mtree * format. */ if (++entry_cnt >= MAX_BID_ENTRY) break; } } else break; } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (bid_keyword_list(p+4, len-4, 0, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else break; /* Test next line. */ p += len; avail -= len; } if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) { if (is_form_d != NULL) { if (form_D == 1) *is_form_d = 1; } return (32); } return (0); } /* * The extended mtree format permits multiple lines specifying * attributes for each file. For those entries, only the last line * is actually used. Practically speaking, that means we have * to read the entire mtree file into memory up front. * * The parsing is done in two steps. First, it is decided if a line * changes the global defaults and if it is, processed accordingly. * Otherwise, the options of the line are merged with the current * global options. */ static int add_option(struct archive_read *a, struct mtree_option **global, const char *value, size_t len) { struct mtree_option *opt; if ((opt = malloc(sizeof(*opt))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } if ((opt->value = malloc(len + 1)) == NULL) { free(opt); archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(opt->value, value, len); opt->value[len] = '\0'; opt->next = *global; *global = opt; return (ARCHIVE_OK); } static void remove_option(struct mtree_option **global, const char *value, size_t len) { struct mtree_option *iter, *last; last = NULL; for (iter = *global; iter != NULL; last = iter, iter = iter->next) { if (strncmp(iter->value, value, len) == 0 && (iter->value[len] == '\0' || iter->value[len] == '=')) break; } if (iter == NULL) return; if (last == NULL) *global = iter->next; else last->next = iter->next; free(iter->value); free(iter); } static int process_global_set(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next, *eq; size_t len; int r; line += 4; for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq > next) len = next - line; else len = eq - line; remove_option(global, line, len); r = add_option(a, global, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int process_global_unset(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next; size_t len; line += 6; if (strchr(line, '=') != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "/unset shall not contain `='"); return ARCHIVE_FATAL; } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; len = strcspn(line, " \t\r\n"); if (len == 3 && strncmp(line, "all", 3) == 0) { free_options(*global); *global = NULL; } else { remove_option(global, line, len); } line += len; } } static int process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_option **global, const char *line, ssize_t line_len, struct mtree_entry **last_entry, int is_form_d) { struct mtree_entry *entry, *ht_iter; struct mtree_option *iter; const char *next, *eq, *name, *end; size_t name_len, len; int r, i; unsigned int ht_idx; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } entry->next = NULL; entry->options = NULL; entry->name = NULL; entry->used = 0; entry->full = 0; entry->name_hash = 0; entry->hashtable_next = NULL; /* Add this entry to list. */ if (*last_entry == NULL) mtree->entries = entry; else (*last_entry)->next = entry; *last_entry = entry; if (is_form_d) { /* Filename is last item on line. */ /* Adjust line_len to trim trailing whitespace */ while (line_len > 0) { char last_character = line[line_len - 1]; if (last_character == '\r' || last_character == '\n' || last_character == '\t' || last_character == ' ') { line_len--; } else { break; } } /* Name starts after the last whitespace separator */ name = line; for (i = 0; i < line_len; i++) { if (line[i] == '\r' || line[i] == '\n' || line[i] == '\t' || line[i] == ' ') { name = line + i + 1; } } name_len = line + line_len - name; end = name; } else { /* Filename is first item on line */ name_len = strcspn(line, " \t\r\n"); name = line; line += name_len; end = line + line_len; } /* name/name_len is the name within the line. */ /* line..end brackets the entire line except the name */ if ((entry->name = malloc(name_len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(entry->name, name, name_len); entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); entry->name_hash = hash(entry->name); ht_idx = entry->name_hash % MTREE_HASHTABLE_SIZE; if ((ht_iter = mtree->entry_hashtable[ht_idx]) != NULL) { while (ht_iter->hashtable_next) ht_iter = ht_iter->hashtable_next; ht_iter->hashtable_next = entry; } else { mtree->entry_hashtable[ht_idx] = entry; } for (iter = *global; iter != NULL; iter = iter->next) { r = add_option(a, &entry->options, iter->value, strlen(iter->value)); if (r != ARCHIVE_OK) return (r); } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); if (next >= end) return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq == NULL || eq > next) len = next - line; else len = eq - line; remove_option(&entry->options, line, len); r = add_option(a, &entry->options, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; uintmax_t counter; char *p; struct mtree_option *global; struct mtree_entry *last_entry; int r, is_form_d; mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; global = NULL; last_entry = NULL; (void)detect_form(a, &is_form_d); for (counter = 1; ; ++counter) { len = readline(a, mtree, &p, 65536); if (len == 0) { mtree->this_entry = mtree->entries; free_options(global); return (ARCHIVE_OK); } if (len < 0) { free_options(global); return ((int)len); } /* Leading whitespace is never significant, ignore it. */ while (*p == ' ' || *p == '\t') { ++p; --len; } /* Skip content lines and blank lines. */ if (*p == '#') continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (p[4] != ' ' && p[4] != '\t') break; r = process_global_set(a, &global, p); } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (p[6] != ' ' && p[6] != '\t') break; r = process_global_unset(a, &global, p); } else break; if (r != ARCHIVE_OK) { free_options(global); return r; } } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't parse line %ju", counter); free_options(global); return (ARCHIVE_FATAL); } /* * Read in the entire mtree file into memory on the first request. * Then use the next unused file to satisfy each header request. */ static int read_header(struct archive_read *a, struct archive_entry *entry) { struct mtree *mtree; char *p; int r, use_next; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } if (mtree->entries == NULL) { mtree->resolver = archive_entry_linkresolver_new(); if (mtree->resolver == NULL) return ARCHIVE_FATAL; archive_entry_linkresolver_set_strategy(mtree->resolver, ARCHIVE_FORMAT_MTREE); r = read_mtree(a, mtree); if (r != ARCHIVE_OK) return (r); } a->archive.archive_format = mtree->archive_format; a->archive.archive_format_name = mtree->archive_format_name; for (;;) { if (mtree->this_entry == NULL) return (ARCHIVE_EOF); if (strcmp(mtree->this_entry->name, "..") == 0) { mtree->this_entry->used = 1; if (archive_strlen(&mtree->current_dir) > 0) { /* Roll back current path. */ p = mtree->current_dir.s + mtree->current_dir.length - 1; while (p >= mtree->current_dir.s && *p != '/') --p; if (p >= mtree->current_dir.s) --p; mtree->current_dir.length = p - mtree->current_dir.s + 1; } } if (!mtree->this_entry->used) { use_next = 0; r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); if (use_next == 0) return (r); } mtree->this_entry = mtree->this_entry->next; } } /* * A single file can have multiple lines contribute specifications. * Parse as many lines as necessary, then pull additional information * from a backing file on disk as necessary. */ static int parse_file(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mentry, int *use_next) { const char *path; struct stat st_storage, *st; struct mtree_entry *mp; struct archive_entry *sparse_entry; int r = ARCHIVE_OK, r1, parsed_kws; mentry->used = 1; /* Initialize reasonable defaults. */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_size(entry, 0); archive_string_empty(&mtree->contents_name); /* Parse options from this line. */ parsed_kws = 0; r = parse_line(a, entry, mtree, mentry, &parsed_kws); if (mentry->full) { archive_entry_copy_pathname(entry, mentry->name); /* * "Full" entries are allowed to have multiple lines * and those lines aren't required to be adjacent. We * don't support multiple lines for "relative" entries * nor do we make any attempt to merge data from * separate "relative" and "full" entries. (Merging * "relative" and "full" entries would require dealing * with pathname canonicalization, which is a very * tricky subject.) */ for (mp = mentry->hashtable_next; mp != NULL; mp = mp->hashtable_next) { if (mp->full && !mp->used && mentry->name_hash == mp->name_hash && strcmp(mentry->name, mp->name) == 0) { /* Later lines override earlier ones. */ mp->used = 1; r1 = parse_line(a, entry, mtree, mp, &parsed_kws); if (r1 < r) r = r1; } } } else { /* * Relative entries require us to construct * the full path and possibly update the * current directory. */ size_t n = archive_strlen(&mtree->current_dir); if (n > 0) archive_strcat(&mtree->current_dir, "/"); archive_strcat(&mtree->current_dir, mentry->name); archive_entry_copy_pathname(entry, mtree->current_dir.s); if (archive_entry_filetype(entry) != AE_IFDIR) mtree->current_dir.length = n; } if (mtree->checkfs) { /* * Try to open and stat the file to get the real size * and other file info. It would be nice to avoid * this here so that getting a listing of an mtree * wouldn't require opening every referenced contents * file. But then we wouldn't know the actual * contents size, so I don't see a really viable way * around this. (Also, we may want to someday pull * other unspecified info from the contents file on * disk.) */ mtree->fd = -1; if (archive_strlen(&mtree->contents_name) > 0) path = mtree->contents_name.s; else path = archive_entry_pathname(entry); if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(mtree->fd); if (mtree->fd == -1 && (errno != ENOENT || archive_strlen(&mtree->contents_name) > 0)) { archive_set_error(&a->archive, errno, "Can't open %s", path); r = ARCHIVE_WARN; } } st = &st_storage; if (mtree->fd >= 0) { if (fstat(mtree->fd, st) == -1) { archive_set_error(&a->archive, errno, "Could not fstat %s", path); r = ARCHIVE_WARN; /* If we can't stat it, don't keep it open. */ close(mtree->fd); mtree->fd = -1; st = NULL; } } else if (lstat(path, st) == -1) { st = NULL; } /* * Check for a mismatch between the type in the specification * and the type of the contents object on disk. */ if (st != NULL) { if (((st->st_mode & S_IFMT) == S_IFREG && archive_entry_filetype(entry) == AE_IFREG) #ifdef S_IFLNK ||((st->st_mode & S_IFMT) == S_IFLNK && archive_entry_filetype(entry) == AE_IFLNK) #endif #ifdef S_IFSOCK ||((st->st_mode & S_IFSOCK) == S_IFSOCK && archive_entry_filetype(entry) == AE_IFSOCK) #endif #ifdef S_IFCHR ||((st->st_mode & S_IFMT) == S_IFCHR && archive_entry_filetype(entry) == AE_IFCHR) #endif #ifdef S_IFBLK ||((st->st_mode & S_IFMT) == S_IFBLK && archive_entry_filetype(entry) == AE_IFBLK) #endif ||((st->st_mode & S_IFMT) == S_IFDIR && archive_entry_filetype(entry) == AE_IFDIR) #ifdef S_IFIFO ||((st->st_mode & S_IFMT) == S_IFIFO && archive_entry_filetype(entry) == AE_IFIFO) #endif ) { /* Types match. */ } else { /* Types don't match; bail out gracefully. */ if (mtree->fd >= 0) close(mtree->fd); mtree->fd = -1; if (parsed_kws & MTREE_HAS_OPTIONAL) { /* It's not an error for an optional * entry to not match disk. */ *use_next = 1; } else if (r == ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "mtree specification has different" " type for %s", archive_entry_pathname(entry)); r = ARCHIVE_WARN; } return (r); } } /* * If there is a contents file on disk, pick some of the * metadata from that file. For most of these, we only * set it from the contents if it wasn't already parsed * from the specification. */ if (st != NULL) { if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) && (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK)) archive_entry_set_rdev(entry, st->st_rdev); if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_gid(entry, st->st_gid); if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_uid(entry, st->st_uid); if ((parsed_kws & MTREE_HAS_MTIME) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime*1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec*1000); #else archive_entry_set_mtime(entry, st->st_mtime, 0); #endif } if ((parsed_kws & MTREE_HAS_NLINK) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_nlink(entry, st->st_nlink); if ((parsed_kws & MTREE_HAS_PERM) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_perm(entry, st->st_mode); if ((parsed_kws & MTREE_HAS_SIZE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_size(entry, st->st_size); archive_entry_set_ino(entry, st->st_ino); archive_entry_set_dev(entry, st->st_dev); archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); } else if (parsed_kws & MTREE_HAS_OPTIONAL) { /* * Couldn't open the entry, stat it or the on-disk type * didn't match. If this entry is optional, just * ignore it and read the next header entry. */ *use_next = 1; return ARCHIVE_OK; } } mtree->cur_size = archive_entry_size(entry); mtree->offset = 0; return r; } /* * Each line contains a sequence of keywords. */ static int parse_line(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws) { struct mtree_option *iter; int r = ARCHIVE_OK, r1; for (iter = mp->options; iter != NULL; iter = iter->next) { r1 = parse_keyword(a, mtree, entry, iter, parsed_kws); if (r1 < r) r = r1; } if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Missing type keyword in mtree specification"); return (ARCHIVE_WARN); } return (r); } /* * Device entries have one of the following forms: * - raw dev_t * - format,major,minor[,subdevice] * When parsing succeeded, `pdev' will contain the appropriate dev_t value. */ /* strsep() is not in C90, but strcspn() is. */ /* Taken from http://unixpapa.com/incnote/string.html */ static char * la_strsep(char **sp, const char *sep) { char *p, *s; if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL); s = *sp; p = s + strcspn(s, sep); if (*p != '\0') *p++ = '\0'; *sp = p; return(s); } static int parse_device(dev_t *pdev, struct archive *a, char *val) { #define MAX_PACK_ARGS 3 unsigned long numbers[MAX_PACK_ARGS]; char *p, *dev; int argc; pack_t *pack; dev_t result; const char *error = NULL; memset(pdev, 0, sizeof(*pdev)); if ((dev = strchr(val, ',')) != NULL) { /* * Device's major/minor are given in a specified format. * Decode and pack it accordingly. */ *dev++ = '\0'; if ((pack = pack_find(val)) == NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown format `%s'", val); return ARCHIVE_WARN; } argc = 0; while ((p = la_strsep(&dev, ",")) != NULL) { if (*p == '\0') { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Missing number"); return ARCHIVE_WARN; } if (argc >= MAX_PACK_ARGS) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Too many arguments"); return ARCHIVE_WARN; } numbers[argc++] = (unsigned long)mtree_atol(&p); } if (argc < 2) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Not enough arguments"); return ARCHIVE_WARN; } result = (*pack)(argc, numbers, &error); if (error != NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "%s", error); return ARCHIVE_WARN; } } else { /* file system raw value. */ result = (dev_t)mtree_atol(&val); } *pdev = result; return ARCHIVE_OK; #undef MAX_PACK_ARGS } /* * Parse a single keyword and its value. */ static int parse_keyword(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws) { char *val, *key; key = opt->value; if (*key == '\0') return (ARCHIVE_OK); if (strcmp(key, "nochange") == 0) { *parsed_kws |= MTREE_HAS_NOCHANGE; return (ARCHIVE_OK); } if (strcmp(key, "optional") == 0) { *parsed_kws |= MTREE_HAS_OPTIONAL; return (ARCHIVE_OK); } if (strcmp(key, "ignore") == 0) { /* * The mtree processing is not recursive, so * recursion will only happen for explicitly listed * entries. */ return (ARCHIVE_OK); } val = strchr(key, '='); if (val == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed attribute \"%s\" (%d)", key, key[0]); return (ARCHIVE_WARN); } *val = '\0'; ++val; switch (key[0]) { case 'c': if (strcmp(key, "content") == 0 || strcmp(key, "contents") == 0) { parse_escapes(val, NULL); archive_strcpy(&mtree->contents_name, val); break; } if (strcmp(key, "cksum") == 0) break; case 'd': if (strcmp(key, "device") == 0) { /* stat(2) st_rdev field, e.g. the major/minor IDs * of a char/block special file */ int r; dev_t dev; *parsed_kws |= MTREE_HAS_DEVICE; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_rdev(entry, dev); return r; } case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); break; } case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "gname") == 0) { *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); break; } case 'i': if (strcmp(key, "inode") == 0) { archive_entry_set_ino(entry, mtree_atol10(&val)); break; } case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); break; } case 'm': if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) break; if (strcmp(key, "mode") == 0) { if (val[0] >= '0' && val[0] <= '9') { *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, (mode_t)mtree_atol8(&val)); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic mode \"%s\" unsupported", val); return ARCHIVE_WARN; } break; } case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; archive_entry_set_nlink(entry, (unsigned int)mtree_atol10(&val)); break; } case 'r': if (strcmp(key, "resdevice") == 0) { /* stat(2) st_dev field, e.g. the device ID where the * inode resides */ int r; dev_t dev; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_dev(entry, dev); return r; } if (strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) break; case 's': if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) break; if (strcmp(key, "sha256") == 0 || strcmp(key, "sha256digest") == 0) break; if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) break; if (strcmp(key, "sha512") == 0 || strcmp(key, "sha512digest") == 0) break; if (strcmp(key, "size") == 0) { archive_entry_set_size(entry, mtree_atol10(&val)); break; } case 't': if (strcmp(key, "tags") == 0) { /* * Comma delimited list of tags. * Ignore the tags for now, but the interface * should be extended to allow inclusion/exclusion. */ break; } if (strcmp(key, "time") == 0) { int64_t m; int64_t my_time_t_max = get_time_t_max(); int64_t my_time_t_min = get_time_t_min(); long ns = 0; *parsed_kws |= MTREE_HAS_MTIME; m = mtree_atol10(&val); /* Replicate an old mtree bug: * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { ++val; ns = (long)mtree_atol10(&val); if (ns < 0) ns = 0; else if (ns > 999999999) ns = 999999999; } if (m > my_time_t_max) m = my_time_t_max; else if (m < my_time_t_min) m = my_time_t_min; archive_entry_set_mtime(entry, (time_t)m, ns); break; } if (strcmp(key, "type") == 0) { switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { archive_entry_set_filetype(entry, AE_IFBLK); break; } case 'c': if (strcmp(val, "char") == 0) { archive_entry_set_filetype(entry, AE_IFCHR); break; } case 'd': if (strcmp(val, "dir") == 0) { archive_entry_set_filetype(entry, AE_IFDIR); break; } case 'f': if (strcmp(val, "fifo") == 0) { archive_entry_set_filetype(entry, AE_IFIFO); break; } if (strcmp(val, "file") == 0) { archive_entry_set_filetype(entry, AE_IFREG); break; } case 'l': if (strcmp(val, "link") == 0) { archive_entry_set_filetype(entry, AE_IFLNK); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized file type \"%s\"; " "assuming \"file\"", val); archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_WARN); } *parsed_kws |= MTREE_HAS_TYPE; break; } case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "uname") == 0) { *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { size_t bytes_to_read; ssize_t bytes_read; struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd < 0) { *buff = NULL; *offset = 0; *size = 0; return (ARCHIVE_EOF); } if (mtree->buff == NULL) { mtree->buffsize = 64 * 1024; mtree->buff = malloc(mtree->buffsize); if (mtree->buff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } *buff = mtree->buff; *offset = mtree->offset; if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset) bytes_to_read = (size_t)(mtree->cur_size - mtree->offset); else bytes_to_read = mtree->buffsize; bytes_read = read(mtree->fd, mtree->buff, bytes_to_read); if (bytes_read < 0) { archive_set_error(&a->archive, errno, "Can't read"); return (ARCHIVE_WARN); } if (bytes_read == 0) { *size = 0; return (ARCHIVE_EOF); } mtree->offset += bytes_read; *size = bytes_read; return (ARCHIVE_OK); } /* Skip does nothing except possibly close the contents file. */ static int skip(struct archive_read *a) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } return (ARCHIVE_OK); } /* * Since parsing backslash sequences always makes strings shorter, * we can always do this conversion in-place. */ static void parse_escapes(char *src, struct mtree_entry *mentry) { char *dest = src; char c; if (mentry != NULL && strcmp(src, ".") == 0) mentry->full = 1; while (*src != '\0') { c = *src++; if (c == '/' && mentry != NULL) mentry->full = 1; if (c == '\\') { switch (src[0]) { case '0': if (src[1] < '0' || src[1] > '7') { c = 0; ++src; break; } /* FALLTHROUGH */ case '1': case '2': case '3': if (src[1] >= '0' && src[1] <= '7' && src[2] >= '0' && src[2] <= '7') { c = (src[0] - '0') << 6; c |= (src[1] - '0') << 3; c |= (src[2] - '0'); src += 3; } break; case 'a': c = '\a'; ++src; break; case 'b': c = '\b'; ++src; break; case 'f': c = '\f'; ++src; break; case 'n': c = '\n'; ++src; break; case 'r': c = '\r'; ++src; break; case 's': c = ' '; ++src; break; case 't': c = '\t'; ++src; break; case 'v': c = '\v'; ++src; break; case '\\': c = '\\'; ++src; break; } } *dest++ = c; } *dest = '\0'; } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t mtree_atol8(char **p) { int64_t l, limit, last_digit_limit; int digit, base; base = 8; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; l = 0; digit = **p - '0'; while (digit >= 0 && digit < base) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = INT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++(*p) - '0'; } return (l); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. + * + * Convert the number pointed to by 'p' into a 64-bit signed integer. + * On return, 'p' points to the first non-digit following the number. + * On overflow, the function returns INT64_MIN or INT64_MAX. */ static int64_t mtree_atol10(char **p) { - int64_t l, limit, last_digit_limit; - int base, digit, sign; + const int base = 10; + const int64_t limit = INT64_MAX / base; + const int64_t last_digit_limit = INT64_MAX % base; + int64_t l; + int sign; - base = 10; - if (**p == '-') { sign = -1; - limit = ((uint64_t)(INT64_MAX) + 1) / base; - last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; ++(*p); } else { sign = 1; - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; } l = 0; - digit = **p - '0'; - while (digit >= 0 && digit < base) { - if (l > limit || (l == limit && digit > last_digit_limit)) + while (**p >= '0' && **p < '0' + base) { + int digit = **p - '0'; + if (l > limit || (l == limit && digit > last_digit_limit)) { + while (**p >= '0' && **p < '0' + base) { + ++(*p); + } return (sign < 0) ? INT64_MIN : INT64_MAX; + } l = (l * base) + digit; - digit = *++(*p) - '0'; + ++(*p); } return (sign < 0) ? -l : l; } /* Parse a hex digit. */ static int parsehex(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') return c - 'a'; else if (c >= 'A' && c <= 'F') return c - 'A'; else return -1; } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t mtree_atol16(char **p) { int64_t l, limit, last_digit_limit; int base, digit, sign; base = 16; if (**p == '-') { sign = -1; limit = ((uint64_t)(INT64_MAX) + 1) / base; last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; ++(*p); } else { sign = 1; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; } l = 0; digit = parsehex(**p); while (digit >= 0 && digit < base) { if (l > limit || (l == limit && digit > last_digit_limit)) return (sign < 0) ? INT64_MIN : INT64_MAX; l = (l * base) + digit; digit = parsehex(*++(*p)); } return (sign < 0) ? -l : l; } static int64_t mtree_atol(char **p) { if (**p != '0') return mtree_atol10(p); if ((*p)[1] == 'x' || (*p)[1] == 'X') { *p += 2; return mtree_atol16(p); } return mtree_atol8(p); } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. */ static ssize_t readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; const void *t; void *nl; char *u; /* Accumulate line in a line buffer. */ for (;;) { /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (t == NULL) return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); nl = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read to end exactly there. */ if (nl != NULL) { bytes_read = ((const char *)nl) - ((const char *)t) + 1; } if (total_size + bytes_read + 1 > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&mtree->line, total_size + bytes_read + 1) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } /* Append new bytes to string. */ memcpy(mtree->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; mtree->line.s[total_size] = '\0'; for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { /* Ends with unescaped newline. */ *start = mtree->line.s; return total_size; } else if (u[0] == '#') { /* Ends with comment sequence #...\n */ if (nl == NULL) { /* But we've not found the \n yet */ break; } } else if (u[0] == '\\') { if (u[1] == '\n') { /* Trim escaped newline. */ total_size -= 2; mtree->line.s[total_size] = '\0'; break; } else if (u[1] != '\0') { /* Skip the two-char escape sequence */ ++u; } } } find_off = u - mtree->line.s; } } static unsigned int hash(const char *p) { /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, as used by ELF for hashing function names. */ unsigned g, h = 0; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xF0000000) != 0) { h ^= g >> 24; h &= 0x0FFFFFFF; } } return h; } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_tar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_tar.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_tar.c (revision 316338) @@ -1,2881 +1,2885 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_acl_private.h" /* For ACL parsing routines. */ #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #define tar_min(a,b) ((a) < (b) ? (a) : (b)) /* * Layout of POSIX 'ustar' tar header. */ struct archive_entry_header_ustar { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char checksum[8]; char typeflag[1]; char linkname[100]; /* "old format" header ends here */ char magic[6]; /* For POSIX: "ustar\0" */ char version[2]; /* For POSIX: "00" */ char uname[32]; char gname[32]; char rdevmajor[8]; char rdevminor[8]; char prefix[155]; }; /* * Structure of GNU tar header */ struct gnu_sparse { char offset[12]; char numbytes[12]; }; struct archive_entry_header_gnutar { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char checksum[8]; char typeflag[1]; char linkname[100]; char magic[8]; /* "ustar \0" (note blank/blank/null at end) */ char uname[32]; char gname[32]; char rdevmajor[8]; char rdevminor[8]; char atime[12]; char ctime[12]; char offset[12]; char longnames[4]; char unused[1]; struct gnu_sparse sparse[4]; char isextended[1]; char realsize[12]; /* * Old GNU format doesn't use POSIX 'prefix' field; they use * the 'L' (longname) entry instead. */ }; /* * Data specific to this format. */ struct sparse_block { struct sparse_block *next; int64_t offset; int64_t remaining; int hole; }; struct tar { struct archive_string acl_text; struct archive_string entry_pathname; /* For "GNU.sparse.name" and other similar path extensions. */ struct archive_string entry_pathname_override; struct archive_string entry_linkpath; struct archive_string entry_uname; struct archive_string entry_gname; struct archive_string longlink; struct archive_string longname; struct archive_string pax_header; struct archive_string pax_global; struct archive_string line; int pax_hdrcharset_binary; int header_recursion_depth; int64_t entry_bytes_remaining; int64_t entry_offset; int64_t entry_padding; int64_t entry_bytes_unconsumed; int64_t realsize; int sparse_allowed; struct sparse_block *sparse_list; struct sparse_block *sparse_last; int64_t sparse_offset; int64_t sparse_numbytes; int sparse_gnu_major; int sparse_gnu_minor; char sparse_gnu_pending; struct archive_string localname; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv; struct archive_string_conv *sconv_acl; struct archive_string_conv *sconv_default; int init_default_conversion; int compat_2x; int process_mac_extensions; int read_concatenated_archives; + int realsize_override; }; static int archive_block_is_null(const char *p); static char *base64_decode(const char *, size_t, size_t *); static int gnu_add_sparse_entry(struct archive_read *, struct tar *, int64_t offset, int64_t remaining); static void gnu_clear_sparse_list(struct tar *); static int gnu_sparse_old_read(struct archive_read *, struct tar *, const struct archive_entry_header_gnutar *header, size_t *); static int gnu_sparse_old_parse(struct archive_read *, struct tar *, const struct gnu_sparse *sparse, int length); static int gnu_sparse_01_parse(struct archive_read *, struct tar *, const char *); static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *, size_t *); static int header_Solaris_ACL(struct archive_read *, struct tar *, struct archive_entry *, const void *, size_t *); static int header_common(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_old_tar(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_pax_extensions(struct archive_read *, struct tar *, struct archive_entry *, const void *, size_t *); static int header_pax_global(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_longlink(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_longname(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int read_mac_metadata_blob(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_volume(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_ustar(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_gnutar(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int archive_read_format_tar_bid(struct archive_read *, int); static int archive_read_format_tar_options(struct archive_read *, const char *, const char *); static int archive_read_format_tar_cleanup(struct archive_read *); static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static int archive_read_format_tar_skip(struct archive_read *a); static int archive_read_format_tar_read_header(struct archive_read *, struct archive_entry *); static int checksum(struct archive_read *, const void *); static int pax_attribute(struct archive_read *, struct tar *, struct archive_entry *, const char *key, const char *value, size_t value_length); static int pax_attribute_acl(struct archive_read *, struct tar *, struct archive_entry *, const char *, int); static int pax_attribute_xattr(struct archive_entry *, const char *, const char *); static int pax_header(struct archive_read *, struct tar *, struct archive_entry *, struct archive_string *); static void pax_time(const char *, int64_t *sec, long *nanos); static ssize_t readline(struct archive_read *, struct tar *, const char **, ssize_t limit, size_t *); static int read_body_to_string(struct archive_read *, struct tar *, struct archive_string *, const void *h, size_t *); static int solaris_sparse_parse(struct archive_read *, struct tar *, struct archive_entry *, const char *); static int64_t tar_atol(const char *, size_t); static int64_t tar_atol10(const char *, size_t); static int64_t tar_atol256(const char *, size_t); static int64_t tar_atol8(const char *, size_t); static int tar_read_header(struct archive_read *, struct tar *, struct archive_entry *, size_t *); static int tohex(int c); static char *url_decode(const char *); static void tar_flush_unconsumed(struct archive_read *, size_t *); int archive_read_support_format_gnutar(struct archive *a) { archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar"); return (archive_read_support_format_tar(a)); } int archive_read_support_format_tar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct tar *tar; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_tar"); tar = (struct tar *)calloc(1, sizeof(*tar)); #ifdef HAVE_COPYFILE_H /* Set this by default on Mac OS. */ tar->process_mac_extensions = 1; #endif if (tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } r = __archive_read_register_format(a, tar, "tar", archive_read_format_tar_bid, archive_read_format_tar_options, archive_read_format_tar_read_header, archive_read_format_tar_read_data, archive_read_format_tar_skip, NULL, archive_read_format_tar_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(tar); return (ARCHIVE_OK); } static int archive_read_format_tar_cleanup(struct archive_read *a) { struct tar *tar; tar = (struct tar *)(a->format->data); gnu_clear_sparse_list(tar); archive_string_free(&tar->acl_text); archive_string_free(&tar->entry_pathname); archive_string_free(&tar->entry_pathname_override); archive_string_free(&tar->entry_linkpath); archive_string_free(&tar->entry_uname); archive_string_free(&tar->entry_gname); archive_string_free(&tar->line); archive_string_free(&tar->pax_global); archive_string_free(&tar->pax_header); archive_string_free(&tar->longname); archive_string_free(&tar->longlink); archive_string_free(&tar->localname); free(tar); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * Validate number field * * This has to be pretty lenient in order to accommodate the enormous * variety of tar writers in the world: * = POSIX (IEEE Std 1003.1-1988) ustar requires octal values with leading * zeros and allows fields to be terminated with space or null characters * = Many writers use different termination (in particular, libarchive * omits terminator bytes to squeeze one or two more digits) * = Many writers pad with space and omit leading zeros * = GNU tar and star write base-256 values if numbers are too * big to be represented in octal * * Examples of specific tar headers that we should support: * = Perl Archive::Tar terminates uid, gid, devminor and devmajor with two * null bytes, pads size with spaces and other numeric fields with zeroes * = plexus-archiver prior to 2.6.3 (before switching to commons-compress) * may have uid and gid fields filled with spaces without any octal digits * at all and pads all numeric fields with spaces * * This should tolerate all variants in use. It will reject a field * where the writer just left garbage after a trailing NUL. */ static int validate_number_field(const char* p_field, size_t i_size) { unsigned char marker = (unsigned char)p_field[0]; if (marker == 128 || marker == 255 || marker == 0) { /* Base-256 marker, there's nothing we can check. */ return 1; } else { /* Must be octal */ size_t i = 0; /* Skip any leading spaces */ while (i < i_size && p_field[i] == ' ') { ++i; } /* Skip octal digits. */ while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') { ++i; } /* Any remaining characters must be space or NUL padding. */ while (i < i_size) { if (p_field[i] != ' ' && p_field[i] != 0) { return 0; } ++i; } return 1; } } static int archive_read_format_tar_bid(struct archive_read *a, int best_bid) { int bid; const char *h; const struct archive_entry_header_ustar *header; (void)best_bid; /* UNUSED */ bid = 0; /* Now let's look at the actual header and see if it matches. */ h = __archive_read_ahead(a, 512, NULL); if (h == NULL) return (-1); /* If it's an end-of-archive mark, we can handle it. */ if (h[0] == 0 && archive_block_is_null(h)) { /* * Usually, I bid the number of bits verified, but * in this case, 4096 seems excessive so I picked 10 as * an arbitrary but reasonable-seeming value. */ return (10); } /* If it's not an end-of-archive mark, it must have a valid checksum.*/ if (!checksum(a, h)) return (0); bid += 48; /* Checksum is usually 6 octal digits. */ header = (const struct archive_entry_header_ustar *)h; /* Recognize POSIX formats. */ if ((memcmp(header->magic, "ustar\0", 6) == 0) && (memcmp(header->version, "00", 2) == 0)) bid += 56; /* Recognize GNU tar format. */ if ((memcmp(header->magic, "ustar ", 6) == 0) && (memcmp(header->version, " \0", 2) == 0)) bid += 56; /* Type flag must be null, digit or A-Z, a-z. */ if (header->typeflag[0] != 0 && !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') && !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') && !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') ) return (0); bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ /* * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields. */ if (bid > 0 && ( validate_number_field(header->mode, sizeof(header->mode)) == 0 || validate_number_field(header->uid, sizeof(header->uid)) == 0 || validate_number_field(header->gid, sizeof(header->gid)) == 0 || validate_number_field(header->mtime, sizeof(header->mtime)) == 0 || validate_number_field(header->size, sizeof(header->size)) == 0 || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0 || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) { bid = 0; } return (bid); } static int archive_read_format_tar_options(struct archive_read *a, const char *key, const char *val) { struct tar *tar; int ret = ARCHIVE_FAILED; tar = (struct tar *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle UTF-8 filenames as libarchive 2.x */ tar->compat_2x = (val != NULL && val[0] != 0); tar->init_default_conversion = tar->compat_2x; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "tar: hdrcharset option needs a character-set name"); else { tar->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (tar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "mac-ext") == 0) { tar->process_mac_extensions = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } else if (strcmp(key, "read_concatenated_archives") == 0) { tar->read_concatenated_archives = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } /* utility function- this exists to centralize the logic of tracking * how much unconsumed data we have floating around, and to consume * anything outstanding since we're going to do read_aheads */ static void tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed) { if (*unconsumed) { /* void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL); * this block of code is to poison claimed unconsumed space, ensuring * things break if it is in use still. * currently it WILL break things, so enable it only for debugging this issue if (data) { memset(data, 0xff, *unconsumed); } */ __archive_read_consume(a, *unconsumed); *unconsumed = 0; } } /* * The function invoked by archive_read_next_header(). This * just sets up a few things and then calls the internal * tar_read_header() function below. */ static int archive_read_format_tar_read_header(struct archive_read *a, struct archive_entry *entry) { /* * When converting tar archives to cpio archives, it is * essential that each distinct file have a distinct inode * number. To simplify this, we keep a static count here to * assign fake dev/inode numbers to each tar entry. Note that * pax format archives may overwrite this with something more * useful. * * Ideally, we would track every file read from the archive so * that we could assign the same dev/ino pair to hardlinks, * but the memory required to store a complete lookup table is * probably not worthwhile just to support the relatively * obscure tar->cpio conversion case. */ static int default_inode; static int default_dev; struct tar *tar; const char *p; const wchar_t *wp; int r; size_t l, unconsumed = 0; /* Assign default device/inode values. */ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */ /* Limit generated st_ino number to 16 bits. */ if (default_inode >= 0xffff) { ++default_dev; default_inode = 0; } tar = (struct tar *)(a->format->data); tar->entry_offset = 0; gnu_clear_sparse_list(tar); tar->realsize = -1; /* Mark this as "unset" */ + tar->realsize_override = 0; /* Setup default string conversion. */ tar->sconv = tar->opt_sconv; if (tar->sconv == NULL) { if (!tar->init_default_conversion) { tar->sconv_default = archive_string_default_conversion_for_read(&(a->archive)); tar->init_default_conversion = 1; } tar->sconv = tar->sconv_default; } r = tar_read_header(a, tar, entry, &unconsumed); tar_flush_unconsumed(a, &unconsumed); /* * "non-sparse" files are really just sparse files with * a single block. */ if (tar->sparse_list == NULL) { if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { struct sparse_block *sb; for (sb = tar->sparse_list; sb != NULL; sb = sb->next) { if (!sb->hole) archive_entry_sparse_add_entry(entry, sb->offset, sb->remaining); } } if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) { /* * "Regular" entry with trailing '/' is really * directory: This is needed for certain old tar * variants and even for some broken newer ones. */ if ((wp = archive_entry_pathname_w(entry)) != NULL) { l = wcslen(wp); if (l > 0 && wp[l - 1] == L'/') { archive_entry_set_filetype(entry, AE_IFDIR); } } else if ((p = archive_entry_pathname(entry)) != NULL) { l = strlen(p); if (l > 0 && p[l - 1] == '/') { archive_entry_set_filetype(entry, AE_IFDIR); } } } return (r); } static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct tar *tar; struct sparse_block *p; tar = (struct tar *)(a->format->data); for (;;) { /* Remove exhausted entries from sparse list. */ while (tar->sparse_list != NULL && tar->sparse_list->remaining == 0) { p = tar->sparse_list; tar->sparse_list = p->next; free(p); } if (tar->entry_bytes_unconsumed) { __archive_read_consume(a, tar->entry_bytes_unconsumed); tar->entry_bytes_unconsumed = 0; } /* If we're at end of file, return EOF. */ if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) { if (__archive_read_consume(a, tar->entry_padding) < 0) return (ARCHIVE_FATAL); tar->entry_padding = 0; *buff = NULL; *size = 0; *offset = tar->realsize; return (ARCHIVE_EOF); } *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated tar archive"); return (ARCHIVE_FATAL); } if (bytes_read > tar->entry_bytes_remaining) bytes_read = (ssize_t)tar->entry_bytes_remaining; /* Don't read more than is available in the * current sparse block. */ if (tar->sparse_list->remaining < bytes_read) bytes_read = (ssize_t)tar->sparse_list->remaining; *size = bytes_read; *offset = tar->sparse_list->offset; tar->sparse_list->remaining -= bytes_read; tar->sparse_list->offset += bytes_read; tar->entry_bytes_remaining -= bytes_read; tar->entry_bytes_unconsumed = bytes_read; if (!tar->sparse_list->hole) return (ARCHIVE_OK); /* Current is hole data and skip this. */ } } static int archive_read_format_tar_skip(struct archive_read *a) { int64_t bytes_skipped; int64_t request; struct sparse_block *p; struct tar* tar; tar = (struct tar *)(a->format->data); /* Do not consume the hole of a sparse file. */ request = 0; for (p = tar->sparse_list; p != NULL; p = p->next) { if (!p->hole) { if (p->remaining >= INT64_MAX - request) { return ARCHIVE_FATAL; } request += p->remaining; } } if (request > tar->entry_bytes_remaining) request = tar->entry_bytes_remaining; request += tar->entry_padding + tar->entry_bytes_unconsumed; bytes_skipped = __archive_read_consume(a, request); if (bytes_skipped < 0) return (ARCHIVE_FATAL); tar->entry_bytes_remaining = 0; tar->entry_bytes_unconsumed = 0; tar->entry_padding = 0; /* Free the sparse list. */ gnu_clear_sparse_list(tar); return (ARCHIVE_OK); } /* * This function recursively interprets all of the headers associated * with a single entry. */ static int tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, size_t *unconsumed) { ssize_t bytes; int err; const char *h; const struct archive_entry_header_ustar *header; const struct archive_entry_header_gnutar *gnuheader; /* Loop until we find a workable header record. */ for (;;) { tar_flush_unconsumed(a, unconsumed); /* Read 512-byte header record */ h = __archive_read_ahead(a, 512, &bytes); if (bytes < 0) return ((int)bytes); if (bytes == 0) { /* EOF at a block boundary. */ /* Some writers do omit the block of nulls. */ return (ARCHIVE_EOF); } if (bytes < 512) { /* Short block at EOF; this is bad. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive"); return (ARCHIVE_FATAL); } *unconsumed = 512; /* Header is workable if it's not an end-of-archive mark. */ if (h[0] != 0 || !archive_block_is_null(h)) break; /* Ensure format is set for archives with only null blocks. */ if (a->archive.archive_format_name == NULL) { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar"; } if (!tar->read_concatenated_archives) { /* Try to consume a second all-null record, as well. */ tar_flush_unconsumed(a, unconsumed); h = __archive_read_ahead(a, 512, NULL); if (h != NULL && h[0] == 0 && archive_block_is_null(h)) __archive_read_consume(a, 512); archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* * We're reading concatenated archives, ignore this block and * loop to get the next. */ } /* * Note: If the checksum fails and we return ARCHIVE_RETRY, * then the client is likely to just retry. This is a very * crude way to search for the next valid header! * * TODO: Improve this by implementing a real header scan. */ if (!checksum(a, h)) { tar_flush_unconsumed(a, unconsumed); archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); return (ARCHIVE_RETRY); /* Retryable: Invalid header */ } if (++tar->header_recursion_depth > 32) { tar_flush_unconsumed(a, unconsumed); archive_set_error(&a->archive, EINVAL, "Too many special headers"); return (ARCHIVE_WARN); } /* Determine the format variant. */ header = (const struct archive_entry_header_ustar *)h; switch(header->typeflag[0]) { case 'A': /* Solaris tar ACL */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "Solaris tar"; err = header_Solaris_ACL(a, tar, entry, h, unconsumed); break; case 'g': /* POSIX-standard 'g' header. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_global(a, tar, entry, h, unconsumed); if (err == ARCHIVE_EOF) return (err); break; case 'K': /* Long link name (GNU tar, others) */ err = header_longlink(a, tar, entry, h, unconsumed); break; case 'L': /* Long filename (GNU tar, others) */ err = header_longname(a, tar, entry, h, unconsumed); break; case 'V': /* GNU volume header */ err = header_volume(a, tar, entry, h, unconsumed); break; case 'X': /* Used by SUN tar; same as 'x'. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format (Sun variant)"; err = header_pax_extensions(a, tar, entry, h, unconsumed); break; case 'x': /* POSIX-standard 'x' header. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_extensions(a, tar, entry, h, unconsumed); break; default: gnuheader = (const struct archive_entry_header_gnutar *)h; if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; a->archive.archive_format_name = "GNU tar format"; err = header_gnutar(a, tar, entry, h, unconsumed); } else if (memcmp(header->magic, "ustar", 5) == 0) { if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; a->archive.archive_format_name = "POSIX ustar format"; } err = header_ustar(a, tar, entry, h); } else { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar (non-POSIX)"; err = header_old_tar(a, tar, entry, h); } } if (err == ARCHIVE_FATAL) return (err); tar_flush_unconsumed(a, unconsumed); h = NULL; header = NULL; --tar->header_recursion_depth; /* Yuck. Apple's design here ends up storing long pathname * extensions for both the AppleDouble extension entry and the * regular entry. */ if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) && tar->header_recursion_depth == 0 && tar->process_mac_extensions) { int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed); if (err2 < err) err = err2; } /* We return warnings or success as-is. Anything else is fatal. */ if (err == ARCHIVE_WARN || err == ARCHIVE_OK) { if (tar->sparse_gnu_pending) { if (tar->sparse_gnu_major == 1 && tar->sparse_gnu_minor == 0) { ssize_t bytes_read; tar->sparse_gnu_pending = 0; /* Read initial sparse map. */ bytes_read = gnu_sparse_10_read(a, tar, unconsumed); if (bytes_read < 0) return ((int)bytes_read); tar->entry_bytes_remaining -= bytes_read; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unrecognized GNU sparse file format"); return (ARCHIVE_WARN); } tar->sparse_gnu_pending = 0; } return (err); } if (err == ARCHIVE_EOF) /* EOF when recursively reading a header is bad. */ archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); return (ARCHIVE_FATAL); } /* * Return true if block checksum is correct. */ static int checksum(struct archive_read *a, const void *h) { const unsigned char *bytes; const struct archive_entry_header_ustar *header; int check, sum; size_t i; (void)a; /* UNUSED */ bytes = (const unsigned char *)h; header = (const struct archive_entry_header_ustar *)h; /* Checksum field must hold an octal number */ for (i = 0; i < sizeof(header->checksum); ++i) { char c = header->checksum[i]; if (c != ' ' && c != '\0' && (c < '0' || c > '7')) return 0; } /* * Test the checksum. Note that POSIX specifies _unsigned_ * bytes for this calculation. */ sum = (int)tar_atol(header->checksum, sizeof(header->checksum)); check = 0; for (i = 0; i < 148; i++) check += (unsigned char)bytes[i]; for (; i < 156; i++) check += 32; for (; i < 512; i++) check += (unsigned char)bytes[i]; if (sum == check) return (1); /* * Repeat test with _signed_ bytes, just in case this archive * was created by an old BSD, Solaris, or HP-UX tar with a * broken checksum calculation. */ check = 0; for (i = 0; i < 148; i++) check += (signed char)bytes[i]; for (; i < 156; i++) check += 32; for (; i < 512; i++) check += (signed char)bytes[i]; if (sum == check) return (1); return (0); } /* * Return true if this block contains only nulls. */ static int archive_block_is_null(const char *p) { unsigned i; for (i = 0; i < 512; i++) if (*p++) return (0); return (1); } /* * Interpret 'A' Solaris ACL header */ static int header_Solaris_ACL(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { const struct archive_entry_header_ustar *header; size_t size; int err, acl_type; int64_t type; char *acl, *p; /* * read_body_to_string adds a NUL terminator, but we need a little * more to make sure that we don't overrun acl_text later. */ header = (const struct archive_entry_header_ustar *)h; size = (size_t)tar_atol(header->size, sizeof(header->size)); err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed); if (err != ARCHIVE_OK) return (err); /* Recursively read next header */ err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* TODO: Examine the first characters to see if this * is an AIX ACL descriptor. We'll likely never support * them, but it would be polite to recognize and warn when * we do see them. */ /* Leading octal number indicates ACL type and number of entries. */ p = acl = tar->acl_text.s; type = 0; while (*p != '\0' && p < acl + size) { if (*p < '0' || *p > '7') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (invalid digit)"); return(ARCHIVE_WARN); } type <<= 3; type += *p - '0'; if (type > 077777777) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (count too large)"); return (ARCHIVE_WARN); } p++; } switch ((int)type & ~0777777) { case 01000000: /* POSIX.1e ACL */ acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; break; case 03000000: /* NFSv4 ACL */ acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (unsupported type %o)", (int)type); return (ARCHIVE_WARN); } p++; if (p >= acl + size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (body overflow)"); return(ARCHIVE_WARN); } /* ACL text is null-terminated; find the end. */ size -= (p - acl); acl = p; while (*p != '\0' && p < acl + size) p++; if (tar->sconv_acl == NULL) { tar->sconv_acl = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (tar->sconv_acl == NULL) return (ARCHIVE_FATAL); } archive_strncpy(&(tar->localname), acl, p - acl); err = archive_acl_from_text_l(archive_entry_acl(entry), tar->localname.s, acl_type, tar->sconv_acl); if (err != ARCHIVE_OK) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for ACL"); } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (unparsable)"); } return (err); } /* * Interpret 'K' long linkname header. */ static int header_longlink(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* Set symlink if symlink already set, else hardlink. */ archive_entry_copy_link(entry, tar->longlink.s); return (ARCHIVE_OK); } static int set_conversion_failed_error(struct archive_read *a, struct archive_string_conv *sconv, const char *name) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for %s", name); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "%s can't be converted from %s to current locale.", name, archive_string_conversion_charset_name(sconv)); return (ARCHIVE_WARN); } /* * Interpret 'L' long filename header. */ static int header_longname(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed); if (err != ARCHIVE_OK) return (err); /* Read and parse "real" header, then override name. */ err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); if (archive_entry_copy_pathname_l(entry, tar->longname.s, archive_strlen(&(tar->longname)), tar->sconv) != 0) err = set_conversion_failed_error(a, tar->sconv, "Pathname"); return (err); } /* * Interpret 'V' GNU tar volume header. */ static int header_volume(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { (void)h; /* Just skip this and read the next header. */ return (tar_read_header(a, tar, entry, unconsumed)); } /* * Read body of an archive entry into an archive_string object. */ static int read_body_to_string(struct archive_read *a, struct tar *tar, struct archive_string *as, const void *h, size_t *unconsumed) { int64_t size; const struct archive_entry_header_ustar *header; const void *src; (void)tar; /* UNUSED */ header = (const struct archive_entry_header_ustar *)h; size = tar_atol(header->size, sizeof(header->size)); if ((size > 1048576) || (size < 0)) { archive_set_error(&a->archive, EINVAL, "Special header too large"); return (ARCHIVE_FATAL); } /* Fail if we can't make our buffer big enough. */ if (archive_string_ensure(as, (size_t)size+1) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } tar_flush_unconsumed(a, unconsumed); /* Read the body into the string. */ *unconsumed = (size_t)((size + 511) & ~ 511); src = __archive_read_ahead(a, *unconsumed, NULL); if (src == NULL) { *unconsumed = 0; return (ARCHIVE_FATAL); } memcpy(as->s, src, (size_t)size); as->s[size] = '\0'; as->length = (size_t)size; return (ARCHIVE_OK); } /* * Parse out common header elements. * * This would be the same as header_old_tar, except that the * filename is handled slightly differently for old and POSIX * entries (POSIX entries support a 'prefix'). This factoring * allows header_old_tar and header_ustar * to handle filenames differently, while still putting most of the * common parsing into one place. */ static int header_common(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; char tartype; int err = ARCHIVE_OK; header = (const struct archive_entry_header_ustar *)h; if (header->linkname[0]) archive_strncpy(&(tar->entry_linkpath), header->linkname, sizeof(header->linkname)); else archive_string_empty(&(tar->entry_linkpath)); /* Parse out the numeric fields (all are octal) */ archive_entry_set_mode(entry, (mode_t)tar_atol(header->mode, sizeof(header->mode))); archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid))); archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size)); if (tar->entry_bytes_remaining < 0) { tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Tar entry has negative size"); return (ARCHIVE_FATAL); } if (tar->entry_bytes_remaining == INT64_MAX) { /* Note: tar_atol returns INT64_MAX on overflow */ tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Tar entry size overflow"); return (ARCHIVE_FATAL); } tar->realsize = tar->entry_bytes_remaining; archive_entry_set_size(entry, tar->entry_bytes_remaining); archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); /* Handle the tar type flag appropriately. */ tartype = header->typeflag[0]; switch (tartype) { case '1': /* Hard link */ if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Linkname"); if (err == ARCHIVE_FATAL) return (err); } /* * The following may seem odd, but: Technically, tar * does not store the file type for a "hard link" * entry, only the fact that it is a hard link. So, I * leave the type zero normally. But, pax interchange * format allows hard links to have data, which * implies that the underlying entry is a regular * file. */ if (archive_entry_size(entry) > 0) archive_entry_set_filetype(entry, AE_IFREG); /* * A tricky point: Traditionally, tar readers have * ignored the size field when reading hardlink * entries, and some writers put non-zero sizes even * though the body is empty. POSIX blessed this * convention in the 1988 standard, but broke with * this tradition in 2001 by permitting hardlink * entries to store valid bodies in pax interchange * format, but not in ustar format. Since there is no * hard and fast way to distinguish pax interchange * from earlier archives (the 'x' and 'g' entries are * optional, after all), we need a heuristic. */ if (archive_entry_size(entry) == 0) { /* If the size is already zero, we're done. */ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { /* Definitely pax extended; must obey hardlink size. */ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR) { /* Old-style or GNU tar: we must ignore the size. */ archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; } else if (archive_read_format_tar_bid(a, 50) > 50) { /* * We don't know if it's pax: If the bid * function sees a valid ustar header * immediately following, then let's ignore * the hardlink size. */ archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; } /* * TODO: There are still two cases I'd like to handle: * = a ustar non-pax archive with a hardlink entry at * end-of-archive. (Look for block of nulls following?) * = a pax archive that has not seen any pax headers * and has an entry which is a hardlink entry storing * a body containing an uncompressed tar archive. * The first is worth addressing; I don't see any reliable * way to deal with the second possibility. */ break; case '2': /* Symlink */ archive_entry_set_filetype(entry, AE_IFLNK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Linkname"); if (err == ARCHIVE_FATAL) return (err); } break; case '3': /* Character device */ archive_entry_set_filetype(entry, AE_IFCHR); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '4': /* Block device */ archive_entry_set_filetype(entry, AE_IFBLK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '5': /* Dir */ archive_entry_set_filetype(entry, AE_IFDIR); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '6': /* FIFO device */ archive_entry_set_filetype(entry, AE_IFIFO); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case 'D': /* GNU incremental directory type */ /* * No special handling is actually required here. * It might be nice someday to preprocess the file list and * provide it to the client, though. */ archive_entry_set_filetype(entry, AE_IFDIR); break; case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/ /* * As far as I can tell, this is just like a regular file * entry, except that the contents should be _appended_ to * the indicated file at the indicated offset. This may * require some API work to fully support. */ break; case 'N': /* Old GNU "long filename" entry. */ /* The body of this entry is a script for renaming * previously-extracted entries. Ugh. It will never * be supported by libarchive. */ archive_entry_set_filetype(entry, AE_IFREG); break; case 'S': /* GNU sparse files */ /* * Sparse files are really just regular files with * sparse information in the extended area. */ /* FALLTHROUGH */ case '0': /* * Enable sparse file "read" support only for regular * files and explicit GNU sparse files. However, we * don't allow non-standard file types to be sparse. */ tar->sparse_allowed = 1; /* FALLTHROUGH */ default: /* Regular file and non-standard types */ /* * Per POSIX: non-recognized types should always be * treated as regular files. */ archive_entry_set_filetype(entry, AE_IFREG); break; } return (err); } /* * Parse out header elements for "old-style" tar archives. */ static int header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; int err = ARCHIVE_OK, err2; /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_ustar *)h; if (archive_entry_copy_pathname_l(entry, header->name, sizeof(header->name), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } /* Grab rest of common fields */ err2 = header_common(a, tar, entry, h); if (err > err2) err = err2; tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Read a Mac AppleDouble-encoded blob of file metadata, * if there is one. */ static int read_mac_metadata_blob(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int64_t size; const void *data; const char *p, *name; const wchar_t *wp, *wname; (void)h; /* UNUSED */ wname = wp = archive_entry_pathname_w(entry); if (wp != NULL) { /* Find the last path element. */ for (; *wp != L'\0'; ++wp) { if (wp[0] == '/' && wp[1] != L'\0') wname = wp + 1; } /* * If last path element starts with "._", then * this is a Mac extension. */ if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0') return ARCHIVE_OK; } else { /* Find the last path element. */ name = p = archive_entry_pathname(entry); if (p == NULL) return (ARCHIVE_FAILED); for (; *p != '\0'; ++p) { if (p[0] == '/' && p[1] != '\0') name = p + 1; } /* * If last path element starts with "._", then * this is a Mac extension. */ if (name[0] != '.' || name[1] != '_' || name[2] == '\0') return ARCHIVE_OK; } /* Read the body as a Mac OS metadata blob. */ size = archive_entry_size(entry); /* * TODO: Look beyond the body here to peek at the next header. * If it's a regular header (not an extension header) * that has the wrong name, just return the current * entry as-is, without consuming the body here. * That would reduce the risk of us mis-identifying * an ordinary file that just happened to have * a name starting with "._". * * Q: Is the above idea really possible? Even * when there are GNU or pax extension entries? */ data = __archive_read_ahead(a, (size_t)size, NULL); if (data == NULL) { *unconsumed = 0; return (ARCHIVE_FATAL); } archive_entry_copy_mac_metadata(entry, data, (size_t)size); *unconsumed = (size_t)((size + 511) & ~ 511); tar_flush_unconsumed(a, unconsumed); return (tar_read_header(a, tar, entry, unconsumed)); } /* * Parse a file header for a pax extended archive entry. */ static int header_pax_global(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry, unconsumed); return (err); } static int header_pax_extensions(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err, err2; err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed); if (err != ARCHIVE_OK) return (err); /* Parse the next header. */ err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* * TODO: Parse global/default options into 'entry' struct here * before handling file-specific options. * * This design (parse standard header, then overwrite with pax * extended attribute data) usually works well, but isn't ideal; * it would be better to parse the pax extended attributes first * and then skip any fields in the standard header that were * defined in the pax header. */ err2 = pax_header(a, tar, entry, &tar->pax_header); err = err_combine(err, err2); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Parse a file header for a Posix "ustar" archive entry. This also * handles "pax" or "extended ustar" entries. */ static int header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; struct archive_string *as; int err = ARCHIVE_OK, r; header = (const struct archive_entry_header_ustar *)h; /* Copy name into an internal buffer to ensure null-termination. */ as = &(tar->entry_pathname); if (header->prefix[0]) { archive_strncpy(as, header->prefix, sizeof(header->prefix)); if (as->s[archive_strlen(as) - 1] != '/') archive_strappend_char(as, '/'); archive_strncat(as, header->name, sizeof(header->name)); } else { archive_strncpy(as, header->name, sizeof(header->name)); } if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } /* Handle rest of common fields. */ r = header_common(a, tar, entry, h); if (r == ARCHIVE_FATAL) return (r); if (r < err) err = r; /* Handle POSIX ustar fields. */ if (archive_entry_copy_uname_l(entry, header->uname, sizeof(header->uname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Uname"); if (err == ARCHIVE_FATAL) return (err); } if (archive_entry_copy_gname_l(entry, header->gname, sizeof(header->gname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); } /* Parse out device numbers only for char and block specials. */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { archive_entry_set_rdevmajor(entry, (dev_t) tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); archive_entry_set_rdevminor(entry, (dev_t) tar_atol(header->rdevminor, sizeof(header->rdevminor))); } tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Parse the pax extended attributes record. * * Returns non-zero if there's an error in the data. */ static int pax_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct archive_string *in_as) { size_t attr_length, l, line_length, value_length; char *p; char *key, *value; struct archive_string *as; struct archive_string_conv *sconv; int err, err2; char *attr = in_as->s; attr_length = in_as->length; tar->pax_hdrcharset_binary = 0; archive_string_empty(&(tar->entry_gname)); archive_string_empty(&(tar->entry_linkpath)); archive_string_empty(&(tar->entry_pathname)); archive_string_empty(&(tar->entry_pathname_override)); archive_string_empty(&(tar->entry_uname)); err = ARCHIVE_OK; while (attr_length > 0) { /* Parse decimal length field at start of line. */ line_length = 0; l = attr_length; p = attr; /* Record start of line. */ while (l>0) { if (*p == ' ') { p++; l--; break; } if (*p < '0' || *p > '9') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring malformed pax extended attributes"); return (ARCHIVE_WARN); } line_length *= 10; line_length += *p - '0'; if (line_length > 999999) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Rejecting pax extended attribute > 1MB"); return (ARCHIVE_WARN); } p++; l--; } /* * Parsed length must be no bigger than available data, * at least 1, and the last character of the line must * be '\n'. */ if (line_length > attr_length || line_length < 1 || attr[line_length - 1] != '\n') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring malformed pax extended attribute"); return (ARCHIVE_WARN); } /* Null-terminate the line. */ attr[line_length - 1] = '\0'; /* Find end of key and null terminate it. */ key = p; if (key[0] == '=') return (-1); while (*p && *p != '=') ++p; if (*p == '\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid pax extended attributes"); return (ARCHIVE_WARN); } *p = '\0'; value = p + 1; /* Some values may be binary data */ value_length = attr + line_length - 1 - value; /* Identify this attribute and set it in the entry. */ err2 = pax_attribute(a, tar, entry, key, value, value_length); if (err2 == ARCHIVE_FATAL) return (err2); err = err_combine(err, err2); /* Skip to next line */ attr += line_length; attr_length -= line_length; } /* * PAX format uses UTF-8 as default charset for its metadata * unless hdrcharset=BINARY is present in its header. * We apply the charset specified by the hdrcharset option only * when the hdrcharset attribute(in PAX header) is BINARY because * we respect the charset described in PAX header and BINARY also * means that metadata(filename,uname and gname) character-set * is unknown. */ if (tar->pax_hdrcharset_binary) sconv = tar->opt_sconv; else { sconv = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (sconv == NULL) return (ARCHIVE_FATAL); if (tar->compat_2x) archive_string_conversion_set_opt(sconv, SCONV_SET_OPT_UTF8_LIBARCHIVE2X); } if (archive_strlen(&(tar->entry_gname)) > 0) { if (archive_entry_copy_gname_l(entry, tar->entry_gname.s, archive_strlen(&(tar->entry_gname)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_gname(entry, tar->entry_gname.s); } } if (archive_strlen(&(tar->entry_linkpath)) > 0) { if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Linkname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_link(entry, tar->entry_linkpath.s); } } /* * Some extensions (such as the GNU sparse file extensions) * deliberately store a synthetic name under the regular 'path' * attribute and the real file name under a different attribute. * Since we're supposed to not care about the order, we * have no choice but to store all of the various filenames * we find and figure it all out afterwards. This is the * figuring out part. */ as = NULL; if (archive_strlen(&(tar->entry_pathname_override)) > 0) as = &(tar->entry_pathname_override); else if (archive_strlen(&(tar->entry_pathname)) > 0) as = &(tar->entry_pathname); if (as != NULL) { if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_pathname(entry, as->s); } } if (archive_strlen(&(tar->entry_uname)) > 0) { if (archive_entry_copy_uname_l(entry, tar->entry_uname.s, archive_strlen(&(tar->entry_uname)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Uname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_uname(entry, tar->entry_uname.s); } } return (err); } static int pax_attribute_xattr(struct archive_entry *entry, const char *name, const char *value) { char *name_decoded; void *value_decoded; size_t value_len; if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0) return 3; name += 17; /* URL-decode name */ name_decoded = url_decode(name); if (name_decoded == NULL) return 2; /* Base-64 decode value */ value_decoded = base64_decode(value, strlen(value), &value_len); if (value_decoded == NULL) { free(name_decoded); return 1; } archive_entry_xattr_add_entry(entry, name_decoded, value_decoded, value_len); free(name_decoded); free(value_decoded); return 0; } static int pax_attribute_schily_xattr(struct archive_entry *entry, const char *name, const char *value, size_t value_length) { if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0) return 1; name += 13; archive_entry_xattr_add_entry(entry, name, value, value_length); return 0; } static int pax_attribute_acl(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const char *value, int type) { int r; const char* errstr; switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: errstr = "SCHILY.acl.access"; break; case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: errstr = "SCHILY.acl.default"; break; case ARCHIVE_ENTRY_ACL_TYPE_NFS4: errstr = "SCHILY.acl.ace"; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown ACL type: %d", type); return(ARCHIVE_FATAL); } if (tar->sconv_acl == NULL) { tar->sconv_acl = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (tar->sconv_acl == NULL) return (ARCHIVE_FATAL); } r = archive_acl_from_text_l(archive_entry_acl(entry), value, type, tar->sconv_acl); if (r != ARCHIVE_OK) { if (r == ARCHIVE_FATAL) { archive_set_error(&a->archive, ENOMEM, "%s %s", "Can't allocate memory for ", errstr); return (r); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr); } return (r); } /* * Parse a single key=value attribute. key/value pointers are * assumed to point into reasonably long-lived storage. * * Note that POSIX reserves all-lowercase keywords. Vendor-specific * extensions should always have keywords of the form "VENDOR.attribute" * In particular, it's quite feasible to support many different * vendor extensions here. I'm using "LIBARCHIVE" for extensions * unique to this library. * * Investigate other vendor-specific extensions and see if * any of them look useful. */ static int pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const char *key, const char *value, size_t value_length) { int64_t s; long n; int err = ARCHIVE_OK, r; #ifndef __FreeBSD__ if (value == NULL) value = ""; /* Disable compiler warning; do not pass * NULL pointer to strlen(). */ #endif switch (key[0]) { case 'G': /* Reject GNU.sparse.* headers on non-regular files. */ if (strncmp(key, "GNU.sparse", 10) == 0 && !tar->sparse_allowed) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Non-regular file cannot be sparse"); return (ARCHIVE_FATAL); } /* GNU "0.0" sparse pax format. */ if (strcmp(key, "GNU.sparse.numblocks") == 0) { tar->sparse_offset = -1; tar->sparse_numbytes = -1; tar->sparse_gnu_major = 0; tar->sparse_gnu_minor = 0; } if (strcmp(key, "GNU.sparse.offset") == 0) { tar->sparse_offset = tar_atol10(value, strlen(value)); if (tar->sparse_numbytes != -1) { if (gnu_add_sparse_entry(a, tar, tar->sparse_offset, tar->sparse_numbytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); tar->sparse_offset = -1; tar->sparse_numbytes = -1; } } if (strcmp(key, "GNU.sparse.numbytes") == 0) { tar->sparse_numbytes = tar_atol10(value, strlen(value)); if (tar->sparse_numbytes != -1) { if (gnu_add_sparse_entry(a, tar, tar->sparse_offset, tar->sparse_numbytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); tar->sparse_offset = -1; tar->sparse_numbytes = -1; } } if (strcmp(key, "GNU.sparse.size") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); + tar->realsize_override = 1; } /* GNU "0.1" sparse pax format. */ if (strcmp(key, "GNU.sparse.map") == 0) { tar->sparse_gnu_major = 0; tar->sparse_gnu_minor = 1; if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK) return (ARCHIVE_WARN); } /* GNU "1.0" sparse pax format */ if (strcmp(key, "GNU.sparse.major") == 0) { tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value)); tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.minor") == 0) { tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value)); tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.name") == 0) { /* * The real filename; when storing sparse * files, GNU tar puts a synthesized name into * the regular 'path' attribute in an attempt * to limit confusion. ;-) */ archive_strcpy(&(tar->entry_pathname_override), value); } if (strcmp(key, "GNU.sparse.realsize") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); + tar->realsize_override = 1; } break; case 'L': /* Our extensions */ /* TODO: Handle arbitrary extended attributes... */ /* if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0) archive_entry_set_xxxxxx(entry, value); */ if (strcmp(key, "LIBARCHIVE.creationtime") == 0) { pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) pax_attribute_xattr(entry, key, value); break; case 'S': /* We support some keys used by the "star" archiver */ if (strcmp(key, "SCHILY.acl.access") == 0) { r = pax_attribute_acl(a, tar, entry, value, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); if (r == ARCHIVE_FATAL) return (r); } else if (strcmp(key, "SCHILY.acl.default") == 0) { r = pax_attribute_acl(a, tar, entry, value, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); if (r == ARCHIVE_FATAL) return (r); } else if (strcmp(key, "SCHILY.acl.ace") == 0) { r = pax_attribute_acl(a, tar, entry, value, ARCHIVE_ENTRY_ACL_TYPE_NFS4); if (r == ARCHIVE_FATAL) return (r); } else if (strcmp(key, "SCHILY.devmajor") == 0) { archive_entry_set_rdevmajor(entry, (dev_t)tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.devminor") == 0) { archive_entry_set_rdevminor(entry, (dev_t)tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.fflags") == 0) { archive_entry_copy_fflags_text(entry, value); } else if (strcmp(key, "SCHILY.dev") == 0) { archive_entry_set_dev(entry, (dev_t)tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.ino") == 0) { archive_entry_set_ino(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.nlink") == 0) { archive_entry_set_nlink(entry, (unsigned) tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.realsize") == 0) { tar->realsize = tar_atol10(value, strlen(value)); + tar->realsize_override = 1; archive_entry_set_size(entry, tar->realsize); } else if (strncmp(key, "SCHILY.xattr.", 13) == 0) { pax_attribute_schily_xattr(entry, key, value, value_length); } else if (strcmp(key, "SUN.holesdata") == 0) { /* A Solaris extension for sparse. */ r = solaris_sparse_parse(a, tar, entry, value); if (r < err) { if (r == ARCHIVE_FATAL) return (r); err = r; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Parse error: SUN.holesdata"); } } break; case 'a': if (strcmp(key, "atime") == 0) { pax_time(value, &s, &n); archive_entry_set_atime(entry, s, n); } break; case 'c': if (strcmp(key, "ctime") == 0) { pax_time(value, &s, &n); archive_entry_set_ctime(entry, s, n); } else if (strcmp(key, "charset") == 0) { /* TODO: Publish charset information in entry. */ } else if (strcmp(key, "comment") == 0) { /* TODO: Publish comment in entry. */ } break; case 'g': if (strcmp(key, "gid") == 0) { archive_entry_set_gid(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "gname") == 0) { archive_strcpy(&(tar->entry_gname), value); } break; case 'h': if (strcmp(key, "hdrcharset") == 0) { if (strcmp(value, "BINARY") == 0) /* Binary mode. */ tar->pax_hdrcharset_binary = 1; else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0) tar->pax_hdrcharset_binary = 0; } break; case 'l': /* pax interchange doesn't distinguish hardlink vs. symlink. */ if (strcmp(key, "linkpath") == 0) { archive_strcpy(&(tar->entry_linkpath), value); } break; case 'm': if (strcmp(key, "mtime") == 0) { pax_time(value, &s, &n); archive_entry_set_mtime(entry, s, n); } break; case 'p': if (strcmp(key, "path") == 0) { archive_strcpy(&(tar->entry_pathname), value); } break; case 'r': /* POSIX has reserved 'realtime.*' */ break; case 's': /* POSIX has reserved 'security.*' */ /* Someday: if (strcmp(key, "security.acl") == 0) { ... } */ if (strcmp(key, "size") == 0) { /* "size" is the size of the data in the entry. */ tar->entry_bytes_remaining = tar_atol10(value, strlen(value)); /* - * But, "size" is not necessarily the size of - * the file on disk; if this is a sparse file, - * the disk size may have already been set from - * GNU.sparse.realsize or GNU.sparse.size or - * an old GNU header field or SCHILY.realsize - * or .... + * The "size" pax header keyword always overrides the + * "size" field in the tar header. + * GNU.sparse.realsize, GNU.sparse.size and + * SCHILY.realsize override this value. */ - if (tar->realsize < 0) { + if (!tar->realsize_override) { archive_entry_set_size(entry, tar->entry_bytes_remaining); tar->realsize = tar->entry_bytes_remaining; } } break; case 'u': if (strcmp(key, "uid") == 0) { archive_entry_set_uid(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "uname") == 0) { archive_strcpy(&(tar->entry_uname), value); } break; } return (err); } /* * parse a decimal time value, which may include a fractional portion */ static void pax_time(const char *p, int64_t *ps, long *pn) { char digit; int64_t s; unsigned long l; int sign; int64_t limit, last_digit_limit; limit = INT64_MAX / 10; last_digit_limit = INT64_MAX % 10; s = 0; sign = 1; if (*p == '-') { sign = -1; p++; } while (*p >= '0' && *p <= '9') { digit = *p - '0'; if (s > limit || (s == limit && digit > last_digit_limit)) { s = INT64_MAX; break; } s = (s * 10) + digit; ++p; } *ps = s * sign; /* Calculate nanoseconds. */ *pn = 0; if (*p != '.') return; l = 100000000UL; do { ++p; if (*p >= '0' && *p <= '9') *pn += (*p - '0') * l; else break; } while (l /= 10); } /* * Parse GNU tar header */ static int header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { const struct archive_entry_header_gnutar *header; int64_t t; int err = ARCHIVE_OK; /* * GNU header is like POSIX ustar, except 'prefix' is * replaced with some other fields. This also means the * filename is stored as in old-style archives. */ /* Grab fields common to all tar variants. */ err = header_common(a, tar, entry, h); if (err == ARCHIVE_FATAL) return (err); /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_gnutar *)h; if (archive_entry_copy_pathname_l(entry, header->name, sizeof(header->name), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } /* Fields common to ustar and GNU */ /* XXX Can the following be factored out since it's common * to ustar and gnu tar? Is it okay to move it down into * header_common, perhaps? */ if (archive_entry_copy_uname_l(entry, header->uname, sizeof(header->uname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Uname"); if (err == ARCHIVE_FATAL) return (err); } if (archive_entry_copy_gname_l(entry, header->gname, sizeof(header->gname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); } /* Parse out device numbers only for char and block specials */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { archive_entry_set_rdevmajor(entry, (dev_t) tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); archive_entry_set_rdevminor(entry, (dev_t) tar_atol(header->rdevminor, sizeof(header->rdevminor))); } else archive_entry_set_rdev(entry, 0); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); /* Grab GNU-specific fields. */ t = tar_atol(header->atime, sizeof(header->atime)); if (t > 0) archive_entry_set_atime(entry, t, 0); t = tar_atol(header->ctime, sizeof(header->ctime)); if (t > 0) archive_entry_set_ctime(entry, t, 0); if (header->realsize[0] != 0) { tar->realsize = tar_atol(header->realsize, sizeof(header->realsize)); archive_entry_set_size(entry, tar->realsize); + tar->realsize_override = 1; } if (header->sparse[0].offset[0] != 0) { if (gnu_sparse_old_read(a, tar, header, unconsumed) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (header->isextended[0] != 0) { /* XXX WTF? XXX */ } } return (err); } static int gnu_add_sparse_entry(struct archive_read *a, struct tar *tar, int64_t offset, int64_t remaining) { struct sparse_block *p; p = (struct sparse_block *)calloc(1, sizeof(*p)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (tar->sparse_last != NULL) tar->sparse_last->next = p; else tar->sparse_list = p; tar->sparse_last = p; if (remaining < 0 || offset < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data"); return (ARCHIVE_FATAL); } p->offset = offset; p->remaining = remaining; return (ARCHIVE_OK); } static void gnu_clear_sparse_list(struct tar *tar) { struct sparse_block *p; while (tar->sparse_list != NULL) { p = tar->sparse_list; tar->sparse_list = p->next; free(p); } tar->sparse_last = NULL; } /* * GNU tar old-format sparse data. * * GNU old-format sparse data is stored in a fixed-field * format. Offset/size values are 11-byte octal fields (same * format as 'size' field in ustart header). These are * stored in the header, allocating subsequent header blocks * as needed. Extending the header in this way is a pretty * severe POSIX violation; this design has earned GNU tar a * lot of criticism. */ static int gnu_sparse_old_read(struct archive_read *a, struct tar *tar, const struct archive_entry_header_gnutar *header, size_t *unconsumed) { ssize_t bytes_read; const void *data; struct extended { struct gnu_sparse sparse[21]; char isextended[1]; char padding[7]; }; const struct extended *ext; if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (header->isextended[0] == 0) return (ARCHIVE_OK); do { tar_flush_unconsumed(a, unconsumed); data = __archive_read_ahead(a, 512, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read < 512) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " "detected while reading sparse file data"); return (ARCHIVE_FATAL); } *unconsumed = 512; ext = (const struct extended *)data; if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK) return (ARCHIVE_FATAL); } while (ext->isextended[0] != 0); if (tar->sparse_list != NULL) tar->entry_offset = tar->sparse_list->offset; return (ARCHIVE_OK); } static int gnu_sparse_old_parse(struct archive_read *a, struct tar *tar, const struct gnu_sparse *sparse, int length) { while (length > 0 && sparse->offset[0] != 0) { if (gnu_add_sparse_entry(a, tar, tar_atol(sparse->offset, sizeof(sparse->offset)), tar_atol(sparse->numbytes, sizeof(sparse->numbytes))) != ARCHIVE_OK) return (ARCHIVE_FATAL); sparse++; length--; } return (ARCHIVE_OK); } /* * GNU tar sparse format 0.0 * * Beginning with GNU tar 1.15, sparse files are stored using * information in the pax extended header. The GNU tar maintainers * have gone through a number of variations in the process of working * out this scheme; fortunately, they're all numbered. * * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to * store offset/size for each block. The repeated instances of these * latter fields violate the pax specification (which frowns on * duplicate keys), so this format was quickly replaced. */ /* * GNU tar sparse format 0.1 * * This version replaced the offset/numbytes attributes with * a single "map" attribute that stored a list of integers. This * format had two problems: First, the "map" attribute could be very * long, which caused problems for some implementations. More * importantly, the sparse data was lost when extracted by archivers * that didn't recognize this extension. */ static int gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p) { const char *e; int64_t offset = -1, size = -1; for (;;) { e = p; while (*e != '\0' && *e != ',') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; } if (offset < 0) { offset = tar_atol10(p, e - p); if (offset < 0) return (ARCHIVE_WARN); } else { size = tar_atol10(p, e - p); if (size < 0) return (ARCHIVE_WARN); if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); offset = -1; } if (*e == '\0') return (ARCHIVE_OK); p = e + 1; } } /* * GNU tar sparse format 1.0 * * The idea: The offset/size data is stored as a series of base-10 * ASCII numbers prepended to the file data, so that dearchivers that * don't support this format will extract the block map along with the * data and a separate post-process can restore the sparseness. * * Unfortunately, GNU tar 1.16 had a bug that added unnecessary * padding to the body of the file when using this format. GNU tar * 1.17 corrected this bug without bumping the version number, so * it's not possible to support both variants. This code supports * the later variant at the expense of not supporting the former. * * This variant also replaced GNU.sparse.size with GNU.sparse.realsize * and introduced the GNU.sparse.major/GNU.sparse.minor attributes. */ /* * Read the next line from the input, and parse it as a decimal * integer followed by '\n'. Returns positive integer value or * negative on error. */ static int64_t gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, int64_t *remaining, size_t *unconsumed) { int64_t l, limit, last_digit_limit; const char *p; ssize_t bytes_read; int base, digit; base = 10; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; /* * Skip any lines starting with '#'; GNU tar specs * don't require this, but they should. */ do { bytes_read = readline(a, tar, &p, (ssize_t)tar_min(*remaining, 100), unconsumed); if (bytes_read <= 0) return (ARCHIVE_FATAL); *remaining -= bytes_read; } while (p[0] == '#'); l = 0; while (bytes_read > 0) { if (*p == '\n') return (l); if (*p < '0' || *p >= '0' + base) return (ARCHIVE_WARN); digit = *p - '0'; if (l > limit || (l == limit && digit > last_digit_limit)) l = INT64_MAX; /* Truncate on overflow. */ else l = (l * base) + digit; p++; bytes_read--; } /* TODO: Error message. */ return (ARCHIVE_WARN); } /* * Returns length (in bytes) of the sparse data description * that was read. */ static ssize_t gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed) { ssize_t bytes_read; int entries; int64_t offset, size, to_skip, remaining; /* Clear out the existing sparse list. */ gnu_clear_sparse_list(tar); remaining = tar->entry_bytes_remaining; /* Parse entries. */ entries = (int)gnu_sparse_10_atol(a, tar, &remaining, unconsumed); if (entries < 0) return (ARCHIVE_FATAL); /* Parse the individual entries. */ while (entries-- > 0) { /* Parse offset/size */ offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed); if (offset < 0) return (ARCHIVE_FATAL); size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed); if (size < 0) return (ARCHIVE_FATAL); /* Add a new sparse entry. */ if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Skip rest of block... */ tar_flush_unconsumed(a, unconsumed); bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining); to_skip = 0x1ff & -bytes_read; /* Fail if tar->entry_bytes_remaing would get negative */ if (to_skip > remaining) return (ARCHIVE_FATAL); if (to_skip != __archive_read_consume(a, to_skip)) return (ARCHIVE_FATAL); return ((ssize_t)(bytes_read + to_skip)); } /* * Solaris pax extension for a sparse file. This is recorded with the * data and hole pairs. The way recording sparse information by Solaris' * pax simply indicates where data and sparse are, so the stored contents * consist of both data and hole. */ static int solaris_sparse_parse(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const char *p) { const char *e; int64_t start, end; int hole = 1; (void)entry; /* UNUSED */ end = 0; if (*p == ' ') p++; else return (ARCHIVE_WARN); for (;;) { e = p; while (*e != '\0' && *e != ' ') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; } start = end; end = tar_atol10(p, e - p); if (end < 0) return (ARCHIVE_WARN); if (start < end) { if (gnu_add_sparse_entry(a, tar, start, end - start) != ARCHIVE_OK) return (ARCHIVE_FATAL); tar->sparse_last->hole = hole; } if (*e == '\0') return (ARCHIVE_OK); p = e + 1; hole = hole == 0; } } /*- * Convert text->integer. * * Traditional tar formats (including POSIX) specify base-8 for * all of the standard numeric fields. This is a significant limitation * in practice: * = file size is limited to 8GB * = rdevmajor and rdevminor are limited to 21 bits * = uid/gid are limited to 21 bits * * There are two workarounds for this: * = pax extended headers, which use variable-length string fields * = GNU tar and STAR both allow either base-8 or base-256 in * most fields. The high bit is set to indicate base-256. * * On read, this implementation supports both extensions. */ static int64_t tar_atol(const char *p, size_t char_cnt) { /* * Technically, GNU tar considers a field to be in base-256 * only if the first byte is 0xff or 0x80. */ if (*p & 0x80) return (tar_atol256(p, char_cnt)); return (tar_atol8(p, char_cnt)); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t tar_atol_base_n(const char *p, size_t char_cnt, int base) { int64_t l, maxval, limit, last_digit_limit; int digit, sign; maxval = INT64_MAX; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; /* the pointer will not be dereferenced if char_cnt is zero * due to the way the && operator is evaluated. */ while (char_cnt != 0 && (*p == ' ' || *p == '\t')) { p++; char_cnt--; } sign = 1; if (char_cnt != 0 && *p == '-') { sign = -1; p++; char_cnt--; maxval = INT64_MIN; limit = -(INT64_MIN / base); last_digit_limit = INT64_MIN % base; } l = 0; if (char_cnt != 0) { digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt != 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { return maxval; /* Truncate on overflow. */ } l = (l * base) + digit; digit = *++p - '0'; char_cnt--; } } return (sign < 0) ? -l : l; } static int64_t tar_atol8(const char *p, size_t char_cnt) { return tar_atol_base_n(p, char_cnt, 8); } static int64_t tar_atol10(const char *p, size_t char_cnt) { return tar_atol_base_n(p, char_cnt, 10); } /* * Parse a base-256 integer. This is just a variable-length * twos-complement signed binary value in big-endian order, except * that the high-order bit is ignored. The values here can be up to * 12 bytes, so we need to be careful about overflowing 64-bit * (8-byte) integers. * * This code unashamedly assumes that the local machine uses 8-bit * bytes and twos-complement arithmetic. */ static int64_t tar_atol256(const char *_p, size_t char_cnt) { uint64_t l; const unsigned char *p = (const unsigned char *)_p; unsigned char c, neg; /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */ c = *p; if (c & 0x40) { neg = 0xff; c |= 0x80; l = ~ARCHIVE_LITERAL_ULL(0); } else { neg = 0; c &= 0x7f; l = 0; } /* If more than 8 bytes, check that we can ignore * high-order bits without overflow. */ while (char_cnt > sizeof(int64_t)) { --char_cnt; if (c != neg) return neg ? INT64_MIN : INT64_MAX; c = *++p; } /* c is first byte that fits; if sign mismatch, return overflow */ if ((c ^ neg) & 0x80) { return neg ? INT64_MIN : INT64_MAX; } /* Accumulate remaining bytes. */ while (--char_cnt > 0) { l = (l << 8) | c; c = *++p; } l = (l << 8) | c; /* Return signed twos-complement value. */ return (int64_t)(l); } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. This avoids copying * when possible. */ static ssize_t readline(struct archive_read *a, struct tar *tar, const char **start, ssize_t limit, size_t *unconsumed) { ssize_t bytes_read; ssize_t total_size = 0; const void *t; const char *s; void *p; tar_flush_unconsumed(a, unconsumed); t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n' in the read buffer, return pointer to that. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; if (bytes_read > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } *unconsumed = bytes_read; *start = s; return (bytes_read); } *unconsumed = bytes_read; /* Otherwise, we need to accumulate in a line buffer. */ for (;;) { if (total_size + bytes_read > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } memcpy(tar->line.s + total_size, t, bytes_read); tar_flush_unconsumed(a, unconsumed); total_size += bytes_read; /* If we found '\n', clean up and return. */ if (p != NULL) { *start = tar->line.s; return (total_size); } /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; } *unconsumed = bytes_read; } } /* * base64_decode - Base64 decode * * This accepts most variations of base-64 encoding, including: * * with or without line breaks * * with or without the final group padded with '=' or '_' characters * (The most economical Base-64 variant does not pad the last group and * omits line breaks; RFC1341 used for MIME requires both.) */ static char * base64_decode(const char *s, size_t len, size_t *out_len) { static const unsigned char digits[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' }; static unsigned char decode_table[128]; char *out, *d; const unsigned char *src = (const unsigned char *)s; /* If the decode table is not yet initialized, prepare it. */ if (decode_table[digits[1]] != 1) { unsigned i; memset(decode_table, 0xff, sizeof(decode_table)); for (i = 0; i < sizeof(digits); i++) decode_table[digits[i]] = i; } /* Allocate enough space to hold the entire output. */ /* Note that we may not use all of this... */ out = (char *)malloc(len - len / 4 + 1); if (out == NULL) { *out_len = 0; return (NULL); } d = out; while (len > 0) { /* Collect the next group of (up to) four characters. */ int v = 0; int group_size = 0; while (group_size < 4 && len > 0) { /* '=' or '_' padding indicates final group. */ if (*src == '=' || *src == '_') { len = 0; break; } /* Skip illegal characters (including line breaks) */ if (*src > 127 || *src < 32 || decode_table[*src] == 0xff) { len--; src++; continue; } v <<= 6; v |= decode_table[*src++]; len --; group_size++; } /* Align a short group properly. */ v <<= 6 * (4 - group_size); /* Unpack the group we just collected. */ switch (group_size) { case 4: d[2] = v & 0xff; /* FALLTHROUGH */ case 3: d[1] = (v >> 8) & 0xff; /* FALLTHROUGH */ case 2: d[0] = (v >> 16) & 0xff; break; case 1: /* this is invalid! */ break; } d += group_size * 3 / 4; } *out_len = d - out; return (out); } static char * url_decode(const char *in) { char *out, *d; const char *s; out = (char *)malloc(strlen(in) + 1); if (out == NULL) return (NULL); for (s = in, d = out; *s != '\0'; ) { if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') { /* Try to convert % escape */ int digit1 = tohex(s[1]); int digit2 = tohex(s[2]); if (digit1 >= 0 && digit2 >= 0) { /* Looks good, consume three chars */ s += 3; /* Convert output */ *d++ = ((digit1 << 4) | digit2); continue; } /* Else fall through and treat '%' as normal char */ } *d++ = *s++; } *d = '\0'; return (out); } static int tohex(int c) { if (c >= '0' && c <= '9') return (c - '0'); else if (c >= 'A' && c <= 'F') return (c - 'A' + 10); else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); else return (-1); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_zip.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 316338) @@ -1,3112 +1,3113 @@ /*- * Copyright (c) 2004-2013 Tim Kientzle * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2013 Konrad Kleine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* * The definitive documentation of the Zip file format is: * http://www.pkware.com/documents/casestudies/APPNOTE.TXT * * The Info-Zip project has pioneered various extensions to better * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855 * "Ux", and 0x7875 "ux" extensions for time and ownership * information. * * History of this code: The streaming Zip reader was first added to * libarchive in January 2005. Support for seekable input sources was * added in Nov 2011. Zip64 support (including a significant code * refactoring) was added in 2014. */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_digest_private.h" #include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_hmac_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif struct zip_entry { struct archive_rb_node node; struct zip_entry *next; int64_t local_header_offset; int64_t compressed_size; int64_t uncompressed_size; int64_t gid; int64_t uid; struct archive_string rsrcname; time_t mtime; time_t atime; time_t ctime; uint32_t crc32; uint16_t mode; uint16_t zip_flags; /* From GP Flags Field */ unsigned char compression; unsigned char system; /* From "version written by" */ unsigned char flags; /* Our extra markers. */ unsigned char decdat;/* Used for Decryption check */ /* WinZip AES encryption extra field should be available * when compression is 99. */ struct { /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */ unsigned vendor; #define AES_VENDOR_AE_1 0x0001 #define AES_VENDOR_AE_2 0x0002 /* AES encryption strength: * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */ unsigned strength; /* Actual compression method. */ unsigned char compression; } aes_extra; }; struct trad_enc_ctx { uint32_t keys[3]; }; /* Bits used in zip_flags. */ #define ZIP_ENCRYPTED (1 << 0) #define ZIP_LENGTH_AT_END (1 << 3) #define ZIP_STRONG_ENCRYPTED (1 << 6) #define ZIP_UTF8_NAME (1 << 11) /* See "7.2 Single Password Symmetric Encryption Method" in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ #define ZIP_CENTRAL_DIRECTORY_ENCRYPTED (1 << 13) /* Bits used in flags. */ #define LA_USED_ZIP64 (1 << 0) #define LA_FROM_CENTRAL_DIRECTORY (1 << 1) /* * See "WinZip - AES Encryption Information" * http://www.winzip.com/aes_info.htm */ /* Value used in compression method. */ #define WINZIP_AES_ENCRYPTION 99 /* Authentication code size. */ #define AUTH_CODE_SIZE 10 /**/ #define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) struct zip { /* Structural information about the archive. */ struct archive_string format_name; int64_t central_directory_offset; size_t central_directory_entries_total; size_t central_directory_entries_on_this_disk; int has_encrypted_entries; /* List of entries (seekable Zip only) */ struct zip_entry *zip_entries; struct archive_rb_tree tree; struct archive_rb_tree tree_rsrc; /* Bytes read but not yet consumed via __archive_read_consume() */ size_t unconsumed; /* Information about entry we're currently reading. */ struct zip_entry *entry; int64_t entry_bytes_remaining; /* These count the number of bytes actually read for the entry. */ int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; unsigned long (*crc32func)(unsigned long, const void *, size_t); char ignore_crc32; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; #ifdef HAVE_ZLIB_H unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; z_stream stream; char stream_valid; #endif struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; int init_default_conversion; int process_mac_extensions; char init_decryption; /* Decryption buffer. */ /* * The decrypted data starts at decrypted_ptr and * extends for decrypted_bytes_remaining. Decryption * adds new data to the end of this block, data is returned * to clients from the beginning. When the block hits the * end of decrypted_buffer, it has to be shuffled back to * the beginning of the buffer. */ unsigned char *decrypted_buffer; unsigned char *decrypted_ptr; size_t decrypted_buffer_size; size_t decrypted_bytes_remaining; size_t decrypted_unconsumed_bytes; /* Traditional PKWARE decryption. */ struct trad_enc_ctx tctx; char tctx_valid; /* WinZip AES decryption. */ /* Contexts used for AES decryption. */ archive_crypto_ctx cctx; char cctx_valid; archive_hmac_sha1_ctx hctx; char hctx_valid; /* Strong encryption's decryption header information. */ unsigned iv_size; unsigned alg_id; unsigned bit_len; unsigned flags; unsigned erd_size; unsigned v_size; unsigned v_crc32; uint8_t *iv; uint8_t *erd; uint8_t *v_data; }; /* Many systems define min or MIN, but not all. */ #define zipmin(a,b) ((a) < (b) ? (a) : (b)) /* ------------------------------------------------------------------------ */ /* Traditional PKWARE Decryption functions. */ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; #define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; t = (ctx->keys[1] >> 24) & 0xff; ctx->keys[2] = CRC32(ctx->keys[2], t); #undef CRC32 } static uint8_t trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; } static void trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { unsigned i, max; max = (unsigned)((in_len < out_len)? in_len: out_len); for (i = 0; i < max; i++) { uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx); out[i] = t; trad_enc_update_keys(ctx, t); } } static int trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len, const uint8_t *key, size_t key_len, uint8_t *crcchk) { uint8_t header[12]; if (key_len < 12) { *crcchk = 0xff; return -1; } ctx->keys[0] = 305419896L; ctx->keys[1] = 591751049L; ctx->keys[2] = 878082192L; for (;pw_len; --pw_len) trad_enc_update_keys(ctx, *pw++); trad_enc_decrypt_update(ctx, key, 12, header, 12); /* Return the last byte for CRC check. */ *crcchk = header[11]; return 0; } #if 0 static void crypt_derive_key_sha1(const void *p, int size, unsigned char *key, int key_size) { #define MD_SIZE 20 archive_sha1_ctx ctx; unsigned char md1[MD_SIZE]; unsigned char md2[MD_SIZE * 2]; unsigned char mkb[64]; int i; archive_sha1_init(&ctx); archive_sha1_update(&ctx, p, size); archive_sha1_final(&ctx, md1); memset(mkb, 0x36, sizeof(mkb)); for (i = 0; i < MD_SIZE; i++) mkb[i] ^= md1[i]; archive_sha1_init(&ctx); archive_sha1_update(&ctx, mkb, sizeof(mkb)); archive_sha1_final(&ctx, md2); memset(mkb, 0x5C, sizeof(mkb)); for (i = 0; i < MD_SIZE; i++) mkb[i] ^= md1[i]; archive_sha1_init(&ctx); archive_sha1_update(&ctx, mkb, sizeof(mkb)); archive_sha1_final(&ctx, md2 + MD_SIZE); if (key_size > 32) key_size = 32; memcpy(key, md2, key_size); #undef MD_SIZE } #endif /* * Common code for streaming or seeking modes. * * Includes code to read local file headers, decompress data * from entry bodies, and common API. */ static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { return crc32(crc, buff, (unsigned int)len); } /* Used by "ignorecrc32" option to speed up tests. */ static unsigned long fake_crc32(unsigned long crc, const void *buff, size_t len) { (void)crc; /* UNUSED */ (void)buff; /* UNUSED */ (void)len; /* UNUSED */ return 0; } -static struct { +static const struct { int id; const char * name; } compression_methods[] = { {0, "uncompressed"}, /* The file is stored (no compression) */ {1, "shrinking"}, /* The file is Shrunk */ {2, "reduced-1"}, /* The file is Reduced with compression factor 1 */ {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */ {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */ {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */ {6, "imploded"}, /* The file is Imploded */ {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */ {8, "deflation"}, /* The file is Deflated */ {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */ {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding * (old IBM TERSE) */ {11, "reserved"}, /* Reserved by PKWARE */ {12, "bzip"}, /* File is compressed using BZIP2 algorithm */ {13, "reserved"}, /* Reserved by PKWARE */ {14, "lzma"}, /* LZMA (EFS) */ {15, "reserved"}, /* Reserved by PKWARE */ {16, "reserved"}, /* Reserved by PKWARE */ {17, "reserved"}, /* Reserved by PKWARE */ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ {97, "wav-pack"}, /* WavPack compressed data */ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ {99, "aes"} /* WinZip AES encryption */ }; static const char * compression_name(const int compression) { static const int num_compression_methods = sizeof(compression_methods)/sizeof(compression_methods[0]); int i=0; while(compression >= 0 && i < num_compression_methods) { if (compression_methods[i].id == compression) return compression_methods[i].name; i++; } return "??"; } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t zip_time(const char *p) { int msTime, msDate; struct tm ts; msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]); msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]); memset(&ts, 0, sizeof(ts)); ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ ts.tm_mday = msDate & 0x1f; /* Day of month. */ ts.tm_hour = (msTime >> 11) & 0x1f; ts.tm_min = (msTime >> 5) & 0x3f; ts.tm_sec = (msTime << 1) & 0x3e; ts.tm_isdst = -1; return mktime(&ts); } /* * The extra data is stored as a list of * id1+size1+data1 + id2+size2+data2 ... * triplets. id and size are 2 bytes each. */ static int process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; if (extra_length == 0) { return ARCHIVE_OK; } if (extra_length < 4) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length); return ARCHIVE_FAILED; } while (offset <= extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > extra_length) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: Need %d bytes but only found %d bytes", (int)datasize, (int)(extra_length - offset)); return ARCHIVE_FAILED; } #ifdef DEBUG fprintf(stderr, "Header id 0x%04x, length %d\n", headerid, datasize); #endif switch (headerid) { case 0x0001: /* Zip64 extended information extra field. */ zip_entry->flags |= LA_USED_ZIP64; if (zip_entry->uncompressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit uncompressed size"); return ARCHIVE_FAILED; } zip_entry->uncompressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->compressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit compressed size"); return ARCHIVE_FAILED; } zip_entry->compressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->local_header_offset == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit local header offset"); return ARCHIVE_FAILED; } zip_entry->local_header_offset = t; offset += 8; datasize -= 8; } /* archive_le32dec(p + offset) gives disk * on which file starts, but we don't handle * multi-volume Zip files. */ break; #ifdef DEBUG case 0x0017: { /* Strong encryption field. */ if (archive_le16dec(p + offset) == 2) { unsigned algId = archive_le16dec(p + offset + 2); unsigned bitLen = archive_le16dec(p + offset + 4); int flags = archive_le16dec(p + offset + 6); fprintf(stderr, "algId=0x%04x, bitLen=%u, " "flgas=%d\n", algId, bitLen,flags); } break; } #endif case 0x5455: { /* Extended time field "UT". */ int flags = p[offset]; offset++; datasize--; /* Flag bits indicate which dates are present. */ if (flags & 0x01) { #ifdef DEBUG fprintf(stderr, "mtime: %lld -> %d\n", (long long)zip_entry->mtime, archive_le32dec(p + offset)); #endif if (datasize < 4) break; zip_entry->mtime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x02) { if (datasize < 4) break; zip_entry->atime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x04) { if (datasize < 4) break; zip_entry->ctime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } break; } case 0x5855: { /* Info-ZIP Unix Extra Field (old version) "UX". */ if (datasize >= 8) { zip_entry->atime = archive_le32dec(p + offset); zip_entry->mtime = archive_le32dec(p + offset + 4); } if (datasize >= 12) { zip_entry->uid = archive_le16dec(p + offset + 8); zip_entry->gid = archive_le16dec(p + offset + 10); } break; } case 0x6c78: { /* Experimental 'xl' field */ /* * Introduced Dec 2013 to provide a way to * include external file attributes (and other * fields that ordinarily appear only in * central directory) in local file header. * This provides file type and permission * information necessary to support full * streaming extraction. Currently being * discussed with other Zip developers * ... subject to change. * * Format: * The field starts with a bitmap that specifies * which additional fields are included. The * bitmap is variable length and can be extended in * the future. * * n bytes - feature bitmap: first byte has low-order * 7 bits. If high-order bit is set, a subsequent * byte holds the next 7 bits, etc. * * if bitmap & 1, 2 byte "version made by" * if bitmap & 2, 2 byte "internal file attributes" * if bitmap & 4, 4 byte "external file attributes" * if bitmap & 8, 2 byte comment length + n byte comment */ int bitmap, bitmap_last; if (datasize < 1) break; bitmap_last = bitmap = 0xff & p[offset]; offset += 1; datasize -= 1; /* We only support first 7 bits of bitmap; skip rest. */ while ((bitmap_last & 0x80) != 0 && datasize >= 1) { bitmap_last = p[offset]; offset += 1; datasize -= 1; } if (bitmap & 1) { /* 2 byte "version made by" */ if (datasize < 2) break; zip_entry->system = archive_le16dec(p + offset) >> 8; offset += 2; datasize -= 2; } if (bitmap & 2) { /* 2 byte "internal file attributes" */ uint32_t internal_attributes; if (datasize < 2) break; internal_attributes = archive_le16dec(p + offset); /* Not used by libarchive at present. */ (void)internal_attributes; /* UNUSED */ offset += 2; datasize -= 2; } if (bitmap & 4) { /* 4 byte "external file attributes" */ uint32_t external_attributes; if (datasize < 4) break; external_attributes = archive_le32dec(p + offset); if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit if (0x10 == (external_attributes & 0x10)) { zip_entry->mode = AE_IFDIR | 0775; } else { zip_entry->mode = AE_IFREG | 0664; } if (0x01 == (external_attributes & 0x01)) { // Read-only bit; strip write permissions zip_entry->mode &= 0555; } } else { zip_entry->mode = 0; } offset += 4; datasize -= 4; } if (bitmap & 8) { /* 2 byte comment length + comment */ uint32_t comment_length; if (datasize < 2) break; comment_length = archive_le16dec(p + offset); offset += 2; datasize -= 2; if (datasize < comment_length) break; /* Comment is not supported by libarchive */ offset += comment_length; datasize -= comment_length; } break; } case 0x7855: /* Info-ZIP Unix Extra Field (type 2) "Ux". */ #ifdef DEBUG fprintf(stderr, "uid %d gid %d\n", archive_le16dec(p + offset), archive_le16dec(p + offset + 2)); #endif if (datasize >= 2) zip_entry->uid = archive_le16dec(p + offset); if (datasize >= 4) zip_entry->gid = archive_le16dec(p + offset + 2); break; case 0x7875: { /* Info-Zip Unix Extra Field (type 3) "ux". */ int uidsize = 0, gidsize = 0; /* TODO: support arbitrary uidsize/gidsize. */ if (datasize >= 1 && p[offset] == 1) {/* version=1 */ if (datasize >= 4) { /* get a uid size. */ uidsize = 0xff & (int)p[offset+1]; if (uidsize == 2) zip_entry->uid = archive_le16dec( p + offset + 2); else if (uidsize == 4 && datasize >= 6) zip_entry->uid = archive_le32dec( p + offset + 2); } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ gidsize = 0xff & (int)p[offset+2+uidsize]; if (gidsize == 2) zip_entry->gid = archive_le16dec( p+offset+2+uidsize+1); else if (gidsize == 4 && datasize >= (2 + uidsize + 5)) zip_entry->gid = archive_le32dec( p+offset+2+uidsize+1); } } break; } case 0x9901: /* WinZip AES extra data field. */ if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { /* Vendor version. */ zip_entry->aes_extra.vendor = archive_le16dec(p + offset); /* AES encryption strength. */ zip_entry->aes_extra.strength = p[offset + 4]; /* Actual compression method. */ zip_entry->aes_extra.compression = p[offset + 5]; } break; default: break; } offset += datasize; } if (offset != extra_length) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed extra data: Consumed %d bytes of %d bytes", (int)offset, (int)extra_length); return ARCHIVE_FAILED; } return ARCHIVE_OK; } /* * Assumes file pointer is at beginning of local file header. */ static int zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const char *p; const void *h; const wchar_t *wp; const char *cp; size_t len, filename_length, extra_length; struct archive_string_conv *sconv; struct zip_entry *zip_entry = zip->entry; struct zip_entry zip_entry_central_dir; int ret = ARCHIVE_OK; char version; /* Save a copy of the original for consistency checks. */ zip_entry_central_dir = *zip_entry; zip->decompress_init = 0; zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); /* Setup default conversion. */ if (zip->sconv == NULL && !zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_read(&(a->archive)); zip->init_default_conversion = 1; } if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_FATAL; } version = p[4]; zip_entry->system = p[5]; zip_entry->zip_flags = archive_le16dec(p + 6); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { zip->has_encrypted_entries = 1; archive_entry_set_is_data_encrypted(entry, 1); if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED && zip_entry->zip_flags & ZIP_ENCRYPTED && zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) { archive_entry_set_is_metadata_encrypted(entry, 1); return ARCHIVE_FATAL; } } zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); zip_entry->mtime = zip_time(p + 10); zip_entry->crc32 = archive_le32dec(p + 14); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[11]; else zip_entry->decdat = p[17]; zip_entry->compressed_size = archive_le32dec(p + 18); zip_entry->uncompressed_size = archive_le32dec(p + 22); filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); __archive_read_consume(a, 30); /* Read the filename. */ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (zip_entry->zip_flags & ZIP_UTF8_NAME) { /* The filename is stored to be UTF-8. */ if (zip->sconv_utf8 == NULL) { zip->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); if (zip->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = zip->sconv_utf8; } else if (zip->sconv != NULL) sconv = zip->sconv; else sconv = zip->sconv_default; if (archive_entry_copy_pathname_l(entry, h, filename_length, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } __archive_read_consume(a, filename_length); /* Read the extra data. */ if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) { return ARCHIVE_FATAL; } __archive_read_consume(a, extra_length); /* Work around a bug in Info-Zip: When reading from a pipe, it * stats the pipe instead of synthesizing a file entry. */ if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) { zip_entry->mode &= ~ AE_IFMT; zip_entry->mode |= AE_IFREG; } /* If the mode is totally empty, set some sane default. */ if (zip_entry->mode == 0) { zip_entry->mode |= 0664; } /* Make sure that entries with a trailing '/' are marked as directories * even if the External File Attributes contains bogus values. If this * is not a directory and there is no type, assume regularfile. */ if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) { int has_slash; wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); has_slash = len > 0 && wp[len - 1] == L'/'; } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; has_slash = len > 0 && cp[len - 1] == '/'; } /* Correct file type as needed. */ if (has_slash) { zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFDIR; zip_entry->mode |= 0111; } else if ((zip_entry->mode & AE_IFMT) == 0) { zip_entry->mode |= AE_IFREG; } } /* Make sure directories end in '/' */ if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) { wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); if (len > 0 && wp[len - 1] != L'/') { struct archive_wstring s; archive_string_init(&s); archive_wstrcat(&s, wp); archive_wstrappend_wchar(&s, L'/'); archive_entry_copy_pathname_w(entry, s.s); archive_wstring_free(&s); } } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; if (len > 0 && cp[len - 1] != '/') { struct archive_string s; archive_string_init(&s); archive_strcat(&s, cp); archive_strappend_char(&s, '/'); archive_entry_set_pathname(entry, s.s); archive_string_free(&s); } } } if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) { /* If this came from the central dir, it's size info * is definitive, so ignore the length-at-end flag. */ zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END; /* If local header is missing a value, use the one from the central directory. If both have it, warn about mismatches. */ if (zip_entry->crc32 == 0) { zip_entry->crc32 = zip_entry_central_dir.crc32; } else if (!zip->ignore_crc32 && zip_entry->crc32 != zip_entry_central_dir.crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent CRC32 values"); ret = ARCHIVE_WARN; } if (zip_entry->compressed_size == 0) { zip_entry->compressed_size = zip_entry_central_dir.compressed_size; } else if (zip_entry->compressed_size != zip_entry_central_dir.compressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent compressed size: " "%jd in central directory, %jd in local header", (intmax_t)zip_entry_central_dir.compressed_size, (intmax_t)zip_entry->compressed_size); ret = ARCHIVE_WARN; } if (zip_entry->uncompressed_size == 0) { zip_entry->uncompressed_size = zip_entry_central_dir.uncompressed_size; } else if (zip_entry->uncompressed_size != zip_entry_central_dir.uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent uncompressed size: " "%jd in central directory, %jd in local header", (intmax_t)zip_entry_central_dir.uncompressed_size, (intmax_t)zip_entry->uncompressed_size); ret = ARCHIVE_WARN; } } /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); archive_entry_set_uid(entry, zip_entry->uid); archive_entry_set_gid(entry, zip_entry->gid); archive_entry_set_mtime(entry, zip_entry->mtime, 0); archive_entry_set_ctime(entry, zip_entry->ctime, 0); archive_entry_set_atime(entry, zip_entry->atime, 0); if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { size_t linkname_length; if (zip_entry->compressed_size > 64 * 1024) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zip file with oversized link entry"); return ARCHIVE_FATAL; } linkname_length = (size_t)zip_entry->compressed_size; archive_entry_set_size(entry, 0); p = __archive_read_ahead(a, linkname_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated Zip file"); return ARCHIVE_FATAL; } sconv = zip->sconv; if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME)) sconv = zip->sconv_utf8; if (sconv == NULL) sconv = zip->sconv_default; if (archive_entry_copy_symlink_l(entry, p, linkname_length, sconv) != 0) { if (errno != ENOMEM && sconv == zip->sconv_utf8 && (zip->entry->zip_flags & ZIP_UTF8_NAME)) archive_entry_copy_symlink_l(entry, p, linkname_length, NULL); if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Symlink"); return (ARCHIVE_FATAL); } /* * Since there is no character-set regulation for * symlink name, do not report the conversion error * in an automatic conversion. */ if (sconv != zip->sconv_utf8 || (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symlink cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name( sconv)); ret = ARCHIVE_WARN; } } zip_entry->uncompressed_size = zip_entry->compressed_size = 0; if (__archive_read_consume(a, linkname_length) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Read error skipping symlink target name"); return ARCHIVE_FATAL; } } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) || zip_entry->uncompressed_size > 0) { /* Set the size only if it's meaningful. */ archive_entry_set_size(entry, zip_entry->uncompressed_size); } zip->entry_bytes_remaining = zip_entry->compressed_size; /* If there's no body, force read_data() to return EOF immediately. */ if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)", version / 10, version % 10, compression_name(zip->entry->compression)); a->archive.archive_format_name = zip->format_name.s; return (ret); } static int check_authentication_code(struct archive_read *a, const void *_p) { struct zip *zip = (struct zip *)(a->format->data); /* Check authentication code. */ if (zip->hctx_valid) { const void *p; uint8_t hmac[20]; size_t hmac_len = 20; int cmp; archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); if (_p == NULL) { /* Read authentication code. */ p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } } else { p = _p; } cmp = memcmp(hmac, p, AUTH_CODE_SIZE); __archive_read_consume(a, AUTH_CODE_SIZE); if (cmp != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad Authentication code"); return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } /* * Read "uncompressed" data. There are three cases: * 1) We know the size of the data. This is always true for the * seeking reader (we've examined the Central Directory already). * 2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred. * Info-ZIP seems to do this; we know the size but have to grab * the CRC from the data descriptor afterwards. * 3) We're streaming and ZIP_LENGTH_AT_END was specified and * we have no size information. In this case, we can do pretty * well by watching for the data descriptor record. The data * descriptor is 16 bytes and includes a computed CRC that should * provide a strong check. * * TODO: Technically, the PK\007\010 signature is optional. * In the original spec, the data descriptor contained CRC * and size fields but had no leading signature. In practice, * newer writers seem to provide the signature pretty consistently. * * For uncompressed data, the PK\007\010 marker seems essential * to be sure we've actually seen the end of the entry. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * zip->end_of_entry if it consumes all of the data. */ static int zip_read_data_none(struct archive_read *a, const void **_buff, size_t *size, int64_t *offset) { struct zip *zip; const char *buff; ssize_t bytes_avail; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { const char *p; ssize_t grabbing_bytes = 24; if (zip->hctx_valid) grabbing_bytes += AUTH_CODE_SIZE; /* Grab at least 24 bytes. */ buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail); if (bytes_avail < grabbing_bytes) { /* Zip archives have end-of-archive markers that are longer than this, so a failure to get at least 24 bytes really does indicate a truncated file. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } /* Check for a complete PK\007\010 signature, followed * by the correct 4-byte CRC. */ p = buff; if (zip->hctx_valid) p += AUTH_CODE_SIZE; if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' && (archive_le32dec(p + 4) == zip->entry_crc32 || zip->ignore_crc32 || (zip->hctx_valid && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { if (zip->entry->flags & LA_USED_ZIP64) { uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p + 4); compressed = archive_le64dec(p + 8); uncompressed = archive_le64dec(p + 16); if (compressed > INT64_MAX || uncompressed > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); return ARCHIVE_FAILED; } zip->entry->compressed_size = compressed; zip->entry->uncompressed_size = uncompressed; zip->unconsumed = 24; } else { zip->entry->crc32 = archive_le32dec(p + 4); zip->entry->compressed_size = archive_le32dec(p + 8); zip->entry->uncompressed_size = archive_le32dec(p + 12); zip->unconsumed = 16; } if (zip->hctx_valid) { r = check_authentication_code(a, buff); if (r != ARCHIVE_OK) return (r); } zip->end_of_entry = 1; return (ARCHIVE_OK); } /* If not at EOF, ensure we consume at least one byte. */ ++p; /* Scan forward until we see where a PK\007\010 signature * might be. */ /* Return bytes up until that point. On the next call, * the code above will verify the data descriptor. */ while (p < buff + bytes_avail - 4) { if (p[3] == 'P') { p += 3; } else if (p[3] == 'K') { p += 2; } else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->hctx_valid) p -= AUTH_CODE_SIZE; break; } else { p += 4; } } bytes_avail = p - buff; } else { if (zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; if (zip->hctx_valid) { r = check_authentication_code(a, NULL); if (r != ARCHIVE_OK) return (r); } return (ARCHIVE_OK); } /* Grab a bunch of bytes. */ buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = (ssize_t)zip->entry_bytes_remaining; } if (zip->tctx_valid || zip->cctx_valid) { size_t dec_size = bytes_avail; if (dec_size > zip->decrypted_buffer_size) dec_size = zip->decrypted_buffer_size; if (zip->tctx_valid) { trad_enc_decrypt_update(&zip->tctx, (const uint8_t *)buff, dec_size, zip->decrypted_buffer, dec_size); } else { size_t dsize = dec_size; archive_hmac_sha1_update(&zip->hctx, (const uint8_t *)buff, dec_size); archive_decrypto_aes_ctr_update(&zip->cctx, (const uint8_t *)buff, dec_size, zip->decrypted_buffer, &dsize); } bytes_avail = dec_size; buff = (const char *)zip->decrypted_buffer; } *size = bytes_avail; zip->entry_bytes_remaining -= bytes_avail; zip->entry_uncompressed_bytes_read += bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; zip->unconsumed += bytes_avail; *_buff = buff; return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int zip_deflate_init(struct archive_read *a, struct zip *zip) { int r; /* If we haven't yet read any data, initialize the decompressor. */ if (!zip->decompress_init) { if (zip->stream_valid) r = inflateReset(&zip->stream); else r = inflateInit2(&zip->stream, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize ZIP decompression."); return (ARCHIVE_FATAL); } /* Stream structure has been set up. */ zip->stream_valid = 1; /* We've initialized decompression for this stream. */ zip->decompress_init = 1; } return (ARCHIVE_OK); } static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip; ssize_t bytes_avail; const void *compressed_buff, *sp; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (unsigned char *)malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decompression"); return (ARCHIVE_FATAL); } } r = zip_deflate_init(a, zip); if (r != ARCHIVE_OK) return (r); /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail); if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && bytes_avail > zip->entry_bytes_remaining) { bytes_avail = (ssize_t)zip->entry_bytes_remaining; } if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); return (ARCHIVE_FATAL); } if (zip->tctx_valid || zip->cctx_valid) { if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { size_t buff_remaining = (zip->decrypted_buffer + zip->decrypted_buffer_size) - (zip->decrypted_ptr + zip->decrypted_bytes_remaining); if (buff_remaining > (size_t)bytes_avail) buff_remaining = (size_t)bytes_avail; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining > 0) { if ((int64_t)(zip->decrypted_bytes_remaining + buff_remaining) > zip->entry_bytes_remaining) { if (zip->entry_bytes_remaining < (int64_t)zip->decrypted_bytes_remaining) buff_remaining = 0; else buff_remaining = (size_t)zip->entry_bytes_remaining - zip->decrypted_bytes_remaining; } } if (buff_remaining > 0) { if (zip->tctx_valid) { trad_enc_decrypt_update(&zip->tctx, compressed_buff, buff_remaining, zip->decrypted_ptr + zip->decrypted_bytes_remaining, buff_remaining); } else { size_t dsize = buff_remaining; archive_decrypto_aes_ctr_update( &zip->cctx, compressed_buff, buff_remaining, zip->decrypted_ptr + zip->decrypted_bytes_remaining, &dsize); } zip->decrypted_bytes_remaining += buff_remaining; } } bytes_avail = zip->decrypted_bytes_remaining; compressed_buff = (const char *)zip->decrypted_ptr; } /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the * next_in pointer, only reads it). The result: this ugly * cast to remove 'const'. */ zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff; zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = zip->uncompressed_buffer; zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: zip->end_of_entry = 1; break; case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for ZIP decompression"); return (ARCHIVE_FATAL); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP decompression failed (%d)", r); return (ARCHIVE_FATAL); } /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; if (zip->tctx_valid || zip->cctx_valid) { zip->decrypted_bytes_remaining -= bytes_avail; if (zip->decrypted_bytes_remaining == 0) zip->decrypted_ptr = zip->decrypted_buffer; else zip->decrypted_ptr += bytes_avail; } /* Calculate compressed data as much as we used.*/ if (zip->hctx_valid) archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail); __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; *size = zip->stream.total_out; zip->entry_uncompressed_bytes_read += zip->stream.total_out; *buff = zip->uncompressed_buffer; if (zip->end_of_entry && zip->hctx_valid) { r = check_authentication_code(a, NULL); if (r != ARCHIVE_OK) return (r); } if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { const char *p; if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP end-of-file record"); return (ARCHIVE_FATAL); } /* Consume the optional PK\007\010 marker. */ if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010') { p += 4; zip->unconsumed = 4; } if (zip->entry->flags & LA_USED_ZIP64) { uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p); compressed = archive_le64dec(p + 4); uncompressed = archive_le64dec(p + 12); if (compressed > INT64_MAX || uncompressed > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); return ARCHIVE_FAILED; } zip->entry->compressed_size = compressed; zip->entry->uncompressed_size = uncompressed; zip->unconsumed += 20; } else { zip->entry->crc32 = archive_le32dec(p); zip->entry->compressed_size = archive_le32dec(p + 4); zip->entry->uncompressed_size = archive_le32dec(p + 8); zip->unconsumed += 12; } } return (ARCHIVE_OK); } #endif static int read_decryption_header(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const char *p; unsigned int remaining_size; unsigned int ts; /* * Read an initialization vector data field. */ p = __archive_read_ahead(a, 2, NULL); if (p == NULL) goto truncated; ts = zip->iv_size; zip->iv_size = archive_le16dec(p); __archive_read_consume(a, 2); if (ts < zip->iv_size) { free(zip->iv); zip->iv = NULL; } p = __archive_read_ahead(a, zip->iv_size, NULL); if (p == NULL) goto truncated; if (zip->iv == NULL) { zip->iv = malloc(zip->iv_size); if (zip->iv == NULL) goto nomem; } memcpy(zip->iv, p, zip->iv_size); __archive_read_consume(a, zip->iv_size); /* * Read a size of remaining decryption header field. */ p = __archive_read_ahead(a, 14, NULL); if (p == NULL) goto truncated; remaining_size = archive_le32dec(p); if (remaining_size < 16 || remaining_size > (1 << 18)) goto corrupted; /* Check if format version is supported. */ if (archive_le16dec(p+4) != 3) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported encryption format version: %u", archive_le16dec(p+4)); return (ARCHIVE_FAILED); } /* * Read an encryption algorithm field. */ zip->alg_id = archive_le16dec(p+6); switch (zip->alg_id) { case 0x6601:/* DES */ case 0x6602:/* RC2 */ case 0x6603:/* 3DES 168 */ case 0x6609:/* 3DES 112 */ case 0x660E:/* AES 128 */ case 0x660F:/* AES 192 */ case 0x6610:/* AES 256 */ case 0x6702:/* RC2 (version >= 5.2) */ case 0x6720:/* Blowfish */ case 0x6721:/* Twofish */ case 0x6801:/* RC4 */ /* Supported encryption algorithm. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption algorithm: %u", zip->alg_id); return (ARCHIVE_FAILED); } /* * Read a bit length field. */ zip->bit_len = archive_le16dec(p+8); /* * Read a flags field. */ zip->flags = archive_le16dec(p+10); switch (zip->flags & 0xf000) { case 0x0001: /* Password is required to decrypt. */ case 0x0002: /* Certificates only. */ case 0x0003: /* Password or certificate required to decrypt. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption flag: %u", zip->flags); return (ARCHIVE_FAILED); } if ((zip->flags & 0xf000) == 0 || (zip->flags & 0xf000) == 0x4000) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption flag: %u", zip->flags); return (ARCHIVE_FAILED); } /* * Read an encrypted random data field. */ ts = zip->erd_size; zip->erd_size = archive_le16dec(p+12); __archive_read_consume(a, 14); if ((zip->erd_size & 0xf) != 0 || (zip->erd_size + 16) > remaining_size || (zip->erd_size + 16) < zip->erd_size) goto corrupted; if (ts < zip->erd_size) { free(zip->erd); zip->erd = NULL; } p = __archive_read_ahead(a, zip->erd_size, NULL); if (p == NULL) goto truncated; if (zip->erd == NULL) { zip->erd = malloc(zip->erd_size); if (zip->erd == NULL) goto nomem; } memcpy(zip->erd, p, zip->erd_size); __archive_read_consume(a, zip->erd_size); /* * Read a reserved data field. */ p = __archive_read_ahead(a, 4, NULL); if (p == NULL) goto truncated; /* Reserved data size should be zero. */ if (archive_le32dec(p) != 0) goto corrupted; __archive_read_consume(a, 4); /* * Read a password validation data field. */ p = __archive_read_ahead(a, 2, NULL); if (p == NULL) goto truncated; ts = zip->v_size; zip->v_size = archive_le16dec(p); __archive_read_consume(a, 2); if ((zip->v_size & 0x0f) != 0 || (zip->erd_size + zip->v_size + 16) > remaining_size || (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size)) goto corrupted; if (ts < zip->v_size) { free(zip->v_data); zip->v_data = NULL; } p = __archive_read_ahead(a, zip->v_size, NULL); if (p == NULL) goto truncated; if (zip->v_data == NULL) { zip->v_data = malloc(zip->v_size); if (zip->v_data == NULL) goto nomem; } memcpy(zip->v_data, p, zip->v_size); __archive_read_consume(a, zip->v_size); p = __archive_read_ahead(a, 4, NULL); if (p == NULL) goto truncated; zip->v_crc32 = archive_le32dec(p); __archive_read_consume(a, 4); /*return (ARCHIVE_OK); * This is not fully implemented yet.*/ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encrypted file is unsupported"); return (ARCHIVE_FAILED); truncated: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); corrupted: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted ZIP file data"); return (ARCHIVE_FATAL); nomem: archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decryption"); return (ARCHIVE_FATAL); } static int zip_alloc_decryption_buffer(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); size_t bs = 256 * 1024; if (zip->decrypted_buffer == NULL) { zip->decrypted_buffer_size = bs; zip->decrypted_buffer = malloc(bs); if (zip->decrypted_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decryption"); return (ARCHIVE_FATAL); } } zip->decrypted_ptr = zip->decrypted_buffer; return (ARCHIVE_OK); } static int init_traditional_PKWARE_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const void *p; int retry; int r; if (zip->tctx_valid) return (ARCHIVE_OK); /* Read the 12 bytes encryption header stored at the start of the data area. */ #define ENC_HEADER_SIZE 12 if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < ENC_HEADER_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated Zip encrypted body: only %jd bytes available", (intmax_t)zip->entry_bytes_remaining); return (ARCHIVE_FATAL); } p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } for (retry = 0;; retry++) { const char *passphrase; uint8_t crcchk; passphrase = __archive_read_next_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, (retry > 0)? "Incorrect passphrase": "Passphrase required for this entry"); return (ARCHIVE_FAILED); } /* * Initialize ctx for Traditional PKWARE Decryption. */ r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), p, ENC_HEADER_SIZE, &crcchk); if (r == 0 && crcchk == zip->entry->decdat) break;/* The passphrase is OK. */ if (retry > 10000) { /* Avoid infinity loop. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many incorrect passphrases"); return (ARCHIVE_FAILED); } } __archive_read_consume(a, ENC_HEADER_SIZE); zip->tctx_valid = 1; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { zip->entry_bytes_remaining -= ENC_HEADER_SIZE; } /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/ zip->entry_compressed_bytes_read += ENC_HEADER_SIZE; zip->decrypted_bytes_remaining = 0; return (zip_alloc_decryption_buffer(a)); #undef ENC_HEADER_SIZE } static int init_WinZip_AES_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const void *p; const uint8_t *pv; size_t key_len, salt_len; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; int retry; int r; if (zip->cctx_valid || zip->hctx_valid) return (ARCHIVE_OK); switch (zip->entry->aes_extra.strength) { case 1: salt_len = 8; key_len = 16; break; case 2: salt_len = 12; key_len = 24; break; case 3: salt_len = 16; key_len = 32; break; default: goto corrupted; } p = __archive_read_ahead(a, salt_len + 2, NULL); if (p == NULL) goto truncated; for (retry = 0;; retry++) { const char *passphrase; passphrase = __archive_read_next_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, (retry > 0)? "Incorrect passphrase": "Passphrase required for this entry"); return (ARCHIVE_FAILED); } memset(derived_key, 0, sizeof(derived_key)); r = archive_pbkdf2_sha1(passphrase, strlen(passphrase), p, salt_len, 1000, derived_key, key_len * 2 + 2); if (r != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of " "crypto library"); return (ARCHIVE_FAILED); } /* Check password verification value. */ pv = ((const uint8_t *)p) + salt_len; if (derived_key[key_len * 2] == pv[0] && derived_key[key_len * 2 + 1] == pv[1]) break;/* The passphrase is OK. */ if (retry > 10000) { /* Avoid infinity loop. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many incorrect passphrases"); return (ARCHIVE_FAILED); } } r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); if (r != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of crypto library"); return (ARCHIVE_FAILED); } r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); if (r != 0) { archive_decrypto_aes_ctr_release(&zip->cctx); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize HMAC-SHA1"); return (ARCHIVE_FAILED); } zip->cctx_valid = zip->hctx_valid = 1; __archive_read_consume(a, salt_len + 2); zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 0) goto corrupted; zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE; zip->decrypted_bytes_remaining = 0; zip->entry->compression = zip->entry->aes_extra.compression; return (zip_alloc_decryption_buffer(a)); truncated: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); corrupted: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted ZIP file data"); return (ARCHIVE_FATAL); } static int archive_read_format_zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int r; struct zip *zip = (struct zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } *offset = zip->entry_uncompressed_bytes_read; *size = 0; *buff = NULL; /* If we hit end-of-entry last time, return ARCHIVE_EOF. */ if (zip->end_of_entry) return (ARCHIVE_EOF); /* Return EOF immediately if this is a non-regular file. */ if (AE_IFREG != (zip->entry->mode & AE_IFMT)) return (ARCHIVE_EOF); __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; if (zip->init_decryption) { zip->has_encrypted_entries = 1; if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) r = read_decryption_header(a); else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) r = init_WinZip_AES_decryption(a); else r = init_traditional_PKWARE_decryption(a); if (r != ARCHIVE_OK) return (r); zip->init_decryption = 0; } switch(zip->entry->compression) { case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); break; #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ r = zip_read_data_deflate(a, buff, size, offset); break; #endif default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported ZIP compression method (%s)", compression_name(zip->entry->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_FAILED); break; } if (r != ARCHIVE_OK) return (r); /* Update checksum */ if (*size) zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff, (unsigned)*size); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { /* Check file size, CRC against these values. */ if (zip->entry->compressed_size != zip->entry_compressed_bytes_read) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP compressed data is wrong size " "(read %jd, expected %jd)", (intmax_t)zip->entry_compressed_bytes_read, (intmax_t)zip->entry->compressed_size); return (ARCHIVE_WARN); } /* Size field only stores the lower 32 bits of the actual * size. */ if ((zip->entry->uncompressed_size & UINT32_MAX) != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP uncompressed data is wrong size " "(read %jd, expected %jd)\n", (intmax_t)zip->entry_uncompressed_bytes_read, (intmax_t)zip->entry->uncompressed_size); return (ARCHIVE_WARN); } /* Check computed CRC against header */ if ((!zip->hctx_valid || zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && zip->entry->crc32 != zip->entry_crc32 && !zip->ignore_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad CRC: 0x%lx should be 0x%lx", (unsigned long)zip->entry_crc32, (unsigned long)zip->entry->crc32); return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } static int archive_read_format_zip_cleanup(struct archive_read *a) { struct zip *zip; struct zip_entry *zip_entry, *next_zip_entry; zip = (struct zip *)(a->format->data); #ifdef HAVE_ZLIB_H if (zip->stream_valid) inflateEnd(&zip->stream); free(zip->uncompressed_buffer); #endif if (zip->zip_entries) { zip_entry = zip->zip_entries; while (zip_entry != NULL) { next_zip_entry = zip_entry->next; archive_string_free(&zip_entry->rsrcname); free(zip_entry); zip_entry = next_zip_entry; } } free(zip->decrypted_buffer); if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); free(zip->iv); free(zip->erd); free(zip->v_data); archive_string_free(&zip->format_name); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } static int archive_read_format_zip_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct zip * zip = (struct zip *)_a->format->data; if (zip) { return zip->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_zip_options(struct archive_read *a, const char *key, const char *val) { struct zip *zip; int ret = ARCHIVE_FAILED; zip = (struct zip *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filenames as libarchive 2.x */ zip->init_default_conversion = (val != NULL) ? 1 : 0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zip: hdrcharset option needs a character-set name" ); else { zip->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (zip->sconv != NULL) { if (strcmp(val, "UTF-8") == 0) zip->sconv_utf8 = zip->sconv; ret = ARCHIVE_OK; } else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "ignorecrc32") == 0) { /* Mostly useful for testing. */ if (val == NULL || val[0] == 0) { zip->crc32func = real_crc32; zip->ignore_crc32 = 0; } else { zip->crc32func = fake_crc32; zip->ignore_crc32 = 1; } return (ARCHIVE_OK); } else if (strcmp(key, "mac-ext") == 0) { zip->process_mac_extensions = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } int archive_read_support_format_zip(struct archive *a) { int r; r = archive_read_support_format_zip_streamable(a); if (r != ARCHIVE_OK) return r; return (archive_read_support_format_zip_seekable(a)); } /* ------------------------------------------------------------------------ */ /* * Streaming-mode support */ static int archive_read_support_format_zip_capabilities_streamable(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) { const char *p; (void)best_bid; /* UNUSED */ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* * Bid of 29 here comes from: * + 16 bits for "PK", * + next 16-bit field has 6 options so contributes * about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits * * So we've effectively verified ~29 total bits of check data. */ if (p[0] == 'P' && p[1] == 'K') { if ((p[2] == '\001' && p[3] == '\002') || (p[2] == '\003' && p[3] == '\004') || (p[2] == '\005' && p[3] == '\006') || (p[2] == '\006' && p[3] == '\006') || (p[2] == '\007' && p[3] == '\010') || (p[2] == '0' && p[3] == '0')) return (29); } /* TODO: It's worth looking ahead a little bit for a valid * PK signature. In particular, that would make it possible * to read some UUEncoded SFX files or SFX files coming from * a network socket. */ return (0); } static int archive_read_format_zip_streamable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "ZIP"; zip = (struct zip *)(a->format->data); /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; /* Make sure we have a zip_entry structure to use. */ if (zip->zip_entries == NULL) { zip->zip_entries = malloc(sizeof(struct zip_entry)); if (zip->zip_entries == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return ARCHIVE_FATAL; } } zip->entry = zip->zip_entries; memset(zip->entry, 0, sizeof(struct zip_entry)); if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; __archive_read_reset_passphrase(a); /* Search ahead for the next local file header. */ __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; for (;;) { int64_t skipped = 0; const char *p, *end; ssize_t bytes; p = __archive_read_ahead(a, 4, &bytes); if (p == NULL) return (ARCHIVE_FATAL); end = p + bytes; while (p + 4 <= end) { if (p[0] == 'P' && p[1] == 'K') { if (p[2] == '\003' && p[3] == '\004') { /* Regular file entry. */ __archive_read_consume(a, skipped); return zip_read_local_file_header(a, entry, zip); } /* * TODO: We cannot restore permissions * based only on the local file headers. * Consider scanning the central * directory and returning additional * entries for at least directories. * This would allow us to properly set * directory permissions. * * This won't help us fix symlinks * and may not help with regular file * permissions, either. */ if (p[2] == '\001' && p[3] == '\002') { return (ARCHIVE_EOF); } /* End of central directory? Must be an * empty archive. */ if ((p[2] == '\005' && p[3] == '\006') || (p[2] == '\006' && p[3] == '\006')) return (ARCHIVE_EOF); } ++p; ++skipped; } __archive_read_consume(a, skipped); } } static int archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) { struct zip *zip; int64_t bytes_skipped; zip = (struct zip *)(a->format->data); bytes_skipped = __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* So we know we're streaming... */ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) || zip->entry->compressed_size > 0) { /* We know the compressed length, so we can just skip. */ bytes_skipped = __archive_read_consume(a, zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } if (zip->init_decryption) { int r; zip->has_encrypted_entries = 1; if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) r = read_decryption_header(a); else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) r = init_WinZip_AES_decryption(a); else r = init_traditional_PKWARE_decryption(a); if (r != ARCHIVE_OK) return (r); zip->init_decryption = 0; } /* We're streaming and we don't know the length. */ /* If the body is compressed and we know the format, we can * find an exact end-of-entry by decompressing it. */ switch (zip->entry->compression) { #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ while (!zip->end_of_entry) { int64_t offset = 0; const void *buff = NULL; size_t size = 0; int r; r = zip_read_data_deflate(a, &buff, &size, &offset); if (r != ARCHIVE_OK) return (r); } return ARCHIVE_OK; #endif default: /* Uncompressed or unknown. */ /* Scan for a PK\007\010 signature. */ for (;;) { const char *p, *buff; ssize_t bytes_avail; buff = __archive_read_ahead(a, 16, &bytes_avail); if (bytes_avail < 16) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } p = buff; while (p <= buff + bytes_avail - 16) { if (p[3] == 'P') { p += 3; } else if (p[3] == 'K') { p += 2; } else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->entry->flags & LA_USED_ZIP64) __archive_read_consume(a, p - buff + 24); else __archive_read_consume(a, p - buff + 16); return ARCHIVE_OK; } else { p += 4; } } __archive_read_consume(a, p - buff); } } } int archive_read_support_format_zip_streamable(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip"); zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } /* Streamable reader doesn't support mac extensions. */ zip->process_mac_extensions = 0; /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; zip->crc32func = real_crc32; r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_streamable_bid, archive_read_format_zip_options, archive_read_format_zip_streamable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip_streamable, NULL, archive_read_format_zip_cleanup, archive_read_support_format_zip_capabilities_streamable, archive_read_format_zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } /* ------------------------------------------------------------------------ */ /* * Seeking-mode support */ static int archive_read_support_format_zip_capabilities_seekable(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } /* * TODO: This is a performance sink because it forces the read core to * drop buffered data from the start of file, which will then have to * be re-read again if this bidder loses. * * We workaround this a little by passing in the best bid so far so * that later bidders can do nothing if they know they'll never * outbid. But we can certainly do better... */ static int read_eocd(struct zip *zip, const char *p, int64_t current_offset) { /* Sanity-check the EOCD we've found. */ /* This must be the first volume. */ if (archive_le16dec(p + 4) != 0) return 0; /* Central directory must be on this volume. */ if (archive_le16dec(p + 4) != archive_le16dec(p + 6)) return 0; /* All central directory entries must be on this volume. */ if (archive_le16dec(p + 10) != archive_le16dec(p + 8)) return 0; /* Central directory can't extend beyond start of EOCD record. */ if (archive_le32dec(p + 16) + archive_le32dec(p + 12) > current_offset) return 0; /* Save the central directory location for later use. */ zip->central_directory_offset = archive_le32dec(p + 16); /* This is just a tiny bit higher than the maximum returned by the streaming Zip bidder. This ensures that the more accurate seeking Zip parser wins whenever seek is available. */ return 32; } /* * Examine Zip64 EOCD locator: If it's valid, store the information * from it. */ -static void +static int read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) { int64_t eocd64_offset; int64_t eocd64_size; /* Sanity-check the locator record. */ /* Central dir must be on first volume. */ if (archive_le32dec(p + 4) != 0) - return; + return 0; /* Must be only a single volume. */ if (archive_le32dec(p + 16) != 1) - return; + return 0; /* Find the Zip64 EOCD record. */ eocd64_offset = archive_le64dec(p + 8); if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0) - return; + return 0; if ((p = __archive_read_ahead(a, 56, NULL)) == NULL) - return; + return 0; /* Make sure we can read all of it. */ eocd64_size = archive_le64dec(p + 4) + 12; if (eocd64_size < 56 || eocd64_size > 16384) - return; + return 0; if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL) - return; + return 0; /* Sanity-check the EOCD64 */ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */ - return; + return 0; if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */ - return; + return 0; /* CD can't be split. */ if (archive_le64dec(p + 24) != archive_le64dec(p + 32)) - return; + return 0; /* Save the central directory offset for later use. */ zip->central_directory_offset = archive_le64dec(p + 48); + + return 32; } static int archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) { struct zip *zip = (struct zip *)a->format->data; int64_t file_size, current_offset; const char *p; int i, tail; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); file_size = __archive_read_seek(a, 0, SEEK_END); if (file_size <= 0) return 0; /* Search last 16k of file for end-of-central-directory * record (which starts with PK\005\006) */ tail = (int)zipmin(1024 * 16, file_size); current_offset = __archive_read_seek(a, -tail, SEEK_END); if (current_offset < 0) return 0; if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) return 0; /* Boyer-Moore search backwards from the end, since we want * to match the last EOCD in the file (there can be more than * one if there is an uncompressed Zip archive as a member * within this Zip archive). */ for (i = tail - 22; i > 0;) { switch (p[i]) { case 'P': if (memcmp(p + i, "PK\005\006", 4) == 0) { int ret = read_eocd(zip, p + i, current_offset + i); - if (ret > 0) { - /* Zip64 EOCD locator precedes - * regular EOCD if present. */ - if (i >= 20 - && memcmp(p + i - 20, "PK\006\007", 4) == 0) { - read_zip64_eocd(a, zip, p + i - 20); - } - return (ret); + /* Zip64 EOCD locator precedes + * regular EOCD if present. */ + if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) { + int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20); + if (ret_zip64 > ret) + ret = ret_zip64; } + return (ret); } i -= 4; break; case 'K': i -= 1; break; case 005: i -= 2; break; case 006: i -= 3; break; default: i -= 4; break; } } return 0; } /* The red-black trees are only used in seeking mode to manage * the in-memory copy of the central directory. */ static int cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct zip_entry *e1 = (const struct zip_entry *)n1; const struct zip_entry *e2 = (const struct zip_entry *)n2; if (e1->local_header_offset > e2->local_header_offset) return -1; if (e1->local_header_offset < e2->local_header_offset) return 1; return 0; } static int cmp_key(const struct archive_rb_node *n, const void *key) { /* This function won't be called */ (void)n; /* UNUSED */ (void)key; /* UNUSED */ return 1; } static const struct archive_rb_tree_ops rb_ops = { &cmp_node, &cmp_key }; static int rsrc_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct zip_entry *e1 = (const struct zip_entry *)n1; const struct zip_entry *e2 = (const struct zip_entry *)n2; return (strcmp(e2->rsrcname.s, e1->rsrcname.s)); } static int rsrc_cmp_key(const struct archive_rb_node *n, const void *key) { const struct zip_entry *e = (const struct zip_entry *)n; return (strcmp((const char *)key, e->rsrcname.s)); } static const struct archive_rb_tree_ops rb_rsrc_ops = { &rsrc_cmp_node, &rsrc_cmp_key }; static const char * rsrc_basename(const char *name, size_t name_length) { const char *s, *r; r = s = name; for (;;) { s = memchr(s, '/', name_length - (s - name)); if (s == NULL) break; r = ++s; } return (r); } static void expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) { struct archive_string str; struct zip_entry *dir; char *s; archive_string_init(&str); archive_strncpy(&str, name, name_length); for (;;) { s = strrchr(str.s, '/'); if (s == NULL) break; *s = '\0'; /* Transfer the parent directory from zip->tree_rsrc RB * tree to zip->tree RB tree to expose. */ dir = (struct zip_entry *) __archive_rb_tree_find_node(&zip->tree_rsrc, str.s); if (dir == NULL) break; __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node); archive_string_free(&dir->rsrcname); __archive_rb_tree_insert_node(&zip->tree, &dir->node); } archive_string_free(&str); } static int slurp_central_directory(struct archive_read *a, struct zip *zip) { ssize_t i; unsigned found; int64_t correction; ssize_t bytes_avail; const char *p; /* * Find the start of the central directory. The end-of-CD * record has our starting point, but there are lots of * Zip archives which have had other data prepended to the * file, which makes the recorded offsets all too small. * So we search forward from the specified offset until we * find the real start of the central directory. Then we * know the correction we need to apply to account for leading * padding. */ if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0) return ARCHIVE_FATAL; found = 0; while (!found) { if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL) return ARCHIVE_FATAL; for (found = 0, i = 0; !found && i < bytes_avail - 4;) { switch (p[i + 3]) { case 'P': i += 3; break; case 'K': i += 2; break; case 001: i += 1; break; case 002: if (memcmp(p + i, "PK\001\002", 4) == 0) { p += i; found = 1; } else i += 4; break; case 005: i += 1; break; case 006: if (memcmp(p + i, "PK\005\006", 4) == 0) { p += i; found = 1; } else if (memcmp(p + i, "PK\006\006", 4) == 0) { p += i; found = 1; } else i += 1; break; default: i += 4; break; } } __archive_read_consume(a, i); } correction = archive_filter_bytes(&a->archive, 0) - zip->central_directory_offset; __archive_rb_tree_init(&zip->tree, &rb_ops); __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); zip->central_directory_entries_total = 0; while (1) { struct zip_entry *zip_entry; size_t filename_length, extra_length, comment_length; uint32_t external_attributes; const char *name, *r; if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return ARCHIVE_FATAL; if (memcmp(p, "PK\006\006", 4) == 0 || memcmp(p, "PK\005\006", 4) == 0) { break; } else if (memcmp(p, "PK\001\002", 4) != 0) { archive_set_error(&a->archive, -1, "Invalid central directory signature"); return ARCHIVE_FATAL; } if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) return ARCHIVE_FATAL; zip_entry = calloc(1, sizeof(struct zip_entry)); zip_entry->next = zip->zip_entries; zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY; zip->zip_entries = zip_entry; zip->central_directory_entries_total++; /* version = p[4]; */ zip_entry->system = p[5]; /* version_required = archive_le16dec(p + 6); */ zip_entry->zip_flags = archive_le16dec(p + 8); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){ zip->has_encrypted_entries = 1; } zip_entry->compression = (char)archive_le16dec(p + 10); zip_entry->mtime = zip_time(p + 12); zip_entry->crc32 = archive_le32dec(p + 16); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[13]; else zip_entry->decdat = p[19]; zip_entry->compressed_size = archive_le32dec(p + 20); zip_entry->uncompressed_size = archive_le32dec(p + 24); filename_length = archive_le16dec(p + 28); extra_length = archive_le16dec(p + 30); comment_length = archive_le16dec(p + 32); /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ external_attributes = archive_le32dec(p + 38); zip_entry->local_header_offset = archive_le32dec(p + 42) + correction; /* If we can't guess the mode, leave it zero here; when we read the local file header we might get more information. */ if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit if (0x10 == (external_attributes & 0x10)) { zip_entry->mode = AE_IFDIR | 0775; } else { zip_entry->mode = AE_IFREG | 0664; } if (0x01 == (external_attributes & 0x01)) { // Read-only bit; strip write permissions zip_entry->mode &= 0555; } } else { zip_entry->mode = 0; } /* We're done with the regular data; get the filename and * extra data. */ __archive_read_consume(a, 46); p = __archive_read_ahead(a, filename_length + extra_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return ARCHIVE_FATAL; } if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) { return ARCHIVE_FATAL; } /* * Mac resource fork files are stored under the * "__MACOSX/" directory, so we should check if * it is. */ if (!zip->process_mac_extensions) { /* Treat every entry as a regular entry. */ __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); } else { name = p; r = rsrc_basename(name, filename_length); if (filename_length >= 9 && strncmp("__MACOSX/", name, 9) == 0) { /* If this file is not a resource fork nor * a directory. We should treat it as a non * resource fork file to expose it. */ if (name[filename_length-1] != '/' && (r - name < 3 || r[0] != '.' || r[1] != '_')) { __archive_rb_tree_insert_node( &zip->tree, &zip_entry->node); /* Expose its parent directories. */ expose_parent_dirs(zip, name, filename_length); } else { /* This file is a resource fork file or * a directory. */ archive_strncpy(&(zip_entry->rsrcname), name, filename_length); __archive_rb_tree_insert_node( &zip->tree_rsrc, &zip_entry->node); } } else { /* Generate resource fork name to find its * resource file at zip->tree_rsrc. */ archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); archive_strncat(&(zip_entry->rsrcname), name, r - name); archive_strcat(&(zip_entry->rsrcname), "._"); archive_strncat(&(zip_entry->rsrcname), name + (r - name), filename_length - (r - name)); /* Register an entry to RB tree to sort it by * file offset. */ __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); } } /* Skip the comment too ... */ __archive_read_consume(a, filename_length + extra_length + comment_length); } return ARCHIVE_OK; } static ssize_t zip_get_local_file_header_size(struct archive_read *a, size_t extra) { const char *p; ssize_t filename_length, extra_length; if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_WARN); } p += extra; if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_WARN; } filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); return (30 + filename_length + extra_length); } static int zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, struct zip_entry *rsrc) { struct zip *zip = (struct zip *)a->format->data; unsigned char *metadata, *mp; int64_t offset = archive_filter_bytes(&a->archive, 0); size_t remaining_bytes, metadata_bytes; ssize_t hsize; int ret = ARCHIVE_OK, eof; switch(rsrc->compression) { case 0: /* No compression. */ if (rsrc->uncompressed_size != rsrc->compressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed OS X metadata entry: inconsistent size"); return (ARCHIVE_FATAL); } #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ #endif break; default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported ZIP compression method (%s)", compression_name(rsrc->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_WARN); } if (rsrc->uncompressed_size > (4 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mac metadata is too large: %jd > 4M bytes", (intmax_t)rsrc->uncompressed_size); return (ARCHIVE_WARN); } if (rsrc->compressed_size > (4 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mac metadata is too large: %jd > 4M bytes", (intmax_t)rsrc->compressed_size); return (ARCHIVE_WARN); } metadata = malloc((size_t)rsrc->uncompressed_size); if (metadata == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Mac metadata"); return (ARCHIVE_FATAL); } if (offset < rsrc->local_header_offset) __archive_read_consume(a, rsrc->local_header_offset - offset); else if (offset != rsrc->local_header_offset) { __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); } hsize = zip_get_local_file_header_size(a, 0); __archive_read_consume(a, hsize); remaining_bytes = (size_t)rsrc->compressed_size; metadata_bytes = (size_t)rsrc->uncompressed_size; mp = metadata; eof = 0; while (!eof && remaining_bytes) { const unsigned char *p; ssize_t bytes_avail; size_t bytes_used; p = __archive_read_ahead(a, 1, &bytes_avail); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); ret = ARCHIVE_WARN; goto exit_mac_metadata; } if ((size_t)bytes_avail > remaining_bytes) bytes_avail = remaining_bytes; switch(rsrc->compression) { case 0: /* No compression. */ if ((size_t)bytes_avail > metadata_bytes) bytes_avail = metadata_bytes; memcpy(mp, p, bytes_avail); bytes_used = (size_t)bytes_avail; metadata_bytes -= bytes_used; mp += bytes_used; if (metadata_bytes == 0) eof = 1; break; #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ { int r; ret = zip_deflate_init(a, zip); if (ret != ARCHIVE_OK) goto exit_mac_metadata; zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = mp; zip->stream.avail_out = (uInt)metadata_bytes; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: eof = 1; break; case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for ZIP decompression"); ret = ARCHIVE_FATAL; goto exit_mac_metadata; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP decompression failed (%d)", r); ret = ARCHIVE_FATAL; goto exit_mac_metadata; } bytes_used = zip->stream.total_in; metadata_bytes -= zip->stream.total_out; mp += zip->stream.total_out; break; } #endif default: bytes_used = 0; break; } __archive_read_consume(a, bytes_used); remaining_bytes -= bytes_used; } archive_entry_copy_mac_metadata(entry, metadata, (size_t)rsrc->uncompressed_size - metadata_bytes); exit_mac_metadata: __archive_read_seek(a, offset, SEEK_SET); zip->decompress_init = 0; free(metadata); return (ret); } static int archive_read_format_zip_seekable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip = (struct zip *)a->format->data; struct zip_entry *rsrc; int64_t offset; int r, ret = ARCHIVE_OK; /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "ZIP"; if (zip->zip_entries == NULL) { r = slurp_central_directory(a, zip); if (r != ARCHIVE_OK) return r; /* Get first entry whose local header offset is lower than * other entries in the archive file. */ zip->entry = (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree); } else if (zip->entry != NULL) { /* Get next entry in local header offset order. */ zip->entry = (struct zip_entry *)__archive_rb_tree_iterate( &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT); } if (zip->entry == NULL) return ARCHIVE_EOF; if (zip->entry->rsrcname.s) rsrc = (struct zip_entry *)__archive_rb_tree_find_node( &zip->tree_rsrc, zip->entry->rsrcname.s); else rsrc = NULL; if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; __archive_read_reset_passphrase(a); /* File entries are sorted by the header offset, we should mostly * use __archive_read_consume to advance a read point to avoid redundant * data reading. */ offset = archive_filter_bytes(&a->archive, 0); if (offset < zip->entry->local_header_offset) __archive_read_consume(a, zip->entry->local_header_offset - offset); else if (offset != zip->entry->local_header_offset) { __archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET); } zip->unconsumed = 0; r = zip_read_local_file_header(a, entry, zip); if (r != ARCHIVE_OK) return r; if (rsrc) { int ret2 = zip_read_mac_metadata(a, entry, rsrc); if (ret2 < ret) ret = ret2; } return (ret); } /* * We're going to seek for the next header anyway, so we don't * need to bother doing anything here. */ static int archive_read_format_zip_read_data_skip_seekable(struct archive_read *a) { struct zip *zip; zip = (struct zip *)(a->format->data); zip->unconsumed = 0; return (ARCHIVE_OK); } int archive_read_support_format_zip_seekable(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable"); zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } #ifdef HAVE_COPYFILE_H /* Set this by default on Mac OS. */ zip->process_mac_extensions = 1; #endif /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; zip->crc32func = real_crc32; r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_seekable_bid, archive_read_format_zip_options, archive_read_format_zip_seekable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip_seekable, NULL, archive_read_format_zip_cleanup, archive_read_support_format_zip_capabilities_seekable, archive_read_format_zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } Index: stable/10/contrib/libarchive/libarchive/archive_string_sprintf.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_string_sprintf.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_string_sprintf.c (revision 316338) @@ -1,192 +1,192 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* * The use of printf()-family functions can be troublesome * for space-constrained applications. In addition, correctly * implementing this function in terms of vsnprintf() requires * two calls (one to determine the size, another to format the * result), which in turn requires duplicating the argument list * using va_copy, which isn't yet universally available. * * So, I've implemented a bare minimum of printf()-like capability * here. This is only used to format error messages, so doesn't * require any floating-point support or field-width handling. */ #ifdef HAVE_ERRNO_H #include #endif #include #include "archive_string.h" #include "archive_private.h" /* * Utility functions to format signed/unsigned integers and append * them to an archive_string. */ static void append_uint(struct archive_string *as, uintmax_t d, unsigned base) { - static const char *digits = "0123456789abcdef"; + static const char digits[] = "0123456789abcdef"; if (d >= base) append_uint(as, d/base, base); archive_strappend_char(as, digits[d % base]); } static void append_int(struct archive_string *as, intmax_t d, unsigned base) { uintmax_t ud; if (d < 0) { archive_strappend_char(as, '-'); ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d); } else ud = d; append_uint(as, ud, base); } void archive_string_sprintf(struct archive_string *as, const char *fmt, ...) { va_list ap; va_start(ap, fmt); archive_string_vsprintf(as, fmt, ap); va_end(ap); } /* * Like 'vsprintf', but ensures the target is big enough, resizing if * necessary. */ void archive_string_vsprintf(struct archive_string *as, const char *fmt, va_list ap) { char long_flag; intmax_t s; /* Signed integer temp. */ uintmax_t u; /* Unsigned integer temp. */ const char *p, *p2; const wchar_t *pw; if (archive_string_ensure(as, 64) == NULL) __archive_errx(1, "Out of memory"); if (fmt == NULL) { as->s[0] = 0; return; } for (p = fmt; *p != '\0'; p++) { const char *saved_p = p; if (*p != '%') { archive_strappend_char(as, *p); continue; } p++; long_flag = '\0'; switch(*p) { case 'j': case 'l': case 'z': long_flag = *p; p++; break; } switch (*p) { case '%': archive_strappend_char(as, '%'); break; case 'c': s = va_arg(ap, int); archive_strappend_char(as, (char)s); break; case 'd': switch(long_flag) { case 'j': s = va_arg(ap, intmax_t); break; case 'l': s = va_arg(ap, long); break; case 'z': s = va_arg(ap, ssize_t); break; default: s = va_arg(ap, int); break; } append_int(as, s, 10); break; case 's': switch(long_flag) { case 'l': pw = va_arg(ap, wchar_t *); if (pw == NULL) pw = L"(null)"; if (archive_string_append_from_wcs(as, pw, wcslen(pw)) != 0 && errno == ENOMEM) __archive_errx(1, "Out of memory"); break; default: p2 = va_arg(ap, char *); if (p2 == NULL) p2 = "(null)"; archive_strcat(as, p2); break; } break; case 'S': pw = va_arg(ap, wchar_t *); if (pw == NULL) pw = L"(null)"; if (archive_string_append_from_wcs(as, pw, wcslen(pw)) != 0 && errno == ENOMEM) __archive_errx(1, "Out of memory"); break; case 'o': case 'u': case 'x': case 'X': /* Common handling for unsigned integer formats. */ switch(long_flag) { case 'j': u = va_arg(ap, uintmax_t); break; case 'l': u = va_arg(ap, unsigned long); break; case 'z': u = va_arg(ap, size_t); break; default: u = va_arg(ap, unsigned int); break; } /* Format it in the correct base. */ switch (*p) { case 'o': append_uint(as, u, 8); break; case 'u': append_uint(as, u, 10); break; default: append_uint(as, u, 16); break; } break; default: /* Rewind and print the initial '%' literally. */ p = saved_p; archive_strappend_char(as, *p); } } } Index: stable/10/contrib/libarchive/libarchive/archive_util.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_util.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_util.c (revision 316338) @@ -1,667 +1,585 @@ /*- * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) #include #endif #ifdef HAVE_ZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZ4_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_string.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif static int archive_utility_string_sort_helper(char **, unsigned int); /* Generic initialization of 'struct archive' objects. */ int __archive_clean(struct archive *a) { archive_string_conversion_free(a); return (ARCHIVE_OK); } int archive_version_number(void) { return (ARCHIVE_VERSION_NUMBER); } const char * archive_version_string(void) { return (ARCHIVE_VERSION_STRING); } -const char * -archive_version_details(void) -{ - static struct archive_string str; - static int init = 0; - const char *zlib = archive_zlib_version(); - const char *liblzma = archive_liblzma_version(); - const char *bzlib = archive_bzlib_version(); - const char *liblz4 = archive_liblz4_version(); - - if (!init) { - archive_string_init(&str); - - archive_strcat(&str, ARCHIVE_VERSION_STRING); - if (zlib != NULL) { - archive_strcat(&str, " zlib/"); - archive_strcat(&str, zlib); - } - if (liblzma) { - archive_strcat(&str, " liblzma/"); - archive_strcat(&str, liblzma); - } - if (bzlib) { - const char *p = bzlib; - const char *sep = strchr(p, ','); - if (sep == NULL) - sep = p + strlen(p); - archive_strcat(&str, " bz2lib/"); - archive_strncat(&str, p, sep - p); - } - if (liblz4) { - archive_strcat(&str, " liblz4/"); - archive_strcat(&str, liblz4); - } - } - return str.s; -} - -const char * -archive_zlib_version(void) -{ -#ifdef HAVE_ZLIB_H - return ZLIB_VERSION; -#else - return NULL; -#endif -} - -const char * -archive_liblzma_version(void) -{ -#ifdef HAVE_LZMA_H - return LZMA_VERSION_STRING; -#else - return NULL; -#endif -} - -const char * -archive_bzlib_version(void) -{ -#ifdef HAVE_BZLIB_H - return BZ2_bzlibVersion(); -#else - return NULL; -#endif -} - -const char * -archive_liblz4_version(void) -{ -#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4) -#define str(s) #s -#define NUMBER(x) str(x) - return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE); -#undef NUMBER -#undef str -#else - return NULL; -#endif -} - int archive_errno(struct archive *a) { return (a->archive_error_number); } const char * archive_error_string(struct archive *a) { if (a->error != NULL && *a->error != '\0') return (a->error); else return (NULL); } int archive_file_count(struct archive *a) { return (a->file_count); } int archive_format(struct archive *a) { return (a->archive_format); } const char * archive_format_name(struct archive *a) { return (a->archive_format_name); } int archive_compression(struct archive *a) { return archive_filter_code(a, 0); } const char * archive_compression_name(struct archive *a) { return archive_filter_name(a, 0); } /* * Return a count of the number of compressed bytes processed. */ int64_t archive_position_compressed(struct archive *a) { return archive_filter_bytes(a, -1); } /* * Return a count of the number of uncompressed bytes processed. */ int64_t archive_position_uncompressed(struct archive *a) { return archive_filter_bytes(a, 0); } void archive_clear_error(struct archive *a) { archive_string_empty(&a->error_string); a->error = NULL; a->archive_error_number = 0; } void archive_set_error(struct archive *a, int error_number, const char *fmt, ...) { va_list ap; a->archive_error_number = error_number; if (fmt == NULL) { a->error = NULL; return; } archive_string_empty(&(a->error_string)); va_start(ap, fmt); archive_string_vsprintf(&(a->error_string), fmt, ap); va_end(ap); a->error = a->error_string.s; } void archive_copy_error(struct archive *dest, struct archive *src) { dest->archive_error_number = src->archive_error_number; archive_string_copy(&dest->error_string, &src->error_string); dest->error = dest->error_string.s; } void __archive_errx(int retvalue, const char *msg) { - static const char *msg1 = "Fatal Internal Error in libarchive: "; + static const char msg1[] = "Fatal Internal Error in libarchive: "; size_t s; s = write(2, msg1, strlen(msg1)); (void)s; /* UNUSED */ s = write(2, msg, strlen(msg)); (void)s; /* UNUSED */ s = write(2, "\n", 1); (void)s; /* UNUSED */ exit(retvalue); } /* * Create a temporary file */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Do not use Windows tmpfile() function. * It will make a temporary file under the root directory * and it'll cause permission error if a user who is * non-Administrator creates temporary files. * Also Windows version of mktemp family including _mktemp_s * are not secure. */ int __archive_mktemp(const char *tmpdir) { - static const wchar_t *prefix = L"libarchive_"; - static const wchar_t *suffix = L"XXXXXXXXXX"; + static const wchar_t prefix[] = L"libarchive_"; + static const wchar_t suffix[] = L"XXXXXXXXXX"; static const wchar_t num[] = { L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd', L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l', L'm', L'n', L'o', L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z' }; HCRYPTPROV hProv; struct archive_wstring temp_name; wchar_t *ws; DWORD attr; wchar_t *xp, *ep; int fd; hProv = (HCRYPTPROV)NULL; fd = -1; ws = NULL; archive_string_init(&temp_name); /* Get a temporary directory. */ if (tmpdir == NULL) { size_t l; wchar_t *tmp; l = GetTempPathW(0, NULL); if (l == 0) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } tmp = malloc(l*sizeof(wchar_t)); if (tmp == NULL) { errno = ENOMEM; goto exit_tmpfile; } GetTempPathW((DWORD)l, tmp); archive_wstrcpy(&temp_name, tmp); free(tmp); } else { if (archive_wstring_append_from_mbs(&temp_name, tmpdir, strlen(tmpdir)) < 0) goto exit_tmpfile; if (temp_name.s[temp_name.length-1] != L'/') archive_wstrappend_wchar(&temp_name, L'/'); } /* Check if temp_name is a directory. */ attr = GetFileAttributesW(temp_name.s); if (attr == (DWORD)-1) { if (GetLastError() != ERROR_FILE_NOT_FOUND) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } ws = __la_win_permissive_name_w(temp_name.s); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } attr = GetFileAttributesW(ws); if (attr == (DWORD)-1) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } } if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { errno = ENOTDIR; goto exit_tmpfile; } /* * Create a temporary file. */ archive_wstrcat(&temp_name, prefix); archive_wstrcat(&temp_name, suffix); ep = temp_name.s + archive_strlen(&temp_name); xp = ep - wcslen(suffix); if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } for (;;) { wchar_t *p; HANDLE h; /* Generate a random file name through CryptGenRandom(). */ p = xp; if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; free(ws); ws = __la_win_permissive_name_w(temp_name.s); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } /* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to * delete this temporary file immediately when this * file closed. */ h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ NULL, CREATE_NEW,/* Create a new file only */ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (h == INVALID_HANDLE_VALUE) { /* The same file already exists. retry with * a new filename. */ if (GetLastError() == ERROR_FILE_EXISTS) continue; /* Otherwise, fail creation temporary file. */ la_dosmaperr(GetLastError()); goto exit_tmpfile; } fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR); if (fd == -1) { CloseHandle(h); goto exit_tmpfile; } else break;/* success! */ } exit_tmpfile: if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); free(ws); archive_wstring_free(&temp_name); return (fd); } #else static int get_tempdir(struct archive_string *temppath) { const char *tmp; tmp = getenv("TMPDIR"); if (tmp == NULL) #ifdef _PATH_TMP tmp = _PATH_TMP; #else tmp = "/tmp"; #endif archive_strcpy(temppath, tmp); if (temppath->s[temppath->length-1] != '/') archive_strappend_char(temppath, '/'); return (ARCHIVE_OK); } #if defined(HAVE_MKSTEMP) /* * We can use mkstemp(). */ int __archive_mktemp(const char *tmpdir) { struct archive_string temp_name; int fd = -1; archive_string_init(&temp_name); if (tmpdir == NULL) { if (get_tempdir(&temp_name) != ARCHIVE_OK) goto exit_tmpfile; } else { archive_strcpy(&temp_name, tmpdir); if (temp_name.s[temp_name.length-1] != '/') archive_strappend_char(&temp_name, '/'); } archive_strcat(&temp_name, "libarchive_XXXXXX"); fd = mkstemp(temp_name.s); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); return (fd); } #else /* * We use a private routine. */ int __archive_mktemp(const char *tmpdir) { static const char num[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; struct archive_string temp_name; struct stat st; int fd; char *tp, *ep; fd = -1; archive_string_init(&temp_name); if (tmpdir == NULL) { if (get_tempdir(&temp_name) != ARCHIVE_OK) goto exit_tmpfile; } else archive_strcpy(&temp_name, tmpdir); if (temp_name.s[temp_name.length-1] == '/') { temp_name.s[temp_name.length-1] = '\0'; temp_name.length --; } if (stat(temp_name.s, &st) < 0) goto exit_tmpfile; if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; goto exit_tmpfile; } archive_strcat(&temp_name, "/libarchive_"); tp = temp_name.s + archive_strlen(&temp_name); archive_strcat(&temp_name, "XXXXXXXXXX"); ep = temp_name.s + archive_strlen(&temp_name); do { char *p; p = tp; archive_random(p, ep - p); while (p < ep) { int d = *((unsigned char *)p) % sizeof(num); *p++ = num[d]; } fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); } while (fd < 0 && errno == EEXIST); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); return (fd); } #endif /* HAVE_MKSTEMP */ #endif /* !_WIN32 || __CYGWIN__ */ /* * Set FD_CLOEXEC flag to a file descriptor if it is not set. * We have to set the flag if the platform does not provide O_CLOEXEC * or F_DUPFD_CLOEXEC flags. * * Note: This function is absolutely called after creating a new file * descriptor even if the platform seemingly provides O_CLOEXEC or * F_DUPFD_CLOEXEC macros because it is possible that the platform * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it. */ void __archive_ensure_cloexec_flag(int fd) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)fd; /* UNUSED */ #else int flags; if (fd >= 0) { flags = fcntl(fd, F_GETFD); if (flags != -1 && (flags & FD_CLOEXEC) == 0) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } #endif } /* * Utility function to sort a group of strings using quicksort. */ static int archive_utility_string_sort_helper(char **strings, unsigned int n) { unsigned int i, lesser_count, greater_count; char **lesser, **greater, **tmp, *pivot; int retval1, retval2; /* A list of 0 or 1 elements is already sorted */ if (n <= 1) return (ARCHIVE_OK); lesser_count = greater_count = 0; lesser = greater = NULL; pivot = strings[0]; for (i = 1; i < n; i++) { if (strcmp(strings[i], pivot) < 0) { lesser_count++; tmp = (char **)realloc(lesser, lesser_count * sizeof(char *)); if (!tmp) { free(greater); free(lesser); return (ARCHIVE_FATAL); } lesser = tmp; lesser[lesser_count - 1] = strings[i]; } else { greater_count++; tmp = (char **)realloc(greater, greater_count * sizeof(char *)); if (!tmp) { free(greater); free(lesser); return (ARCHIVE_FATAL); } greater = tmp; greater[greater_count - 1] = strings[i]; } } /* quicksort(lesser) */ retval1 = archive_utility_string_sort_helper(lesser, lesser_count); for (i = 0; i < lesser_count; i++) strings[i] = lesser[i]; free(lesser); /* pivot */ strings[lesser_count] = pivot; /* quicksort(greater) */ retval2 = archive_utility_string_sort_helper(greater, greater_count); for (i = 0; i < greater_count; i++) strings[lesser_count + 1 + i] = greater[i]; free(greater); return (retval1 < retval2) ? retval1 : retval2; } int archive_utility_string_sort(char **strings) { unsigned int size = 0; while (strings[size] != NULL) size++; return archive_utility_string_sort_helper(strings, size); } Index: stable/10/contrib/libarchive/libarchive/archive_version_details.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_version_details.c (nonexistent) +++ stable/10/contrib/libarchive/libarchive/archive_version_details.c (revision 316338) @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ZLIB_H +#include +#endif +#ifdef HAVE_LZMA_H +#include +#endif +#ifdef HAVE_BZLIB_H +#include +#endif +#ifdef HAVE_LZ4_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_string.h" + +const char * +archive_version_details(void) +{ + static struct archive_string str; + static int init = 0; + const char *zlib = archive_zlib_version(); + const char *liblzma = archive_liblzma_version(); + const char *bzlib = archive_bzlib_version(); + const char *liblz4 = archive_liblz4_version(); + + if (!init) { + archive_string_init(&str); + + archive_strcat(&str, ARCHIVE_VERSION_STRING); + if (zlib != NULL) { + archive_strcat(&str, " zlib/"); + archive_strcat(&str, zlib); + } + if (liblzma) { + archive_strcat(&str, " liblzma/"); + archive_strcat(&str, liblzma); + } + if (bzlib) { + const char *p = bzlib; + const char *sep = strchr(p, ','); + if (sep == NULL) + sep = p + strlen(p); + archive_strcat(&str, " bz2lib/"); + archive_strncat(&str, p, sep - p); + } + if (liblz4) { + archive_strcat(&str, " liblz4/"); + archive_strcat(&str, liblz4); + } + } + return str.s; +} + +const char * +archive_zlib_version(void) +{ +#ifdef HAVE_ZLIB_H + return ZLIB_VERSION; +#else + return NULL; +#endif +} + +const char * +archive_liblzma_version(void) +{ +#ifdef HAVE_LZMA_H + return LZMA_VERSION_STRING; +#else + return NULL; +#endif +} + +const char * +archive_bzlib_version(void) +{ +#ifdef HAVE_BZLIB_H + return BZ2_bzlibVersion(); +#else + return NULL; +#endif +} + +const char * +archive_liblz4_version(void) +{ +#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4) +#define str(s) #s +#define NUMBER(x) str(x) + return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE); +#undef NUMBER +#undef str +#else + return NULL; +#endif +} Property changes on: stable/10/contrib/libarchive/libarchive/archive_version_details.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/archive_write_add_filter.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_add_filter.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_add_filter.c (revision 316338) @@ -1,71 +1,71 @@ /*- * Copyright (c) 2012 Ondrej Holy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps filter codes to functions. */ -static +static const struct { int code; int (*setter)(struct archive *); } codes[] = { { ARCHIVE_FILTER_NONE, archive_write_add_filter_none }, { ARCHIVE_FILTER_GZIP, archive_write_add_filter_gzip }, { ARCHIVE_FILTER_BZIP2, archive_write_add_filter_bzip2 }, { ARCHIVE_FILTER_COMPRESS, archive_write_add_filter_compress }, { ARCHIVE_FILTER_GRZIP, archive_write_add_filter_grzip }, { ARCHIVE_FILTER_LRZIP, archive_write_add_filter_lrzip }, { ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 }, { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma }, { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode }, { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz }, { -1, NULL } }; int archive_write_add_filter(struct archive *a, int code) { int i; for (i = 0; codes[i].code != -1; i++) { if (code == codes[i].code) return ((codes[i].setter)(a)); } archive_set_error(a, EINVAL, "No such filter"); return (ARCHIVE_FATAL); } Index: stable/10/contrib/libarchive/libarchive/archive_write_add_filter_by_name.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_add_filter_by_name.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_add_filter_by_name.c (revision 316338) @@ -1,76 +1,76 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps names to functions. */ -static +static const struct { const char *name; int (*setter)(struct archive *); } names[] = { { "b64encode", archive_write_add_filter_b64encode }, { "bzip2", archive_write_add_filter_bzip2 }, { "compress", archive_write_add_filter_compress }, { "grzip", archive_write_add_filter_grzip }, { "gzip", archive_write_add_filter_gzip }, { "lrzip", archive_write_add_filter_lrzip }, { "lz4", archive_write_add_filter_lz4 }, { "lzip", archive_write_add_filter_lzip }, { "lzma", archive_write_add_filter_lzma }, { "lzop", archive_write_add_filter_lzop }, { "uuencode", archive_write_add_filter_uuencode }, { "xz", archive_write_add_filter_xz }, { NULL, NULL } }; int archive_write_add_filter_by_name(struct archive *a, const char *name) { int i; for (i = 0; names[i].name != NULL; i++) { if (strcmp(name, names[i].name) == 0) return ((names[i].setter)(a)); } archive_set_error(a, EINVAL, "No such filter '%s'", name); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } Index: stable/10/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c (revision 316338) @@ -1,707 +1,707 @@ /*- * Copyright (c) 2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_LZ4_H #include #endif #ifdef HAVE_LZ4HC_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_xxhash.h" #define LZ4_MAGICNUMBER 0x184d2204 struct private_data { int compression_level; unsigned header_written:1; unsigned version_number:1; unsigned block_independence:1; unsigned block_checksum:1; unsigned stream_size:1; unsigned stream_checksum:1; unsigned preset_dictionary:1; unsigned block_maximum_size:3; #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 int64_t total_in; char *out; char *out_buffer; size_t out_buffer_size; size_t out_block_size; char *in; char *in_buffer_allocated; char *in_buffer; size_t in_buffer_size; size_t block_size; void *xxh32_state; void *lz4_stream; #else struct archive_write_program_data *pdata; #endif }; static int archive_filter_lz4_close(struct archive_write_filter *); static int archive_filter_lz4_free(struct archive_write_filter *); static int archive_filter_lz4_open(struct archive_write_filter *); static int archive_filter_lz4_options(struct archive_write_filter *, const char *, const char *); static int archive_filter_lz4_write(struct archive_write_filter *, const void *, size_t); /* * Add a lz4 compression filter to this write handle. */ int archive_write_add_filter_lz4(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f = __archive_write_allocate_filter(_a); struct private_data *data; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4"); data = calloc(1, sizeof(*data)); if (data == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } /* * Setup default settings. */ data->compression_level = 1; data->version_number = 0x01; data->block_independence = 1; data->block_checksum = 0; data->stream_size = 0; data->stream_checksum = 1; data->preset_dictionary = 0; data->block_maximum_size = 7; /* * Setup a filter setting. */ f->data = data; f->options = &archive_filter_lz4_options; f->close = &archive_filter_lz4_close; f->free = &archive_filter_lz4_free; f->open = &archive_filter_lz4_open; f->code = ARCHIVE_FILTER_LZ4; f->name = "lz4"; #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 return (ARCHIVE_OK); #else /* * We don't have lz4 library, and execute external lz4 program * instead. */ data->pdata = __archive_write_program_allocate("lz4"); if (data->pdata == NULL) { free(data); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } data->compression_level = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Using external lz4 program"); return (ARCHIVE_WARN); #endif } /* * Set write options. */ static int archive_filter_lz4_options(struct archive_write_filter *f, const char *key, const char *value) { struct private_data *data = (struct private_data *)f->data; if (strcmp(key, "compression-level") == 0) { int val; if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) || value[1] != '\0') return (ARCHIVE_WARN); #ifndef HAVE_LZ4HC_H if(val >= 3) { archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, "High compression not included in this build"); return (ARCHIVE_FATAL); } #endif data->compression_level = val; return (ARCHIVE_OK); } if (strcmp(key, "stream-checksum") == 0) { data->stream_checksum = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "block-checksum") == 0) { data->block_checksum = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "block-size") == 0) { if (value == NULL || !(value[0] >= '4' && value[0] <= '7') || value[1] != '\0') return (ARCHIVE_WARN); data->block_maximum_size = value[0] - '0'; return (ARCHIVE_OK); } if (strcmp(key, "block-dependence") == 0) { data->block_independence = value == NULL; return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 /* Don't compile this if we don't have liblz4. */ static int drive_compressor(struct archive_write_filter *, const char *, size_t); static int drive_compressor_independence(struct archive_write_filter *, const char *, size_t); static int drive_compressor_dependence(struct archive_write_filter *, const char *, size_t); static int lz4_write_stream_descriptor(struct archive_write_filter *); static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *, size_t); /* * Setup callback. */ static int archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; int ret; size_t required_size; - static size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, + static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 4 * 1024 * 1024 }; size_t pre_block_size; ret = __archive_write_open_filter(f->next_filter); if (ret != 0) return (ret); if (data->block_maximum_size < 4) data->block_size = bkmap[0]; else data->block_size = bkmap[data->block_maximum_size - 4]; required_size = 4 + 15 + 4 + data->block_size + 4 + 4; if (data->out_buffer_size < required_size) { size_t bs = required_size, bpb; free(data->out_buffer); 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 += bpb; bs -= bs % bpb; } } data->out_block_size = bs; bs += required_size; data->out_buffer = malloc(bs); data->out = data->out_buffer; data->out_buffer_size = bs; } pre_block_size = (data->block_independence)? 0: 64 * 1024; if (data->in_buffer_size < data->block_size + pre_block_size) { free(data->in_buffer_allocated); data->in_buffer_size = data->block_size; data->in_buffer_allocated = malloc(data->in_buffer_size + pre_block_size); data->in_buffer = data->in_buffer_allocated + pre_block_size; if (!data->block_independence && data->compression_level >= 3) data->in_buffer = data->in_buffer_allocated; data->in = data->in_buffer; data->in_buffer_size = data->block_size; } if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } f->write = archive_filter_lz4_write; return (ARCHIVE_OK); } /* * Write data to the out stream. * * Returns ARCHIVE_OK if all data written, error otherwise. */ static int archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; int ret = ARCHIVE_OK; const char *p; size_t remaining; ssize_t size; /* If we haven't written a stream descriptor, we have to do it first. */ if (!data->header_written) { ret = lz4_write_stream_descriptor(f); if (ret != ARCHIVE_OK) return (ret); data->header_written = 1; } /* Update statistics */ data->total_in += length; p = (const char *)buff; remaining = length; while (remaining) { size_t l; /* Compress input data to output buffer */ size = lz4_write_one_block(f, p, remaining); if (size < ARCHIVE_OK) return (ARCHIVE_FATAL); l = data->out - data->out_buffer; if (l >= data->out_block_size) { ret = __archive_write_filter(f->next_filter, data->out_buffer, data->out_block_size); l -= data->out_block_size; memcpy(data->out_buffer, data->out_buffer + data->out_block_size, l); data->out = data->out_buffer + l; if (ret < ARCHIVE_WARN) break; } p += size; remaining -= size; } return (ret); } /* * Finish the compression. */ static int archive_filter_lz4_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; int ret, r1; /* Finish compression cycle. */ ret = (int)lz4_write_one_block(f, NULL, 0); if (ret >= 0) { /* * Write the last block and the end of the stream data. */ /* Write End Of Stream. */ memset(data->out, 0, 4); data->out += 4; /* Write Stream checksum if needed. */ if (data->stream_checksum) { unsigned int checksum; checksum = __archive_xxhash.XXH32_digest( data->xxh32_state); data->xxh32_state = NULL; archive_le32enc(data->out, checksum); data->out += 4; } ret = __archive_write_filter(f->next_filter, data->out_buffer, data->out - data->out_buffer); } r1 = __archive_write_close_filter(f->next_filter); return (r1 < ret ? r1 : ret); } static int archive_filter_lz4_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; if (data->lz4_stream != NULL) { #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 LZ4_freeStreamHC(data->lz4_stream); #else LZ4_freeHC(data->lz4_stream); #endif else #endif #if LZ4_VERSION_MINOR >= 3 LZ4_freeStream(data->lz4_stream); #else LZ4_free(data->lz4_stream); #endif } free(data->out_buffer); free(data->in_buffer_allocated); free(data->xxh32_state); free(data); f->data = NULL; return (ARCHIVE_OK); } static int lz4_write_stream_descriptor(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; uint8_t *sd; sd = (uint8_t *)data->out; /* Write Magic Number. */ archive_le32enc(&sd[0], LZ4_MAGICNUMBER); /* FLG */ sd[4] = (data->version_number << 6) | (data->block_independence << 5) | (data->block_checksum << 4) | (data->stream_size << 3) | (data->stream_checksum << 2) | (data->preset_dictionary << 0); /* BD */ sd[5] = (data->block_maximum_size << 4); sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; data->out += 7; if (data->stream_checksum) data->xxh32_state = __archive_xxhash.XXH32_init(0); else data->xxh32_state = NULL; return (ARCHIVE_OK); } static ssize_t lz4_write_one_block(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; ssize_t r; if (p == NULL) { /* Compress remaining uncompressed data. */ if (data->in_buffer == data->in) return 0; else { size_t l = data->in - data->in_buffer; r = drive_compressor(f, data->in_buffer, l); if (r == ARCHIVE_OK) r = (ssize_t)l; } } else if ((data->block_independence || data->compression_level < 3) && data->in_buffer == data->in && length >= data->block_size) { r = drive_compressor(f, p, data->block_size); if (r == ARCHIVE_OK) r = (ssize_t)data->block_size; } else { size_t remaining_size = data->in_buffer_size - (data->in - data->in_buffer); size_t l = (remaining_size > length)? length: remaining_size; memcpy(data->in, p, l); data->in += l; if (l == remaining_size) { r = drive_compressor(f, data->in_buffer, data->block_size); if (r == ARCHIVE_OK) r = (ssize_t)l; data->in = data->in_buffer; } else r = (ssize_t)l; } return (r); } /* * Utility function to push input data through compressor, writing * full output blocks as necessary. * * Note that this handles both the regular write case (finishing == * false) and the end-of-archive case (finishing == true). */ static int drive_compressor(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; if (data->stream_checksum) __archive_xxhash.XXH32_update(data->xxh32_state, p, (int)length); if (data->block_independence) return drive_compressor_independence(f, p, length); else return drive_compressor_dependence(f, p, length); } static int drive_compressor_independence(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; unsigned int outsize; #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_HC(p, data->out + 4, (int)length, (int)data->block_size, data->compression_level); #else outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4, (int)length, (int)data->block_size, data->compression_level); #endif else #endif #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_default(p, data->out + 4, (int)length, (int)data->block_size); #else outsize = LZ4_compress_limitedOutput(p, data->out + 4, (int)length, (int)data->block_size); #endif if (outsize) { /* The buffer is compressed. */ archive_le32enc(data->out, outsize); data->out += 4; } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ archive_le32enc(data->out, length | 0x80000000); data->out += 4; memcpy(data->out, p, length); outsize = length; } data->out += outsize; if (data->block_checksum) { unsigned int checksum = __archive_xxhash.XXH32(data->out - outsize, outsize, 0); archive_le32enc(data->out, checksum); data->out += 4; } return (ARCHIVE_OK); } static int drive_compressor_dependence(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; int outsize; #define DICT_SIZE (64 * 1024) #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) { if (data->lz4_stream == NULL) { #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 data->lz4_stream = LZ4_createStreamHC(); LZ4_resetStreamHC(data->lz4_stream, data->compression_level); #else data->lz4_stream = LZ4_createHC(data->in_buffer_allocated); #endif if (data->lz4_stream == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression" " buffer"); return (ARCHIVE_FATAL); } } else LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_HC_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size); #else outsize = LZ4_compressHC2_limitedOutput_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size, data->compression_level); #endif } else #endif { if (data->lz4_stream == NULL) { data->lz4_stream = LZ4_createStream(); if (data->lz4_stream == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression" " buffer"); return (ARCHIVE_FATAL); } } else LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_fast_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size, 1); #else outsize = LZ4_compress_limitedOutput_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size); #endif } if (outsize) { /* The buffer is compressed. */ archive_le32enc(data->out, outsize); data->out += 4; } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ archive_le32enc(data->out, length | 0x80000000); data->out += 4; memcpy(data->out, p, length); outsize = length; } data->out += outsize; if (data->block_checksum) { unsigned int checksum = __archive_xxhash.XXH32(data->out - outsize, outsize, 0); archive_le32enc(data->out, checksum); data->out += 4; } if (length == data->block_size) { #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) { #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #else LZ4_slideInputBufferHC(data->lz4_stream); #endif data->in_buffer = data->in_buffer_allocated + DICT_SIZE; } else #endif LZ4_saveDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #undef DICT_SIZE } return (ARCHIVE_OK); } #else /* HAVE_LIBLZ4 */ static int archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; struct archive_string as; int r; archive_string_init(&as); archive_strcpy(&as, "lz4 -z -q -q"); /* Specify a compression level. */ if (data->compression_level > 0) { archive_strcat(&as, " -"); archive_strappend_char(&as, '0' + data->compression_level); } /* Specify a block size. */ archive_strcat(&as, " -B"); archive_strappend_char(&as, '0' + data->block_maximum_size); if (data->block_checksum) archive_strcat(&as, " -BX"); if (data->stream_checksum == 0) archive_strcat(&as, " --no-frame-crc"); if (data->block_independence == 0) archive_strcat(&as, " -BD"); f->write = archive_filter_lz4_write; r = __archive_write_program_open(f, data->pdata, as.s); archive_string_free(&as); return (r); } static int archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_write(f, data->pdata, buff, length); } static int archive_filter_lz4_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_close(f, data->pdata); } static int archive_filter_lz4_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; __archive_write_program_free(data->pdata); free(data); return (ARCHIVE_OK); } #endif /* HAVE_LIBLZ4 */ Index: stable/10/contrib/libarchive/libarchive/archive_write_add_filter_program.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_add_filter_program.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_add_filter_program.c (revision 316338) @@ -1,415 +1,415 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * 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: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $"); #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" #include "filter_fork.h" #if ARCHIVE_VERSION_NUMBER < 4000000 int archive_write_set_compression_program(struct archive *a, const char *cmd) { __archive_write_filters_free(a); return (archive_write_add_filter_program(a, cmd)); } #endif struct archive_write_program_data { #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE child; #else pid_t child; #endif int child_stdin, child_stdout; char *child_buf; size_t child_buf_len, child_buf_avail; char *program_name; }; struct private_data { struct archive_write_program_data *pdata; struct archive_string description; char *cmd; }; static int archive_compressor_program_open(struct archive_write_filter *); static int archive_compressor_program_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_program_close(struct archive_write_filter *); static int archive_compressor_program_free(struct archive_write_filter *); /* * Add a filter to this write handle that passes all data through an * external program. */ int archive_write_add_filter_program(struct archive *_a, const char *cmd) { struct archive_write_filter *f = __archive_write_allocate_filter(_a); struct private_data *data; - static const char *prefix = "Program: "; + static const char prefix[] = "Program: "; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_program"); f->data = calloc(1, sizeof(*data)); if (f->data == NULL) goto memerr; data = (struct private_data *)f->data; data->cmd = strdup(cmd); if (data->cmd == NULL) goto memerr; data->pdata = __archive_write_program_allocate(cmd); if (data->pdata == NULL) goto memerr; /* Make up a description string. */ if (archive_string_ensure(&data->description, strlen(prefix) + strlen(cmd) + 1) == NULL) goto memerr; archive_strcpy(&data->description, prefix); archive_strcat(&data->description, cmd); f->name = data->description.s; f->code = ARCHIVE_FILTER_PROGRAM; f->open = archive_compressor_program_open; f->write = archive_compressor_program_write; f->close = archive_compressor_program_close; f->free = archive_compressor_program_free; return (ARCHIVE_OK); memerr: archive_compressor_program_free(f); archive_set_error(_a, ENOMEM, "Can't allocate memory for filter program"); return (ARCHIVE_FATAL); } static int archive_compressor_program_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_open(f, data->pdata, data->cmd); } static int archive_compressor_program_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_write(f, data->pdata, buff, length); } static int archive_compressor_program_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_close(f, data->pdata); } static int archive_compressor_program_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; if (data) { free(data->cmd); archive_string_free(&data->description); __archive_write_program_free(data->pdata); free(data); f->data = NULL; } return (ARCHIVE_OK); } /* * Allocate resources for executing an external program. */ struct archive_write_program_data * __archive_write_program_allocate(const char *program) { struct archive_write_program_data *data; data = calloc(1, sizeof(struct archive_write_program_data)); if (data == NULL) return (data); data->child_stdin = -1; data->child_stdout = -1; data->program_name = strdup(program); return (data); } /* * Release the resources. */ int __archive_write_program_free(struct archive_write_program_data *data) { if (data) { #if defined(_WIN32) && !defined(__CYGWIN__) if (data->child) CloseHandle(data->child); #endif free(data->program_name); free(data->child_buf); free(data); } return (ARCHIVE_OK); } int __archive_write_program_open(struct archive_write_filter *f, struct archive_write_program_data *data, const char *cmd) { pid_t child; int ret; ret = __archive_write_open_filter(f->next_filter); if (ret != ARCHIVE_OK) return (ret); if (data->child_buf == NULL) { data->child_buf_len = 65536; data->child_buf_avail = 0; data->child_buf = malloc(data->child_buf_len); if (data->child_buf == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate compression buffer"); return (ARCHIVE_FATAL); } } child = __archive_create_child(cmd, &data->child_stdin, &data->child_stdout); if (child == -1) { archive_set_error(f->archive, EINVAL, "Can't launch external program: %s", cmd); return (ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) data->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child); if (data->child == NULL) { close(data->child_stdin); data->child_stdin = -1; close(data->child_stdout); data->child_stdout = -1; archive_set_error(f->archive, EINVAL, "Can't launch external program: %s", cmd); return (ARCHIVE_FATAL); } #else data->child = child; #endif return (ARCHIVE_OK); } static ssize_t child_write(struct archive_write_filter *f, struct archive_write_program_data *data, const char *buf, size_t buf_len) { ssize_t ret; if (data->child_stdin == -1) return (-1); if (buf_len == 0) return (-1); for (;;) { do { ret = write(data->child_stdin, buf, buf_len); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0) { close(data->child_stdin); data->child_stdin = -1; fcntl(data->child_stdout, F_SETFL, 0); return (0); } if (ret == -1 && errno != EAGAIN) return (-1); if (data->child_stdout == -1) { fcntl(data->child_stdin, F_SETFL, 0); __archive_check_child(data->child_stdin, data->child_stdout); continue; } do { ret = read(data->child_stdout, data->child_buf + data->child_buf_avail, data->child_buf_len - data->child_buf_avail); } while (ret == -1 && errno == EINTR); if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(data->child_stdout); data->child_stdout = -1; fcntl(data->child_stdin, F_SETFL, 0); continue; } if (ret == -1 && errno == EAGAIN) { __archive_check_child(data->child_stdin, data->child_stdout); continue; } if (ret == -1) return (-1); data->child_buf_avail += ret; ret = __archive_write_filter(f->next_filter, data->child_buf, data->child_buf_avail); if (ret != ARCHIVE_OK) return (-1); data->child_buf_avail = 0; } } /* * Write data to the filter stream. */ int __archive_write_program_write(struct archive_write_filter *f, struct archive_write_program_data *data, const void *buff, size_t length) { ssize_t ret; const char *buf; if (data->child == 0) return (ARCHIVE_OK); buf = buff; while (length > 0) { ret = child_write(f, data, buf, length); if (ret == -1 || ret == 0) { archive_set_error(f->archive, EIO, "Can't write to program: %s", data->program_name); return (ARCHIVE_FATAL); } length -= ret; buf += ret; } return (ARCHIVE_OK); } /* * Finish the filtering... */ int __archive_write_program_close(struct archive_write_filter *f, struct archive_write_program_data *data) { int ret, r1, status; ssize_t bytes_read; if (data->child == 0) return __archive_write_close_filter(f->next_filter); ret = 0; close(data->child_stdin); data->child_stdin = -1; fcntl(data->child_stdout, F_SETFL, 0); for (;;) { do { bytes_read = read(data->child_stdout, data->child_buf + data->child_buf_avail, data->child_buf_len - data->child_buf_avail); } while (bytes_read == -1 && errno == EINTR); if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) break; if (bytes_read == -1) { archive_set_error(f->archive, errno, "Error reading from program: %s", data->program_name); ret = ARCHIVE_FATAL; goto cleanup; } data->child_buf_avail += bytes_read; ret = __archive_write_filter(f->next_filter, data->child_buf, data->child_buf_avail); if (ret != ARCHIVE_OK) { ret = ARCHIVE_FATAL; goto cleanup; } data->child_buf_avail = 0; } cleanup: /* Shut down the child. */ if (data->child_stdin != -1) close(data->child_stdin); if (data->child_stdout != -1) close(data->child_stdout); while (waitpid(data->child, &status, 0) == -1 && errno == EINTR) continue; #if defined(_WIN32) && !defined(__CYGWIN__) CloseHandle(data->child); #endif data->child = 0; if (status != 0) { archive_set_error(f->archive, EIO, "Error closing program: %s", data->program_name); ret = ARCHIVE_FATAL; } r1 = __archive_write_close_filter(f->next_filter); return (r1 < ret ? r1 : ret); } Index: stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 316338) @@ -1,4243 +1,4295 @@ /*- * 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 defined(HAVE_SYS_XATTR_H) +#if HAVE_SYS_XATTR_H #include -#elif defined(HAVE_ATTR_XATTR_H) +#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 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 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 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 (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, int64_t d, 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; -#ifdef HAVE_DARWIN_ACL - /* - * On Mac OS, platform ACLs are stored also in mac_metadata by - * the operating system. If mac_metadata is present it takes - * precedence and we skip extracting libarchive NFSv4 ACLs - */ - const void *metadata; - size_t metadata_size; - metadata = archive_entry_mac_metadata(a->entry, &metadata_size); - if ((a->todo & TODO_MAC_METADATA) == 0 || - metadata == NULL || metadata_size == 0) { -#endif r2 = archive_write_disk_set_acls(&a->archive, a->fd, archive_entry_pathname(a->entry), - archive_entry_acl(a->entry)); + archive_entry_acl(a->entry), + archive_entry_mode(a->entry)); if (r2 < ret) ret = r2; -#ifdef HAVE_DARWIN_ACL - } -#endif } 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. */ if (a->entry) { 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, int64_t (*lookup_gid)(void *private, const char *gname, 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, 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, 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); __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. */ 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 = 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 = 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; 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) { a->pst = NULL; /* Mark stat cache as out-of-date. */ if (p->fixup & TODO_TIMES) { set_times(a, -1, 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) chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) -#ifdef HAVE_DARWIN_ACL - if ((p->fixup & TODO_MAC_METADATA) == 0 || - p->mac_metadata == NULL || - p->mac_metadata_size == 0) -#endif - archive_write_disk_set_acls(&a->archive, - -1, p->name, &p->acl); + archive_write_disk_set_acls(&a->archive, -1, p->name, + &p->acl, p->mode); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, -1, 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); 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); if (a->entry) 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) /* 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; /* 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) 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. */ r = lstat(head, &st); 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) { 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)) { 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) { 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. */ r = stat(head, &st); 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) { 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 we changed directory above, restore it here. */ if (restore_pwd >= 0) { r = fchdir(restore_pwd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } close(restore_pwd); restore_pwd = -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 (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 (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) { #ifndef __CYGWIN__ /* unfortunately, on win32 there is no 'root' user with uid 0, so we just have to try the chown and see if it works */ /* If we know we can't change it, don't bother trying. */ 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; 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 #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; } } return (r); } static int set_fflags(struct archive_write_disk *a) { struct fixup_entry *le; unsigned long set, clear; int r; int critical_flags; 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. */ /* Hopefully, the compiler will optimize this mess into a constant. */ critical_flags = 0; #ifdef SF_IMMUTABLE critical_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE critical_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND critical_flags |= SF_APPEND; #endif #ifdef UF_APPEND critical_flags |= UF_APPEND; #endif #if defined(FS_APPEND_FL) critical_flags |= FS_APPEND_FL; #elif defined(EXT2_APPEND_FL) critical_flags |= EXT2_APPEND_FL; #endif #if defined(FS_IMMUTABLE_FL) critical_flags |= FS_IMMUTABLE_FL; #elif defined(EXT2_IMMUTABLE_FL) critical_flags |= EXT2_IMMUTABLE_FL; #endif #ifdef FS_JOURNAL_DATA_FL critical_flags |= 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) { int nochange_flags; mode_t mode = archive_entry_mode(a->entry); /* Hopefully, the compiler will optimize this mess into a constant. */ nochange_flags = 0; #ifdef SF_IMMUTABLE nochange_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE nochange_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND nochange_flags |= SF_APPEND; #endif #ifdef UF_APPEND nochange_flags |= UF_APPEND; #endif #ifdef EXT2_APPEND_FL nochange_flags |= EXT2_APPEND_FL; #endif #ifdef EXT2_IMMUTABLE_FL nochange_flags |= 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; (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; #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; int sf_mask = 0; 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); /* * 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. */ #if defined(FS_IMMUTABLE_FL) sf_mask |= FS_IMMUTABLE_FL; #elif defined(EXT2_IMMUTABLE_FL) sf_mask |= EXT2_IMMUTABLE_FL; #endif #if defined(FS_APPEND_FL) sf_mask |= FS_APPEND_FL; #elif defined(EXT2_APPEND_FL) sf_mask |= EXT2_APPEND_FL; #endif #if defined(FS_JOURNAL_DATA_FL) sf_mask |= FS_JOURNAL_DATA_FL; #endif /* * 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 HAVE_LSETXATTR || HAVE_LSETEA +#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX /* - * Restore extended attributes - Linux and AIX implementations: + * 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 HAVE_FSETXATTR - if (a->fd >= 0) + if (a->fd >= 0) { +#if ARCHIVE_XATTR_LINUX e = fsetxattr(a->fd, name, value, size, 0); - else -#elif HAVE_FSETEA - if (a->fd >= 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); - else #endif - { -#if HAVE_LSETXATTR + } else { +#if ARCHIVE_XATTR_LINUX e = lsetxattr(archive_entry_pathname(entry), name, value, size, 0); -#elif HAVE_LSETEA +#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 HAVE_EXTATTR_SET_FILE && HAVE_DECL_EXTATTR_NAMESPACE_USER +#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 HAVE_EXTATTR_SET_FD - if (a->fd >= 0) + + if (a->fd >= 0) { e = extattr_set_fd(a->fd, namespace, name, value, size); - else -#endif - /* TODO: should we use extattr_set_link() instead? */ - { - e = extattr_set_file( + } 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/archive_write_disk_private.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_disk_private.h (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_disk_private.h (revision 316338) @@ -1,43 +1,45 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif #ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED #define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED +#include "archive_platform_acl.h" #include "archive_acl_private.h" +#include "archive_entry.h" struct archive_write_disk; -int -archive_write_disk_set_acls(struct archive *, int /* fd */, const char * /* pathname */, struct archive_acl *); +int archive_write_disk_set_acls(struct archive *, int, const char *, + struct archive_acl *, __LA_MODE_T); #endif Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format.c (revision 316338) @@ -1,78 +1,78 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps format codes to functions. */ -static +static const struct { int code; int (*setter)(struct archive *); } codes[] = { { ARCHIVE_FORMAT_7ZIP, archive_write_set_format_7zip }, { ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio }, { ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio }, { ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, archive_write_set_format_cpio_newc }, { ARCHIVE_FORMAT_ISO9660, archive_write_set_format_iso9660 }, { ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree }, { ARCHIVE_FORMAT_RAW, archive_write_set_format_raw }, { ARCHIVE_FORMAT_SHAR, archive_write_set_format_shar }, { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar }, { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump }, { ARCHIVE_FORMAT_TAR, archive_write_set_format_pax_restricted }, { ARCHIVE_FORMAT_TAR_GNUTAR, archive_write_set_format_gnutar }, { ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax }, { ARCHIVE_FORMAT_TAR_PAX_RESTRICTED, archive_write_set_format_pax_restricted }, { ARCHIVE_FORMAT_TAR_USTAR, archive_write_set_format_ustar }, { ARCHIVE_FORMAT_WARC, archive_write_set_format_warc }, { ARCHIVE_FORMAT_XAR, archive_write_set_format_xar }, { ARCHIVE_FORMAT_ZIP, archive_write_set_format_zip }, { 0, NULL } }; int archive_write_set_format(struct archive *a, int code) { int i; for (i = 0; codes[i].code != 0; i++) { if (code == codes[i].code) return ((codes[i].setter)(a)); } archive_set_error(a, EINVAL, "No such format"); return (ARCHIVE_FATAL); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_by_name.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_by_name.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_by_name.c (revision 316338) @@ -1,92 +1,92 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps names to functions. */ -static +static const struct { const char *name; int (*setter)(struct archive *); } names[] = { { "7zip", archive_write_set_format_7zip }, { "ar", archive_write_set_format_ar_bsd }, { "arbsd", archive_write_set_format_ar_bsd }, { "argnu", archive_write_set_format_ar_svr4 }, { "arsvr4", archive_write_set_format_ar_svr4 }, { "bsdtar", archive_write_set_format_pax_restricted }, { "cd9660", archive_write_set_format_iso9660 }, { "cpio", archive_write_set_format_cpio }, { "gnutar", archive_write_set_format_gnutar }, { "iso", archive_write_set_format_iso9660 }, { "iso9660", archive_write_set_format_iso9660 }, { "mtree", archive_write_set_format_mtree }, { "mtree-classic", archive_write_set_format_mtree_classic }, { "newc", archive_write_set_format_cpio_newc }, { "odc", archive_write_set_format_cpio }, { "oldtar", archive_write_set_format_v7tar }, { "pax", archive_write_set_format_pax }, { "paxr", archive_write_set_format_pax_restricted }, { "posix", archive_write_set_format_pax }, { "raw", archive_write_set_format_raw }, { "rpax", archive_write_set_format_pax_restricted }, { "shar", archive_write_set_format_shar }, { "shardump", archive_write_set_format_shar_dump }, { "ustar", archive_write_set_format_ustar }, { "v7tar", archive_write_set_format_v7tar }, { "v7", archive_write_set_format_v7tar }, { "warc", archive_write_set_format_warc }, { "xar", archive_write_set_format_xar }, { "zip", archive_write_set_format_zip }, { NULL, NULL } }; int archive_write_set_format_by_name(struct archive *a, const char *name) { int i; for (i = 0; names[i].name != NULL; i++) { if (strcmp(name, names[i].name) == 0) return ((names[i].setter)(a)); } archive_set_error(a, EINVAL, "No such format '%s'", name); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_filter_by_ext.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_filter_by_ext.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_filter_by_ext.c (revision 316338) @@ -1,142 +1,142 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2015 Okhotnikov Kirill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps names to functions. */ -static +static const struct { const char *name; int (*format)(struct archive *); int (*filter)(struct archive *); } names[] = { { ".7z", archive_write_set_format_7zip, archive_write_add_filter_none}, { ".zip", archive_write_set_format_zip, archive_write_add_filter_none}, { ".jar", archive_write_set_format_zip, archive_write_add_filter_none}, { ".cpio", archive_write_set_format_cpio, archive_write_add_filter_none}, { ".iso", archive_write_set_format_iso9660, archive_write_add_filter_none}, #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) { ".a", archive_write_set_format_ar_bsd, archive_write_add_filter_none}, { ".ar", archive_write_set_format_ar_bsd, archive_write_add_filter_none}, #else { ".a", archive_write_set_format_ar_svr4, archive_write_add_filter_none}, { ".ar", archive_write_set_format_ar_svr4, archive_write_add_filter_none}, #endif { ".tar", archive_write_set_format_pax_restricted, archive_write_add_filter_none}, { ".tgz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip}, { ".tar.gz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip}, { ".tar.bz2", archive_write_set_format_pax_restricted, archive_write_add_filter_bzip2}, { ".tar.xz", archive_write_set_format_pax_restricted, archive_write_add_filter_xz}, { NULL, NULL, NULL } }; static int cmpsuff(const char *str, const char *suffix) { size_t length_str, length_suffix; if ((str == NULL) || (suffix == NULL)) return -1; length_str = strlen(str); length_suffix = strlen(suffix); if (length_str >= length_suffix) { return strcmp(str + (length_str - length_suffix), suffix); } else { return -1; } } static int get_array_index(const char *name) { int i; for (i = 0; names[i].name != NULL; i++) { if (cmpsuff(name, names[i].name) == 0) return i; } return -1; } int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename) { int names_index = get_array_index(filename); if (names_index >= 0) { int format_state = (names[names_index].format)(a); if (format_state == ARCHIVE_OK) return ((names[names_index].filter)(a)); else return format_state; } archive_set_error(a, EINVAL, "No such format '%s'", filename); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext) { int names_index = get_array_index(filename); if (names_index < 0) names_index = get_array_index(def_ext); if (names_index >= 0) { int format_state = (names[names_index].format)(a); if (format_state == ARCHIVE_OK) return ((names[names_index].filter)(a)); else return format_state; } archive_set_error(a, EINVAL, "No such format '%s'", filename); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_warc.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_warc.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_warc.c (revision 316338) @@ -1,445 +1,445 @@ /*- * Copyright (c) 2014 Sebastian Freundt * Author: Sebastian Freundt * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" struct warc_s { unsigned int omit_warcinfo:1; time_t now; mode_t typ; unsigned int rng; /* populated size */ uint64_t populz; }; static const char warcinfo[] = "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n" "format: WARC file version 1.0\r\n"; typedef enum { WT_NONE, /* warcinfo */ WT_INFO, /* metadata */ WT_META, /* resource */ WT_RSRC, /* request, unsupported */ WT_REQ, /* response, unsupported */ WT_RSP, /* revisit, unsupported */ WT_RVIS, /* conversion, unsupported */ WT_CONV, /* continuation, unsupported at the moment */ WT_CONT, /* invalid type */ LAST_WT } warc_type_t; typedef struct { warc_type_t type; const char *tgturi; const char *recid; time_t rtime; time_t mtime; const char *cnttyp; uint64_t cntlen; } warc_essential_hdr_t; typedef struct { unsigned int u[4U]; } warc_uuid_t; static int _warc_options(struct archive_write*, const char *key, const char *v); static int _warc_header(struct archive_write *a, struct archive_entry *entry); static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz); static int _warc_finish_entry(struct archive_write *a); static int _warc_close(struct archive_write *a); static int _warc_free(struct archive_write *a); /* private routines */ static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t); static int _gen_uuid(warc_uuid_t *tgt); /* * Set output format to ISO 28500 (aka WARC) format. */ int archive_write_set_format_warc(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct warc_s *w; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_warc"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) { (a->format_free)(a); } w = malloc(sizeof(*w)); if (w == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } /* by default we're emitting a file wide header */ w->omit_warcinfo = 0U; /* obtain current time for date fields */ w->now = time(NULL); /* reset file type info */ w->typ = 0; /* also initialise our rng */ w->rng = (unsigned int)w->now; a->format_data = w; a->format_name = "WARC/1.0"; a->format_options = _warc_options; a->format_write_header = _warc_header; a->format_write_data = _warc_data; a->format_close = _warc_close; a->format_free = _warc_free; a->format_finish_entry = _warc_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_WARC; a->archive.archive_format_name = "WARC/1.0"; return (ARCHIVE_OK); } /* archive methods */ static int _warc_options(struct archive_write *a, const char *key, const char *val) { struct warc_s *w = a->format_data; if (strcmp(key, "omit-warcinfo") == 0) { if (val == NULL || strcmp(val, "true") == 0) { /* great */ w->omit_warcinfo = 1U; return (ARCHIVE_OK); } } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int _warc_header(struct archive_write *a, struct archive_entry *entry) { struct warc_s *w = a->format_data; struct archive_string hdr; #define MAX_HDR_SIZE 512 /* check whether warcinfo record needs outputting */ if (!w->omit_warcinfo) { ssize_t r; warc_essential_hdr_t wi = { WT_INFO, /*uri*/NULL, /*urn*/NULL, /*rtm*/0, /*mtm*/0, /*cty*/"application/warc-fields", /*len*/sizeof(warcinfo) - 1U, }; wi.rtime = w->now; wi.mtime = w->now; archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi); if (r >= 0) { /* jackpot! */ /* now also use HDR buffer for the actual warcinfo */ archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1); /* append end-of-record indicator */ archive_strncat(&hdr, "\r\n\r\n", 4); /* write to output stream */ __archive_write_output(a, hdr.s, archive_strlen(&hdr)); } /* indicate we're done with file header writing */ w->omit_warcinfo = 1U; archive_string_free(&hdr); } if (archive_entry_pathname(entry) == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } w->typ = archive_entry_filetype(entry); w->populz = 0U; if (w->typ == AE_IFREG) { warc_essential_hdr_t rh = { WT_RSRC, /*uri*/NULL, /*urn*/NULL, /*rtm*/0, /*mtm*/0, /*cty*/NULL, /*len*/0, }; ssize_t r; rh.tgturi = archive_entry_pathname(entry); rh.rtime = w->now; rh.mtime = archive_entry_mtime(entry); rh.cntlen = (size_t)archive_entry_size(entry); archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh); if (r < 0) { /* don't bother */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cannot archive file"); return (ARCHIVE_WARN); } /* otherwise append to output stream */ __archive_write_output(a, hdr.s, r); /* and let subsequent calls to _data() know about the size */ w->populz = rh.cntlen; archive_string_free(&hdr); return (ARCHIVE_OK); } /* just resort to erroring as per Tim's advice */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "WARC can only process regular files"); return (ARCHIVE_FAILED); } static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t len) { struct warc_s *w = a->format_data; if (w->typ == AE_IFREG) { int rc; /* never write more bytes than announced */ if (len > w->populz) { len = (size_t)w->populz; } /* now then, out we put the whole shebang */ rc = __archive_write_output(a, buf, len); if (rc != ARCHIVE_OK) { return rc; } } return len; } static int _warc_finish_entry(struct archive_write *a) { static const char _eor[] = "\r\n\r\n"; struct warc_s *w = a->format_data; if (w->typ == AE_IFREG) { int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U); if (rc != ARCHIVE_OK) { return rc; } } /* reset type info */ w->typ = 0; return (ARCHIVE_OK); } static int _warc_close(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int _warc_free(struct archive_write *a) { struct warc_s *w = a->format_data; free(w); a->format_data = NULL; return (ARCHIVE_OK); } /* private routines */ static void xstrftime(struct archive_string *as, const char *fmt, time_t t) { /** like strftime(3) but for time_t objects */ struct tm *rt; #if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) struct tm timeHere; #endif char strtime[100]; size_t len; #ifdef HAVE_GMTIME_R if ((rt = gmtime_r(&t, &timeHere)) == NULL) return; #elif defined(HAVE__GMTIME64_S) _gmtime64_s(&timeHere, &t); #else if ((rt = gmtime(&t)) == NULL) return; #endif /* leave the hard yacker to our role model strftime() */ len = strftime(strtime, sizeof(strtime)-1, fmt, rt); archive_strncat(as, strtime, len); } static ssize_t _popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr) { static const char _ver[] = "WARC/1.0\r\n"; - static const char *_typ[LAST_WT] = { + static const char * const _typ[LAST_WT] = { NULL, "warcinfo", "metadata", "resource", NULL }; char std_uuid[48U]; if (hdr.type == WT_NONE || hdr.type > WT_RSRC) { /* brilliant, how exactly did we get here? */ return -1; } archive_strcpy(tgt, _ver); archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]); if (hdr.tgturi != NULL) { /* check if there's a xyz:// */ static const char _uri[] = ""; static const char _fil[] = "file://"; const char *u; char *chk = strchr(hdr.tgturi, ':'); if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') { /* yep, it's definitely a URI */ u = _uri; } else { /* hm, best to prepend file:// then */ u = _fil; } archive_string_sprintf(tgt, "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi); } /* record time is usually when the http is sent off, * just treat the archive writing as such for a moment */ xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime); /* while we're at it, record the mtime */ xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime); if (hdr.recid == NULL) { /* generate one, grrrr */ warc_uuid_t u; _gen_uuid(&u); /* Unfortunately, archive_string_sprintf does not * handle the minimum number following '%'. * So we have to use snprintf function here instead * of archive_string_snprintf function. */ #if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900) #define snprintf _snprintf #endif snprintf( std_uuid, sizeof(std_uuid), "", u.u[0U], u.u[1U] >> 16U, u.u[1U] & 0xffffU, u.u[2U] >> 16U, u.u[2U] & 0xffffU, u.u[3U]); hdr.recid = std_uuid; } /* record-id is mandatory, fingers crossed we won't fail */ archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid); if (hdr.cnttyp != NULL) { archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp); } /* next one is mandatory */ archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen); /**/ archive_strncat(tgt, "\r\n", 2); return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt); } static int _gen_uuid(warc_uuid_t *tgt) { archive_random(tgt->u, sizeof(tgt->u)); /* obey uuid version 4 rules */ tgt->u[1U] &= 0xffff0fffU; tgt->u[1U] |= 0x4000U; tgt->u[2U] &= 0x3fffffffU; tgt->u[2U] |= 0x80000000U; return 0; } /* archive_write_set_format_warc.c ends here */ Index: stable/10/contrib/libarchive/libarchive/test/test_acl_platform_nfs4.c =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_acl_platform_nfs4.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/test/test_acl_platform_nfs4.c (revision 316338) @@ -1,894 +1,1058 @@ /*- * Copyright (c) 2003-2010 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(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); -#if HAVE_POSIX_ACL || HAVE_NFS4_ACL +#if ARCHIVE_ACL_NFS4 +#if HAVE_SYS_ACL_H #define _ACL_PRIVATE #include -#if HAVE_DARWIN_ACL -#include #endif +#if HAVE_SYS_RICHACL_H +#include #endif +#if HAVE_MEMBERSHIP_H +#include +#endif -#if HAVE_NFS4_ACL struct myacl_t { int type; int permset; int tag; int qual; /* GID or UID of user/group, depending on tag. */ const char *name; /* Name of user/group, depending on tag. */ }; static struct myacl_t acls_reg[] = { -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN /* For this test, we need the file owner to be able to read and write the ACL. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""}, #endif /* An entry for each type. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 108, "user108" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 109, "user109" }, /* An entry for each permission. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 112, "user112" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA, ARCHIVE_ENTRY_ACL_USER, 113, "user113" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_USER, 115, "user115" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_USER, 117, "user117" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_USER, 119, "user119" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_USER, 120, "user120" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER, 122, "user122" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER, 123, "user123" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE, ARCHIVE_ENTRY_ACL_USER, 124, "user124" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL, ARCHIVE_ENTRY_ACL_USER, 125, "user125" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_USER, 126, "user126" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_USER, 127, "user127" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER, 128, "user128" }, /* One entry for each qualifier. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 135, "user135" }, // { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, // ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" }, -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" } #else /* MacOS - mode 0654 */ { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" } #endif }; static const int acls_reg_cnt = (int)(sizeof(acls_reg)/sizeof(acls_reg[0])); static struct myacl_t acls_dir[] = { /* For this test, we need to be able to read and write the ACL. */ -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""}, #endif /* An entry for each type. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_USER, 101, "user101" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_USER, 102, "user102" }, /* An entry for each permission. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_USER, 201, "user201" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_USER, 202, "user202" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_USER, 203, "user203" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_USER, 204, "user204" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_USER, 205, "user205" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_USER, 206, "user206" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER, 207, "user207" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER, 208, "user208" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE, ARCHIVE_ENTRY_ACL_USER, 209, "user209" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL, ARCHIVE_ENTRY_ACL_USER, 210, "user210" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_USER, 211, "user211" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_USER, 212, "user212" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER, 213, "user213" }, /* One entry with each inheritance value. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_USER, 301, "user301" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_USER, 302, "user302" }, -#if 0 { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, - ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, + ARCHIVE_ENTRY_ACL_READ_DATA | + ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT | + ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_USER, 303, "user303" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, - ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, + ARCHIVE_ENTRY_ACL_READ_DATA | + ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT | + ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_USER, 304, "user304" }, +#if !defined(ARCHIVE_ACL_SUNOS_NFS4) || defined(ACE_INHERITED_ACE) { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ARCHIVE_ENTRY_ACL_USER, 305, "user305" }, #endif #if 0 /* FreeBSD does not support audit entries. */ { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ARCHIVE_ENTRY_ACL_USER, 401, "user401" }, { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ARCHIVE_ENTRY_ACL_USER, 402, "user402" }, #endif /* One entry for each qualifier. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_USER, 501, "user501" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_GROUP, 502, "group502" }, -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" } #else /* MacOS - mode 0654 */ { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" } #endif }; static const int acls_dir_cnt = (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); static void set_acls(struct archive_entry *ae, struct myacl_t *acls, int start, int end) { int i; archive_entry_acl_clear(ae); -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN if (start > 0) { assertEqualInt(ARCHIVE_OK, archive_entry_acl_add_entry(ae, acls[0].type, acls[0].permset, acls[0].tag, acls[0].qual, acls[0].name)); } #endif for (i = start; i < end; i++) { assertEqualInt(ARCHIVE_OK, archive_entry_acl_add_entry(ae, acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name)); } } static int -#ifdef HAVE_SUN_NFS4_ACL -acl_permset_to_bitmap(uint32_t a_access_mask) +#if ARCHIVE_ACL_SUNOS_NFS4 +acl_permset_to_bitmap(uint32_t mask) +#elif ARCHIVE_ACL_LIBRICHACL +acl_permset_to_bitmap(unsigned int mask) #else acl_permset_to_bitmap(acl_permset_t opaque_ps) #endif { - static struct { int machine; int portable; } perms[] = { -#ifdef HAVE_SUN_NFS4_ACL /* Solaris NFSv4 ACL permissions */ - {ACE_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE}, - {ACE_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA}, - {ACE_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY}, - {ACE_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA}, - {ACE_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE}, - {ACE_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA}, - {ACE_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY}, - {ACE_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS}, - {ACE_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS}, - {ACE_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD}, - {ACE_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES}, - {ACE_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES}, - {ACE_DELETE, ARCHIVE_ENTRY_ACL_DELETE}, - {ACE_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL}, - {ACE_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL}, - {ACE_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER}, - {ACE_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE} -#elif HAVE_DARWIN_ACL /* MacOS NFSv4 ACL permissions */ - {ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA}, - {ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY}, - {ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA}, - {ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE}, - {ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE}, - {ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE}, - {ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA}, - {ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY}, - {ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD}, - {ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES}, - {ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES}, - {ACL_READ_EXTATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS}, - {ACL_WRITE_EXTATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS}, - {ACL_READ_SECURITY, ARCHIVE_ENTRY_ACL_READ_ACL}, - {ACL_WRITE_SECURITY, ARCHIVE_ENTRY_ACL_WRITE_ACL}, - {ACL_CHANGE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER}, + static struct { int portable; int machine; } perms[] = { +#ifdef ARCHIVE_ACL_SUNOS_NFS4 /* Solaris NFSv4 ACL permissions */ + {ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE}, + {ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA}, + {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY}, + {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE}, + {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY}, + {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD}, + {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE}, + {ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER}, + {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE} +#elif ARCHIVE_ACL_DARWIN /* MacOS NFSv4 ACL permissions */ + {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, + {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, + {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, + {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, + {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, + {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY}, + {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY}, + {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER}, #if HAVE_DECL_ACL_SYNCHRONIZE - {ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}, + {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} #endif +#elif ARCHIVE_ACL_LIBRICHACL + {ARCHIVE_ENTRY_ACL_EXECUTE, RICHACE_EXECUTE}, + {ARCHIVE_ENTRY_ACL_READ_DATA, RICHACE_READ_DATA}, + {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, RICHACE_LIST_DIRECTORY}, + {ARCHIVE_ENTRY_ACL_WRITE_DATA, RICHACE_WRITE_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_FILE, RICHACE_ADD_FILE}, + {ARCHIVE_ENTRY_ACL_APPEND_DATA, RICHACE_APPEND_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, RICHACE_ADD_SUBDIRECTORY}, + {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, RICHACE_READ_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, RICHACE_WRITE_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_DELETE_CHILD, RICHACE_DELETE_CHILD}, + {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, RICHACE_READ_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, RICHACE_WRITE_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_DELETE, RICHACE_DELETE}, + {ARCHIVE_ENTRY_ACL_READ_ACL, RICHACE_READ_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_ACL, RICHACE_WRITE_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_OWNER, RICHACE_WRITE_OWNER}, + {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, RICHACE_SYNCHRONIZE} #else /* FreeBSD NFSv4 ACL permissions */ - {ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE}, - {ACL_WRITE, ARCHIVE_ENTRY_ACL_WRITE}, - {ACL_READ, ARCHIVE_ENTRY_ACL_READ}, - {ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA}, - {ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY}, - {ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA}, - {ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE}, - {ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA}, - {ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY}, - {ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS}, - {ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS}, - {ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD}, - {ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES}, - {ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES}, - {ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE}, - {ACL_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL}, - {ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL}, - {ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER}, - {ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE} + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, + {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, + {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, + {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, + {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, + {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, + {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, + {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} #endif }; int i, permset = 0; for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i) -#if HAVE_SUN_NFS4_ACL - if (a_access_mask & perms[i].machine) +#if ARCHIVE_ACL_SUNOS_NFS4 || ARCHIVE_ACL_LIBRICHACL + if (mask & perms[i].machine) #else if (acl_get_perm_np(opaque_ps, perms[i].machine)) #endif permset |= perms[i].portable; return permset; } static int -#if HAVE_SUN_NFS4_ACL -acl_flagset_to_bitmap(uint16_t a_flags) +#if ARCHIVE_ACL_SUNOS_NFS4 +acl_flagset_to_bitmap(uint16_t flags) +#elif ARCHIVE_ACL_LIBRICHACL +acl_flagset_to_bitmap(int flags) #else acl_flagset_to_bitmap(acl_flagset_t opaque_fs) #endif { - static struct { int machine; int portable; } flags[] = { -#if HAVE_SUN_NFS4_ACL /* Solaris NFSv4 ACL inheritance flags */ - {ACE_FILE_INHERIT_ACE, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT}, - {ACE_DIRECTORY_INHERIT_ACE, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT}, - {ACE_NO_PROPAGATE_INHERIT_ACE, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT}, - {ACE_INHERIT_ONLY_ACE, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY}, - {ACE_SUCCESSFUL_ACCESS_ACE_FLAG, ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS}, - {ACE_FAILED_ACCESS_ACE_FLAG, ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS}, + static struct { int portable; int machine; } perms[] = { +#if ARCHIVE_ACL_SUNOS_NFS4 /* Solaris NFSv4 ACL inheritance flags */ + {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG}, + {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG}, #ifdef ACE_INHERITED_ACE - {ACE_INHERITED_ACE, ARCHIVE_ENTRY_ACL_ENTRY_INHERITED} + {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE} #endif -#elif HAVE_DARWIN_ACL /* MacOS NFSv4 ACL inheritance flags */ - {ACL_ENTRY_INHERITED, ARCHIVE_ENTRY_ACL_ENTRY_INHERITED}, - {ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT}, - {ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT}, - {ACL_ENTRY_LIMIT_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT}, - {ACL_ENTRY_ONLY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY} +#elif ARCHIVE_ACL_DARWIN /* MacOS NFSv4 ACL inheritance flags */ + {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}, + {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT} +#elif ARCHIVE_ACL_LIBRICHACL + {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, RICHACE_FILE_INHERIT_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, RICHACE_DIRECTORY_INHERIT_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, RICHACE_NO_PROPAGATE_INHERIT_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, RICHACE_INHERIT_ONLY_ACE}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, RICHACE_INHERITED_ACE} #else /* FreeBSD NFSv4 ACL inheritance flags */ - {ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT}, - {ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT}, - {ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT}, - {ACL_ENTRY_SUCCESSFUL_ACCESS, ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS}, - {ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS}, - {ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}, + {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, + {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY} #endif }; int i, flagset = 0; - for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i) -#if HAVE_SUN_NFS4_ACL - if (a_flags & flags[i].machine) + for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i) +#if ARCHIVE_ACL_SUNOS_NFS4 || ARCHIVE_ACL_LIBRICHACL + if (flags & perms[i].machine) #else - if (acl_get_flag_np(opaque_fs, flags[i].machine)) + if (acl_get_flag_np(opaque_fs, perms[i].machine)) #endif - flagset |= flags[i].portable; + flagset |= perms[i].portable; return flagset; } +#if ARCHIVE_ACL_SUNOS_NFS4 static int -#if HAVE_SUN_NFS4_ACL acl_match(ace_t *ace, struct myacl_t *myacl) -#else -acl_match(acl_entry_t aclent, struct myacl_t *myacl) -#endif { -#if !HAVE_SUN_NFS4_ACL -#if HAVE_DARWIN_ACL - void *q; - uid_t ugid; - int r, idtype; -#else - gid_t g, *gp; - uid_t u, *up; - acl_entry_type_t entry_type; -#endif /* !HAVE_DARWIN_ACL */ - acl_tag_t tag_type; - acl_permset_t opaque_ps; - acl_flagset_t opaque_fs; -#endif /* !HAVE_SUN_NFS4_ACL */ int perms; -#if HAVE_SUN_NFS4_ACL perms = acl_permset_to_bitmap(ace->a_access_mask) | acl_flagset_to_bitmap(ace->a_flags); -#else - acl_get_tag_type(aclent, &tag_type); -#if !HAVE_DARWIN_ACL - acl_get_entry_type_np(aclent, &entry_type); -#endif - /* translate the silly opaque permset to a bitmap */ - acl_get_permset(aclent, &opaque_ps); - acl_get_flagset_np(aclent, &opaque_fs); - perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs); -#endif if (perms != myacl->permset) return (0); -#if HAVE_SUN_NFS4_ACL switch (ace->a_type) { case ACE_ACCESS_ALLOWED_ACE_TYPE: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW) return (0); break; case ACE_ACCESS_DENIED_ACE_TYPE: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY) return (0); break; case ACE_SYSTEM_AUDIT_ACE_TYPE: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_AUDIT) return (0); break; case ACE_SYSTEM_ALARM_ACE_TYPE: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALARM) return (0); break; default: return (0); } if (ace->a_flags & ACE_OWNER) { if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); } else if (ace->a_flags & ACE_GROUP) { if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); } else if (ace->a_flags & ACE_EVERYONE) { if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0); } else if (ace->a_flags & ACE_IDENTIFIER_GROUP) { if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); if ((gid_t)myacl->qual != ace->a_who) return (0); } else { if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); if ((uid_t)myacl->qual != ace->a_who) return (0); } -#elif HAVE_DARWIN_ACL + return (1); +} +#elif ARCHIVE_ACL_LIBRICHACL +static int +acl_match(struct richace *richace, struct myacl_t *myacl) +{ + int perms; + + perms = acl_permset_to_bitmap(richace->e_mask) | + acl_flagset_to_bitmap(richace->e_flags); + + if (perms != myacl->permset) + return (0); + + switch (richace->e_type) { + case RICHACE_ACCESS_ALLOWED_ACE_TYPE: + if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW) + return (0); + break; + case RICHACE_ACCESS_DENIED_ACE_TYPE: + if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY) + return (0); + break; + default: + return (0); + } + + if (richace->e_flags & RICHACE_SPECIAL_WHO) { + switch (richace->e_id) { + case RICHACE_OWNER_SPECIAL_ID: + if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) + return (0); + break; + case RICHACE_GROUP_SPECIAL_ID: + if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) + return (0); + break; + case RICHACE_EVERYONE_SPECIAL_ID: + if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) + return (0); + break; + default: + /* Invalid e_id */ + return (0); + } + } else if (richace->e_flags & RICHACE_IDENTIFIER_GROUP) { + if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) + return (0); + if ((gid_t)myacl->qual != richace->e_id) + return (0); + } else { + if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) + return (0); + if ((uid_t)myacl->qual != richace->e_id) + return (0); + } + return (1); +} +#elif ARCHIVE_ACL_DARWIN +static int +acl_match(acl_entry_t aclent, struct myacl_t *myacl) +{ + void *q; + uid_t ugid; + int r, idtype; + acl_tag_t tag_type; + acl_permset_t opaque_ps; + acl_flagset_t opaque_fs; + int perms; + + acl_get_tag_type(aclent, &tag_type); + + /* translate the silly opaque permset to a bitmap */ + acl_get_permset(aclent, &opaque_ps); + acl_get_flagset_np(aclent, &opaque_fs); + perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs); + if (perms != myacl->permset) + return (0); + r = 0; switch (tag_type) { case ACL_EXTENDED_ALLOW: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW) return (0); break; case ACL_EXTENDED_DENY: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY) return (0); break; default: return (0); } q = acl_get_qualifier(aclent); if (q == NULL) return (0); r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype); acl_free(q); if (r != 0) return (0); switch (idtype) { case ID_TYPE_UID: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); if ((uid_t)myacl->qual != ugid) return (0); break; case ID_TYPE_GID: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); if ((gid_t)myacl->qual != ugid) return (0); break; default: return (0); } -#else /* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */ + return (1); +} +#else /* ARCHIVE_ACL_FREEBSD_NFS4 */ +static int +acl_match(acl_entry_t aclent, struct myacl_t *myacl) +{ + gid_t g, *gp; + uid_t u, *up; + acl_entry_type_t entry_type; + acl_tag_t tag_type; + acl_permset_t opaque_ps; + acl_flagset_t opaque_fs; + int perms; + + acl_get_tag_type(aclent, &tag_type); + acl_get_entry_type_np(aclent, &entry_type); + + /* translate the silly opaque permset to a bitmap */ + acl_get_permset(aclent, &opaque_ps); + acl_get_flagset_np(aclent, &opaque_fs); + perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs); + if (perms != myacl->permset) + return (0); + switch (entry_type) { case ACL_ENTRY_TYPE_ALLOW: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW) return (0); break; case ACL_ENTRY_TYPE_DENY: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY) return (0); break; case ACL_ENTRY_TYPE_AUDIT: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_AUDIT) return (0); case ACL_ENTRY_TYPE_ALARM: if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALARM) return (0); default: return (0); } switch (tag_type) { case ACL_USER_OBJ: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); break; case ACL_USER: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); up = acl_get_qualifier(aclent); u = *up; acl_free(up); if ((uid_t)myacl->qual != u) return (0); break; case ACL_GROUP_OBJ: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); break; case ACL_GROUP: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); gp = acl_get_qualifier(aclent); g = *gp; acl_free(gp); if ((gid_t)myacl->qual != g) return (0); break; case ACL_MASK: if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); break; case ACL_EVERYONE: if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0); break; } -#endif /* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */ return (1); } +#endif /* various ARCHIVE_ACL_NFS4 implementations */ static void compare_acls( -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 void *aclp, int aclcnt, +#elif ARCHIVE_ACL_LIBRICHACL + struct richacl *richacl, #else acl_t acl, #endif struct myacl_t *myacls, const char *filename, int start, int end) { int *marker; int matched; int i, n; -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 int e; ace_t *acl_entry; +#elif ARCHIVE_ACL_LIBRICHACL + int e; + struct richace *acl_entry; + int aclcnt; #else int entry_id = ACL_FIRST_ENTRY; acl_entry_t acl_entry; +#if ARCHIVE_ACL_DARWIN + const int acl_get_entry_ret = 0; +#else + const int acl_get_entry_ret = 1; #endif +#endif +#if ARCHIVE_ACL_SUNOS_NFS4 + if (aclp == NULL) + return; +#elif ARCHIVE_ACL_LIBRICHACL + if (richacl == NULL) + return; + aclcnt = richacl->a_count; +#else + if (acl == NULL) + return; +#endif + n = end - start; marker = malloc(sizeof(marker[0]) * (n + 1)); for (i = 0; i < n; i++) marker[i] = i + start; -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN /* Always include the first ACE. */ if (start > 0) { marker[n] = 0; ++n; } #endif /* * Iterate over acls in system acl object, try to match each * one with an item in the myacls array. */ -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 || ARCHIVE_ACL_LIBRICHACL for (e = 0; e < aclcnt; e++) -#elif HAVE_DARWIN_ACL - while (0 == acl_get_entry(acl, entry_id, &acl_entry)) #else - while (1 == acl_get_entry(acl, entry_id, &acl_entry)) + while (acl_get_entry_ret == acl_get_entry(acl, entry_id, &acl_entry)) #endif { -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 acl_entry = &((ace_t *)aclp)[e]; +#elif ARCHIVE_ACL_LIBRICHACL + acl_entry = &(richacl->a_entries[e]); #else /* After the first time... */ entry_id = ACL_NEXT_ENTRY; #endif /* Search for a matching entry (tag and qualifier) */ for (i = 0, matched = 0; i < n && !matched; i++) { if (acl_match(acl_entry, &myacls[marker[i]])) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } failure("ACL entry on file %s that shouldn't be there", filename); assert(matched == 1); } /* Dump entries in the myacls array that weren't in the system acl. */ for (i = 0; i < n; ++i) { failure(" ACL entry %d missing from %s: " "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n", marker[i], filename, myacls[marker[i]].type, myacls[marker[i]].permset, myacls[marker[i]].tag, myacls[marker[i]].qual, myacls[marker[i]].name); assert(0); /* Record this as a failure. */ } free(marker); } static void compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char *filename, int start, int end) { int *marker; int matched; int i, n; int type, permset, tag, qual; const char *name; /* Count ACL entries in myacls array and allocate an indirect array. */ n = end - start; marker = malloc(sizeof(marker[0]) * (n + 1)); for (i = 0; i < n; i++) marker[i] = i + start; /* Always include the first ACE. */ if (start > 0) { marker[n] = 0; ++n; } /* * Iterate over acls in entry, try to match each * one with an item in the myacls array. */ assertEqualInt(n, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); while (ARCHIVE_OK == archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4, &type, &permset, &tag, &qual, &name)) { /* Search for a matching entry (tag and qualifier) */ for (i = 0, matched = 0; i < n && !matched; i++) { if (tag == myacls[marker[i]].tag && qual == myacls[marker[i]].qual && permset == myacls[marker[i]].permset && type == myacls[marker[i]].type) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } failure("ACL entry on file that shouldn't be there: " "type=%#010x,permset=%#010x,tag=%d,qual=%d", type,permset,tag,qual); assert(matched == 1); } /* Dump entries in the myacls array that weren't in the system acl. */ for (i = 0; i < n; ++i) { failure(" ACL entry %d missing from %s: " "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n", marker[i], filename, myacls[marker[i]].type, myacls[marker[i]].permset, myacls[marker[i]].tag, myacls[marker[i]].qual, myacls[marker[i]].name); assert(0); /* Record this as a failure. */ } free(marker); } -#endif /* HAVE_NFS4_ACL */ +#endif /* ARCHIVE_ACL_NFS4 */ /* * Verify ACL restore-to-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_nfs4) { -#if !HAVE_NFS4_ACL +#if !ARCHIVE_ACL_NFS4 skipping("NFS4 ACLs are not supported on this platform"); -#else +#else /* ARCHIVE_ACL_NFS4 */ char buff[64]; int i; struct stat st; struct archive *a; struct archive_entry *ae; -#if HAVE_DARWIN_ACL /* On MacOS we skip trivial ACLs in some tests */ +#if ARCHIVE_ACL_DARWIN /* On MacOS we skip trivial ACLs in some tests */ const int regcnt = acls_reg_cnt - 4; const int dircnt = acls_dir_cnt - 4; #else const int regcnt = acls_reg_cnt; const int dircnt = acls_dir_cnt; #endif -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 void *aclp; int aclcnt; -#else /* !HAVE_SUN_NFS4_ACL */ +#elif ARCHIVE_ACL_LIBRICHACL + struct richacl *richacl; +#else /* !ARCHIVE_ACL_SUNOS_NFS4 */ acl_t acl; #endif assertMakeFile("pretest", 0644, "a"); if (setTestAcl("pretest") != ARCHIVE_TEST_ACL_TYPE_NFS4) { skipping("NFS4 ACLs are not writable on this filesystem"); return; } /* Create a write-to-disk object. */ assert(NULL != (a = archive_write_disk_new())); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL); /* Populate an archive entry with some metadata, including ACL info */ ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "testall"); archive_entry_set_filetype(ae, AE_IFREG); archive_entry_set_perm(ae, 0654); archive_entry_set_mtime(ae, 123456, 7890); archive_entry_set_size(ae, 0); set_acls(ae, acls_reg, 0, acls_reg_cnt); /* Write the entry to disk, including ACLs. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); /* Likewise for a dir. */ archive_entry_set_pathname(ae, "dirall"); archive_entry_set_filetype(ae, AE_IFDIR); archive_entry_set_perm(ae, 0654); archive_entry_set_mtime(ae, 123456, 7890); set_acls(ae, acls_dir, 0, acls_dir_cnt); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); for (i = 0; i < acls_dir_cnt; ++i) { sprintf(buff, "dir%d", i); archive_entry_set_pathname(ae, buff); archive_entry_set_filetype(ae, AE_IFDIR); archive_entry_set_perm(ae, 0654); archive_entry_set_mtime(ae, 123456 + i, 7891 + i); set_acls(ae, acls_dir, i, i + 1); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); } archive_entry_free(ae); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Verify the data on disk. */ assertEqualInt(0, stat("testall", &st)); assertEqualInt(st.st_mtime, 123456); -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, "testall"); - failure("acl(): errno = %d (%s)", errno, strerror(errno)); + failure("acl(\"%s\"): errno = %d (%s)", "testall", errno, + strerror(errno)); assert(aclp != NULL); +#elif ARCHIVE_ACL_LIBRICHACL + richacl = richacl_get_file("testall"); + failure("richacl_get_file(\"%s\"): errno = %d (%s)", "testall", errno, + strerror(errno)); + assert(richacl != NULL); #else -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN acl = acl_get_file("testall", ACL_TYPE_EXTENDED); #else acl = acl_get_file("testall", ACL_TYPE_NFS4); #endif - failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno)); + failure("acl_get_file(\"%s\"): errno = %d (%s)", "testall", errno, + strerror(errno)); assert(acl != (acl_t)NULL); #endif -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 compare_acls(aclp, aclcnt, acls_reg, "testall", 0, regcnt); free(aclp); aclp = NULL; +#elif ARCHIVE_ACL_LIBRICHACL + compare_acls(richacl, acls_reg, "testall", 0, regcnt); + richacl_free(richacl); #else compare_acls(acl, acls_reg, "testall", 0, regcnt); acl_free(acl); #endif /* Verify single-permission dirs on disk. */ for (i = 0; i < dircnt; ++i) { sprintf(buff, "dir%d", i); assertEqualInt(0, stat(buff, &st)); assertEqualInt(st.st_mtime, 123456 + i); -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, buff); - failure("acl(): errno = %d (%s)", errno, strerror(errno)); + failure("acl(\"%s\"): errno = %d (%s)", buff, errno, + strerror(errno)); assert(aclp != NULL); +#elif ARCHIVE_ACL_LIBRICHACL + richacl = richacl_get_file(buff); + /* First and last two dir do not return a richacl */ + if ((i == 0 || i >= dircnt - 2) && richacl == NULL && + errno == ENODATA) + continue; + failure("richacl_get_file(\"%s\"): errno = %d (%s)", buff, + errno, strerror(errno)); + assert(richacl != NULL); #else -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN acl = acl_get_file(buff, ACL_TYPE_EXTENDED); #else acl = acl_get_file(buff, ACL_TYPE_NFS4); #endif - failure("acl_get_file(): errno = %d (%s)", errno, + failure("acl_get_file(\"%s\"): errno = %d (%s)", buff, errno, strerror(errno)); assert(acl != (acl_t)NULL); #endif -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 compare_acls(aclp, aclcnt, acls_dir, buff, i, i + 1); free(aclp); aclp = NULL; +#elif ARCHIVE_ACL_LIBRICHACL + compare_acls(richacl, acls_dir, buff, i, i + 1); + richacl_free(richacl); #else compare_acls(acl, acls_dir, buff, i, i + 1); acl_free(acl); #endif } /* Verify "dirall" on disk. */ assertEqualInt(0, stat("dirall", &st)); assertEqualInt(st.st_mtime, 123456); -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, "dirall"); - failure("acl(): errno = %d (%s)", errno, strerror(errno)); + failure("acl(\"%s\"): errno = %d (%s)", "dirall", errno, + strerror(errno)); assert(aclp != NULL); +#elif ARCHIVE_ACL_LIBRICHACL + richacl = richacl_get_file("dirall"); + failure("richacl_get_file(\"%s\"): errno = %d (%s)", "dirall", + errno, strerror(errno)); + assert(richacl != NULL); #else -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN acl = acl_get_file("dirall", ACL_TYPE_EXTENDED); #else acl = acl_get_file("dirall", ACL_TYPE_NFS4); #endif - failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno)); + failure("acl_get_file(\"%s\"): errno = %d (%s)", "dirall", errno, + strerror(errno)); assert(acl != (acl_t)NULL); #endif -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 compare_acls(aclp, aclcnt, acls_dir, "dirall", 0, dircnt); free(aclp); aclp = NULL; +#elif ARCHIVE_ACL_LIBRICHACL + compare_acls(richacl, acls_dir, "dirall", 0, dircnt); + richacl_free(richacl); #else compare_acls(acl, acls_dir, "dirall", 0, dircnt); acl_free(acl); #endif /* Read and compare ACL via archive_read_disk */ a = archive_read_disk_new(); assert(a != NULL); ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "testall"); assertEqualInt(ARCHIVE_OK, archive_read_disk_entry_from_file(a, ae, -1, NULL)); compare_entry_acls(ae, acls_reg, "testall", 0, acls_reg_cnt); archive_entry_free(ae); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* Read and compare ACL via archive_read_disk */ a = archive_read_disk_new(); assert(a != NULL); ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "dirall"); assertEqualInt(ARCHIVE_OK, archive_read_disk_entry_from_file(a, ae, -1, NULL)); compare_entry_acls(ae, acls_dir, "dirall", 0, acls_dir_cnt); archive_entry_free(ae); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); -#endif /* HAVE_NFS4_ACL */ +#endif /* ARCHIVE_ACL_NFS4 */ } Index: stable/10/contrib/libarchive/libarchive/test/test_acl_platform_posix1e.c =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_acl_platform_posix1e.c (revision 316337) +++ stable/10/contrib/libarchive/libarchive/test/test_acl_platform_posix1e.c (revision 316338) @@ -1,614 +1,606 @@ /*- * Copyright (c) 2003-2008 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(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $"); -#if HAVE_POSIX_ACL || HAVE_SUN_ACL +#if ARCHIVE_ACL_POSIX1E #include #if HAVE_ACL_GET_PERM #include #define ACL_GET_PERM acl_get_perm #elif HAVE_ACL_GET_PERM_NP #define ACL_GET_PERM acl_get_perm_np #endif static struct archive_test_acl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_MASK, -1, "" }, }; static int -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS acl_entry_get_perm(aclent_t *aclent) #else acl_entry_get_perm(acl_entry_t aclent) #endif { int permset = 0; -#if HAVE_POSIX_ACL +#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL acl_permset_t opaque_ps; #endif -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS if (aclent->a_perm & 1) permset |= ARCHIVE_ENTRY_ACL_EXECUTE; if (aclent->a_perm & 2) permset |= ARCHIVE_ENTRY_ACL_WRITE; if (aclent->a_perm & 4) permset |= ARCHIVE_ENTRY_ACL_READ; #else /* translate the silly opaque permset to a bitmap */ acl_get_permset(aclent, &opaque_ps); if (ACL_GET_PERM(opaque_ps, ACL_EXECUTE)) permset |= ARCHIVE_ENTRY_ACL_EXECUTE; if (ACL_GET_PERM(opaque_ps, ACL_WRITE)) permset |= ARCHIVE_ENTRY_ACL_WRITE; if (ACL_GET_PERM(opaque_ps, ACL_READ)) permset |= ARCHIVE_ENTRY_ACL_READ; #endif return permset; } #if 0 static int acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) { int entry_id = ACL_FIRST_ENTRY; acl_entry_t acl_entry; acl_tag_t acl_tag_type; while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { /* After the first time... */ entry_id = ACL_NEXT_ENTRY; /* If this matches, return perm mask */ acl_get_tag_type(acl_entry, &acl_tag_type); if (acl_tag_type == requested_tag_type) { switch (acl_tag_type) { case ACL_USER_OBJ: if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) { return acl_entry_get_perm(acl_entry); } break; case ACL_GROUP_OBJ: if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) { return acl_entry_get_perm(acl_entry); } break; case ACL_USER: case ACL_GROUP: case ACL_OTHER: return acl_entry_get_perm(acl_entry); default: failure("Unexpected ACL tag type"); assert(0); } } } return -1; } #endif +#if ARCHIVE_ACL_SUNOS static int -#if HAVE_SUN_ACL acl_match(aclent_t *aclent, struct archive_test_acl_t *myacl) -#else +{ + + if (myacl->permset != acl_entry_get_perm(aclent)) + return (0); + + switch (aclent->a_type) { + case DEF_USER_OBJ: + case USER_OBJ: + if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); + break; + if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) + return (0); + if ((uid_t)myacl->qual != aclent->a_id) + return (0); + break; + case DEF_GROUP_OBJ: + case GROUP_OBJ: + if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); + break; + case DEF_GROUP: + case GROUP: + if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) + return (0); + if ((gid_t)myacl->qual != aclent->a_id) + return (0); + break; + case DEF_CLASS_OBJ: + case CLASS_OBJ: + if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); + break; + case DEF_OTHER_OBJ: + case OTHER_OBJ: + if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0); + break; + } + return (1); +} + +#else /* ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL */ +static int acl_match(acl_entry_t aclent, struct archive_test_acl_t *myacl) -#endif { -#if HAVE_POSIX_ACL gid_t g, *gp; uid_t u, *up; acl_tag_t tag_type; -#endif if (myacl->permset != acl_entry_get_perm(aclent)) return (0); -#if HAVE_SUN_ACL - switch (aclent->a_type) -#else acl_get_tag_type(aclent, &tag_type); - switch (tag_type) -#endif - { -#if HAVE_SUN_ACL - case DEF_USER_OBJ: - case USER_OBJ: -#else + switch (tag_type) { case ACL_USER_OBJ: -#endif if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); break; -#if HAVE_SUN_ACL - case DEF_USER: - case USER: -#else case ACL_USER: -#endif if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); -#if HAVE_SUN_ACL - if ((uid_t)myacl->qual != aclent->a_id) - return (0); -#else up = acl_get_qualifier(aclent); u = *up; acl_free(up); if ((uid_t)myacl->qual != u) return (0); -#endif break; -#if HAVE_SUN_ACL - case DEF_GROUP_OBJ: - case GROUP_OBJ: -#else case ACL_GROUP_OBJ: -#endif if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); break; -#if HAVE_SUN_ACL - case DEF_GROUP: - case GROUP: -#else case ACL_GROUP: -#endif if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); -#if HAVE_SUN_ACL - if ((gid_t)myacl->qual != aclent->a_id) - return (0); -#else gp = acl_get_qualifier(aclent); g = *gp; acl_free(gp); if ((gid_t)myacl->qual != g) return (0); -#endif break; -#if HAVE_SUN_ACL - case DEF_CLASS_OBJ: - case CLASS_OBJ: -#else case ACL_MASK: -#endif if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); break; -#if HAVE_SUN_ACL - case DEF_OTHER_OBJ: - case OTHER_OBJ: -#else case ACL_OTHER: -#endif if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0); break; } return (1); } +#endif static void -#if HAVE_SUN_ACL -compare_acls(void *aclp, int aclcnt, struct archive_test_acl_t *myacls, int n) +compare_acls( +#if ARCHIVE_ACL_SUNOS + void *aclp, int aclcnt, #else -compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n) + acl_t acl, #endif + struct archive_test_acl_t *myacls, int n) { int *marker; int matched; int i; -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS int e; aclent_t *acl_entry; #else int entry_id = ACL_FIRST_ENTRY; acl_entry_t acl_entry; #endif /* Count ACL entries in myacls array and allocate an indirect array. */ marker = malloc(sizeof(marker[0]) * n); if (marker == NULL) return; for (i = 0; i < n; i++) marker[i] = i; /* * Iterate over acls in system acl object, try to match each * one with an item in the myacls array. */ -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS for(e = 0; e < aclcnt; e++) { acl_entry = &((aclent_t *)aclp)[e]; #else while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { /* After the first time... */ entry_id = ACL_NEXT_ENTRY; #endif /* Search for a matching entry (tag and qualifier) */ for (i = 0, matched = 0; i < n && !matched; i++) { if (acl_match(acl_entry, &myacls[marker[i]])) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } /* TODO: Print out more details in this case. */ failure("ACL entry on file that shouldn't be there"); assert(matched == 1); } /* Dump entries in the myacls array that weren't in the system acl. */ for (i = 0; i < n; ++i) { failure(" ACL entry missing from file: " "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n", myacls[marker[i]].type, myacls[marker[i]].permset, myacls[marker[i]].tag, myacls[marker[i]].qual, myacls[marker[i]].name); assert(0); /* Record this as a failure. */ } free(marker); } - #endif - /* * Verify ACL restore-to-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_posix1e_restore) { -#if !HAVE_SUN_ACL && !HAVE_POSIX_ACL +#if !ARCHIVE_ACL_POSIX1E skipping("POSIX.1e ACLs are not supported on this platform"); -#else /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ +#else /* ARCHIVE_ACL_POSIX1E */ struct stat st; struct archive *a; struct archive_entry *ae; -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS void *aclp; int aclcnt; #else acl_t acl; #endif assertMakeFile("pretest", 0644, "a"); if (setTestAcl("pretest") != ARCHIVE_TEST_ACL_TYPE_POSIX1E) { skipping("POSIX.1e ACLs are not writable on this filesystem"); return; } /* Create a write-to-disk object. */ assert(NULL != (a = archive_write_disk_new())); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL); /* Populate an archive entry with some metadata, including ACL info */ ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "test0"); archive_entry_set_mtime(ae, 123456, 7890); archive_entry_set_size(ae, 0); assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Verify the data on disk. */ assertEqualInt(0, stat("test0", &st)); assertEqualInt(st.st_mtime, 123456); -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS aclp = sunacl_get(GETACL, &aclcnt, 0, "test0"); failure("acl(): errno = %d (%s)", errno, strerror(errno)); assert(aclp != NULL); #else acl = acl_get_file("test0", ACL_TYPE_ACCESS); failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno)); assert(acl != (acl_t)NULL); #endif -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS compare_acls(aclp, aclcnt, acls2, sizeof(acls2)/sizeof(acls2[0])); free(aclp); aclp = NULL; #else compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0])); acl_free(acl); #endif -#endif /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ +#endif /* ARCHIVE_ACL_POSIX1E */ } /* * Verify ACL read-from-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_posix1e_read) { -#if !HAVE_SUN_ACL && !HAVE_POSIX_ACL +#if !ARCHIVE_ACL_POSIX1E skipping("POSIX.1e ACLs are not supported on this platform"); -#else +#else /* ARCHIVE_ACL_POSIX1E */ struct archive *a; struct archive_entry *ae; int n, fd, flags, dflags; char *func, *acl_text; const char *acl1_text, *acl2_text, *acl3_text; -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS void *aclp; int aclcnt; #else acl_t acl1, acl2, acl3; #endif /* * Manually construct a directory and two files with * different ACLs. This also serves to verify that ACLs * are supported on the local filesystem. */ /* Create a test file f1 with acl1 */ -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS acl1_text = "user::rwx," "group::rwx," "other:rwx," "user:1:rw-," "group:15:r-x," "mask:rwx"; aclent_t aclp1[] = { { USER_OBJ, -1, 4 | 2 | 1 }, { USER, 1, 4 | 2 }, { GROUP_OBJ, -1, 4 | 2 | 1 }, { GROUP, 15, 4 | 1 }, { CLASS_OBJ, -1, 4 | 2 | 1 }, { OTHER_OBJ, -1, 4 | 2 | 1 } }; #else acl1_text = "user::rwx\n" "group::rwx\n" "other::rwx\n" "user:1:rw-\n" "group:15:r-x\n" "mask::rwx"; acl1 = acl_from_text(acl1_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl1 != NULL); #endif fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { -#if !HAVE_SUN_ACL +#if !ARCHIVE_ACL_SUNOS acl_free(acl1); #endif return; } -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS /* Check if Solaris filesystem supports POSIX.1e ACLs */ aclp = sunacl_get(GETACL, &aclcnt, fd, NULL); if (aclp == 0) close(fd); if (errno == ENOSYS || errno == ENOTSUP) { skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } failure("facl(): errno = %d (%s)", errno, strerror(errno)); assert(aclp != NULL); func = "facl()"; n = facl(fd, SETACL, (int)(sizeof(aclp1)/sizeof(aclp1[0])), aclp1); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl1); #endif -#if !HAVE_SUN_ACL +#if !ARCHIVE_ACL_SUNOS acl_free(acl1); #endif if (n != 0) { -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS if (errno == ENOSYS || errno == ENOTSUP) #else if (errno == EOPNOTSUPP || errno == EINVAL) #endif { close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } } failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); close(fd); assertMakeDir("d", 0700); /* * Create file d/f1 with acl2 * * This differs from acl1 in the u:1: and g:15: permissions. * * This file deliberately has the same name but a different ACL. * Github Issue #777 explains how libarchive's directory traversal * did not always correctly enter directories before attempting * to read ACLs, resulting in reading the ACL from a like-named * file in the wrong directory. */ -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS acl2_text = "user::rwx," "group::rwx," "other:---," "user:1:r--," "group:15:r--," "mask:rwx"; aclent_t aclp2[] = { { USER_OBJ, -1, 4 | 2 | 1 }, { USER, 1, 4 }, { GROUP_OBJ, -1, 4 | 2 | 1}, { GROUP, 15, 4 }, { CLASS_OBJ, -1, 4 | 2 | 1}, { OTHER_OBJ, -1, 0 } }; #else acl2_text = "user::rwx\n" "group::rwx\n" "other::---\n" "user:1:r--\n" "group:15:r--\n" "mask::rwx"; acl2 = acl_from_text(acl2_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl2 != NULL); #endif fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { -#if !HAVE_SUN_ACL +#if !ARCHIVE_ACL_SUNOS acl_free(acl2); #endif return; } -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS func = "facl()"; n = facl(fd, SETACL, (int)(sizeof(aclp2) / sizeof(aclp2[0])), aclp2); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl2); acl_free(acl2); #endif if (n != 0) close(fd); failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); close(fd); /* Create nested directory d2 with default ACLs */ assertMakeDir("d/d2", 0755); -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS acl3_text = "user::rwx," "group::r-x," "other:r-x," "user:2:r--," "group:16:-w-," "mask:rwx," "default:user::rwx," "default:user:1:r--," "default:group::r-x," "default:group:15:r--," "default:mask:rwx," "default:other:r-x"; aclent_t aclp3[] = { { USER_OBJ, -1, 4 | 2 | 1 }, { USER, 2, 4 }, { GROUP_OBJ, -1, 4 | 1 }, { GROUP, 16, 2 }, { CLASS_OBJ, -1, 4 | 2 | 1 }, { OTHER_OBJ, -1, 4 | 1 }, { USER_OBJ | ACL_DEFAULT, -1, 4 | 2 | 1 }, { USER | ACL_DEFAULT, 1, 4 }, { GROUP_OBJ | ACL_DEFAULT, -1, 4 | 1 }, { GROUP | ACL_DEFAULT, 15, 4 }, { CLASS_OBJ | ACL_DEFAULT, -1, 4 | 2 | 1}, { OTHER_OBJ | ACL_DEFAULT, -1, 4 | 1 } }; #else acl3_text = "user::rwx\n" "user:1:r--\n" "group::r-x\n" "group:15:r--\n" "mask::rwx\n" "other::r-x"; acl3 = acl_from_text(acl3_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl3 != NULL); #endif -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS func = "acl()"; n = acl("d/d2", SETACL, (int)(sizeof(aclp3) / sizeof(aclp3[0])), aclp3); #else func = "acl_set_file()"; n = acl_set_file("d/d2", ACL_TYPE_DEFAULT, acl3); acl_free(acl3); #endif failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); /* Create a read-from-disk object. */ assert(NULL != (a = archive_read_disk_new())); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); assert(NULL != (ae = archive_entry_new())); -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS flags = ARCHIVE_ENTRY_ACL_TYPE_POSIX1E | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS; dflags = flags; #else flags = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; dflags = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; #endif /* Walk the dir until we see both of the files */ while (ARCHIVE_OK == archive_read_next_header2(a, ae)) { archive_read_disk_descend(a); if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, flags); assertEqualString(acl_text, acl1_text); free(acl_text); } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, flags); assertEqualString(acl_text, acl2_text); free(acl_text); } else if (strcmp(archive_entry_pathname(ae), "./d/d2") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, dflags); assertEqualString(acl_text, acl3_text); free(acl_text); } } archive_entry_free(ae); assertEqualInt(ARCHIVE_OK, archive_free(a)); -#endif +#endif /* ARCHIVE_ACL_POSIX1E */ } Index: stable/10/contrib/libarchive/libarchive/test/test_xattr_platform.c =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_xattr_platform.c (nonexistent) +++ stable/10/contrib/libarchive/libarchive/test/test_xattr_platform.c (revision 316338) @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2003-2010 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(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_xattr_platform) +{ +#if !ARCHIVE_XATTR_SUPPORT + skipping("Extended attributes are not supported on this platform"); +#else /* ARCHIVE_XATTR_SUPPORT */ + struct archive *a; + struct archive_entry *ae; + const char *name; + const void *value; + size_t size, insize; + int e, r; + const char *attrname = "user.libarchive.test"; + const char *readval = "readval"; + const char *writeval = "writeval"; + + assertMakeFile("readtest", 0644, "a"); + + if (!setXattr("readtest", attrname, readval, strlen(readval) + 1)) { + skipping("Extended attributes are not supported on this " + "filesystem"); + return; + } + + /* Read test */ + assert(NULL != (a = archive_read_disk_new())); + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "readtest"); + assertEqualInt(ARCHIVE_OK, + archive_read_disk_entry_from_file(a, ae, -1, NULL)); + e = archive_entry_xattr_reset(ae); + assert(e > 0); + + r = 0; + while (archive_entry_xattr_next(ae, &name, &value, + &size) == ARCHIVE_OK) { + if (name != NULL && value != NULL && size > 0 && + strcmp(name, attrname) == 0) { + failure("Attribute value does not match"); + assertEqualString((const char *)value, readval); + r = 1; + break; + } + } + failure("Attribute not found: %s", attrname); + assertEqualInt(r, 1); + + archive_entry_free(ae); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + assert(NULL != (a = archive_write_disk_new())); + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME | + ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_XATTR); + + /* Write test */ + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "writetest"); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_perm(ae, 0654); + archive_entry_set_mtime(ae, 123456, 7890); + archive_entry_set_size(ae, 0); + archive_entry_xattr_add_entry(ae, attrname, writeval, + strlen(writeval) + 1); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + value = getXattr("writetest", attrname, &insize); + if (assertEqualInt(insize, strlen(writeval) + 1) != 0) + assertEqualMem(value, writeval, insize); +#endif +} Property changes on: stable/10/contrib/libarchive/libarchive/test/test_xattr_platform.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/tar/bsdtar.1 =================================================================== --- stable/10/contrib/libarchive/tar/bsdtar.1 (revision 316337) +++ stable/10/contrib/libarchive/tar/bsdtar.1 (revision 316338) @@ -1,1242 +1,1242 @@ .\" 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 February 25, 2017 .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 Mac OS X) or if +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. .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 fflags (c, r, u, x modes only) Archive or extract 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, unlike other .Nm tar implementations, this 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, unlike other .Nm tar implementations, this 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. .It Fl Fl lz4 (c mode only) Compress the archive with lz4-compatible compression before writing it. In input mode, this option is ignored; lz4 compression is recognized automatically on input. .It Fl Fl lzma (c mode only) Compress the resulting archive with the original LZMA algorithm. Use of this option is discouraged and new archives should be created with .Fl Fl xz instead. Note that, unlike other .Nm tar implementations, this 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. .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 .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 (c, r, u modes only) Do not recursively archive the contents 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 +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 also in c, r and u modes). +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 .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 .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). 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 .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 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 atributes and extended file flags, if available, for each item +extended attributes and extended file flags, if available, for each item extracted from the archive. This is te 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 .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 .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, unlike other .Nm tar implementations, this 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, unlike other .Nm tar implementations, this 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, unlike other .Nm tar implementations, this 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/contrib/libarchive/tar/test/test_option_acls.c =================================================================== --- stable/10/contrib/libarchive/tar/test/test_option_acls.c (revision 316337) +++ stable/10/contrib/libarchive/tar/test/test_option_acls.c (revision 316338) @@ -1,471 +1,508 @@ /*- * 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(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); -#if HAVE_POSIX_ACL || HAVE_DARWIN_ACL +#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL static const acl_perm_t acl_perms[] = { -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN ACL_READ_DATA, ACL_LIST_DIRECTORY, ACL_WRITE_DATA, ACL_ADD_FILE, ACL_EXECUTE, ACL_SEARCH, ACL_DELETE, ACL_APPEND_DATA, ACL_ADD_SUBDIRECTORY, ACL_DELETE_CHILD, ACL_READ_ATTRIBUTES, ACL_WRITE_ATTRIBUTES, ACL_READ_EXTATTRIBUTES, ACL_WRITE_EXTATTRIBUTES, ACL_READ_SECURITY, ACL_WRITE_SECURITY, ACL_CHANGE_OWNER, ACL_SYNCHRONIZE -#else /* !HAVE_DARWIN_ACL */ +#else /* !ARCHIVE_ACL_DARWIN */ ACL_EXECUTE, ACL_WRITE, ACL_READ, -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 ACL_READ_DATA, ACL_LIST_DIRECTORY, ACL_WRITE_DATA, ACL_ADD_FILE, ACL_APPEND_DATA, ACL_ADD_SUBDIRECTORY, ACL_READ_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS, ACL_DELETE_CHILD, ACL_READ_ATTRIBUTES, ACL_WRITE_ATTRIBUTES, ACL_DELETE, ACL_READ_ACL, ACL_WRITE_ACL, ACL_WRITE_OWNER, ACL_SYNCHRONIZE -#endif /* HAVE_FREEBSD_NFS4_ACL */ -#endif /* !HAVE_DARWIN_ACL */ +#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ +#endif /* !ARCHIVE_ACL_DARWIN */ }; -#if HAVE_DARWIN_ACL || HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 static const acl_flag_t acl_flags[] = { -#if HAVE_DARWIN_ACL - ACL_FLAG_DEFER_INHERIT, - ACL_FLAG_NO_INHERIT, +#if ARCHIVE_ACL_DARWIN ACL_ENTRY_INHERITED, ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_LIMIT_INHERIT, ACL_ENTRY_ONLY_INHERIT -#else /* HAVE_FREEBSD_NFS4_ACL */ +#else /* ARCHIVE_ACL_FREEBSD_NFS4 */ ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_INHERITED -#endif /* HAVE_FREEBSD_NFS4_ACL */ +#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ }; -#endif /* HAVE_DARWIN_ACL || HAVE_FREEBSD_NFS4_ACL */ +#endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 */ /* * Compare two ACL entries on FreeBSD or on Mac OS X */ static int compare_acl_entry(acl_entry_t ae_a, acl_entry_t ae_b, int is_nfs4) { acl_tag_t tag_a, tag_b; acl_permset_t permset_a, permset_b; int perm_a, perm_b, perm_start, perm_end; void *qual_a, *qual_b; -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 acl_entry_type_t type_a, type_b; #endif -#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN acl_flagset_t flagset_a, flagset_b; int flag_a, flag_b; #endif int i, r; /* Compare ACL tag */ r = acl_get_tag_type(ae_a, &tag_a); failure("acl_get_tag_type() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); r = acl_get_tag_type(ae_b, &tag_b); failure("acl_get_tag_type() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); if (tag_a != tag_b) return (0); /* Compare ACL qualifier */ -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN if (tag_a == ACL_EXTENDED_ALLOW || tag_b == ACL_EXTENDED_DENY) #else if (tag_a == ACL_USER || tag_a == ACL_GROUP) #endif { qual_a = acl_get_qualifier(ae_a); failure("acl_get_qualifier() error: %s", strerror(errno)); if (assert(qual_a != NULL) == 0) return (-1); qual_b = acl_get_qualifier(ae_b); failure("acl_get_qualifier() error: %s", strerror(errno)); if (assert(qual_b != NULL) == 0) { acl_free(qual_a); return (-1); } -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN if (memcmp(((guid_t *)qual_a)->g_guid, ((guid_t *)qual_b)->g_guid, KAUTH_GUID_SIZE) != 0) #else if ((tag_a == ACL_USER && (*(uid_t *)qual_a != *(uid_t *)qual_b)) || (tag_a == ACL_GROUP && (*(gid_t *)qual_a != *(gid_t *)qual_b))) #endif { acl_free(qual_a); acl_free(qual_b); return (0); } acl_free(qual_a); acl_free(qual_b); } -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 if (is_nfs4) { /* Compare NFS4 ACL type */ r = acl_get_entry_type_np(ae_a, &type_a); failure("acl_get_entry_type_np() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); r = acl_get_entry_type_np(ae_b, &type_b); failure("acl_get_entry_type_np() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); if (type_a != type_b) return (0); } #endif /* Compare ACL perms */ r = acl_get_permset(ae_a, &permset_a); failure("acl_get_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); r = acl_get_permset(ae_b, &permset_b); failure("acl_get_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); perm_start = 0; perm_end = (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 if (is_nfs4) perm_start = 3; else perm_end = 3; #endif /* Cycle through all perms and compare their value */ for (i = perm_start; i < perm_end; i++) { -#if HAVE_LIBACL +#if ARCHIVE_ACL_LIBACL perm_a = acl_get_perm(permset_a, acl_perms[i]); perm_b = acl_get_perm(permset_b, acl_perms[i]); #else perm_a = acl_get_perm_np(permset_a, acl_perms[i]); perm_b = acl_get_perm_np(permset_b, acl_perms[i]); #endif if (perm_a == -1 || perm_b == -1) return (-1); if (perm_a != perm_b) return (0); } -#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN if (is_nfs4) { r = acl_get_flagset_np(ae_a, &flagset_a); failure("acl_get_flagset_np() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); r = acl_get_flagset_np(ae_b, &flagset_b); failure("acl_get_flagset_np() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) return (-1); /* Cycle through all flags and compare their status */ for (i = 0; i < (int)(sizeof(acl_flags) / sizeof(acl_flags[0])); i++) { flag_a = acl_get_flag_np(flagset_a, acl_flags[i]); flag_b = acl_get_flag_np(flagset_b, acl_flags[i]); if (flag_a == -1 || flag_b == -1) return (-1); if (flag_a != flag_b) return (0); } } -#else /* HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL*/ +#else /* ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN */ (void)is_nfs4; /* UNUSED */ #endif return (1); } -#endif /* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */ +#endif /* ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL */ -#if HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL +#if ARCHIVE_ACL_SUPPORT /* * Clear default ACLs or inheritance flags */ static void clear_inheritance_flags(const char *path, int type) { switch (type) { case ARCHIVE_TEST_ACL_TYPE_POSIX1E: -#if HAVE_POSIX_ACL +#if ARCHIVE_ACL_POSIX1E +#if !ARCHIVE_ACL_SUNOS acl_delete_def_file(path); #else /* Solaris */ setTestAcl(path); #endif +#endif /* ARCHIVE_ACL_POSIX1E */ break; case ARCHIVE_TEST_ACL_TYPE_NFS4: -#if HAVE_NFS4_ACL +#if ARCHIVE_ACL_NFS4 setTestAcl(path); #endif break; default: (void)path; /* UNUSED */ break; } } static int compare_acls(const char *path_a, const char *path_b) { int ret = 1; int is_nfs4 = 0; -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS void *acl_a, *acl_b; int aclcnt_a, aclcnt_b; aclent_t *aclent_a, *aclent_b; ace_t *ace_a, *ace_b; int e; -#else +#elif ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL acl_t acl_a, acl_b; acl_entry_t aclent_a, aclent_b; int a, b, r; #endif +#if ARCHIVE_ACL_LIBRICHACL + struct richacl *richacl_a, *richacl_b; + richacl_a = NULL; + richacl_b = NULL; +#endif + +#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL || \ + ARCHIVE_ACL_SUNOS acl_a = NULL; acl_b = NULL; -#if HAVE_SUN_ACL +#endif +#if ARCHIVE_ACL_SUNOS acl_a = sunacl_get(GETACL, &aclcnt_a, 0, path_a); if (acl_a == NULL) { -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 is_nfs4 = 1; acl_a = sunacl_get(ACE_GETACL, &aclcnt_a, 0, path_a); #endif failure("acl_get() error: %s", strerror(errno)); if (assert(acl_a != NULL) == 0) return (-1); -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 acl_b = sunacl_get(ACE_GETACL, &aclcnt_b, 0, path_b); #endif } else acl_b = sunacl_get(GETACL, &aclcnt_b, 0, path_b); if (acl_b == NULL && (errno == ENOSYS || errno == ENOTSUP)) { free(acl_a); return (0); } failure("acl_get() error: %s", strerror(errno)); if (assert(acl_b != NULL) == 0) { free(acl_a); return (-1); } if (aclcnt_a != aclcnt_b) { ret = 0; goto exit_free; } for (e = 0; e < aclcnt_a; e++) { if (!is_nfs4) { aclent_a = &((aclent_t *)acl_a)[e]; aclent_b = &((aclent_t *)acl_b)[e]; if (aclent_a->a_type != aclent_b->a_type || aclent_a->a_id != aclent_b->a_id || aclent_a->a_perm != aclent_b->a_perm) { ret = 0; goto exit_free; } } -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 else { ace_a = &((ace_t *)acl_a)[e]; ace_b = &((ace_t *)acl_b)[e]; if (ace_a->a_who != ace_b->a_who || ace_a->a_access_mask != ace_b->a_access_mask || ace_a->a_flags != ace_b->a_flags || ace_a->a_type != ace_b->a_type) { ret = 0; goto exit_free; } } #endif } -#else /* !HAVE_SUN_ACL */ -#if HAVE_DARWIN_ACL +#else /* !ARCHIVE_ACL_SUNOS */ +#if ARCHIVE_ACL_LIBRICHACL + richacl_a = richacl_get_file(path_a); +#if !ARCHIVE_ACL_LIBACL + if (richacl_a == NULL && + (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) + return (0); + failure("richacl_get_file() error: %s (%s)", path_a, strerror(errno)); + if (assert(richacl_a != NULL) == 0) + return (-1); +#endif + if (richacl_a != NULL) { + richacl_b = richacl_get_file(path_b); + if (richacl_b == NULL && + (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) + return (0); + failure("richacl_get_file() error: %s (%s)", path_b, + strerror(errno)); + if (assert(richacl_b != NULL) == 0) { + richacl_free(richacl_a); + return (-1); + } + if (richacl_compare(richacl_a, richacl_b) == 0) + ret = 0; + richacl_free(richacl_a); + richacl_free(richacl_b); + return (ret); + } +#endif /* ARCHIVE_ACL_LIBRICHACL */ +#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL +#if ARCHIVE_ACL_DARWIN is_nfs4 = 1; acl_a = acl_get_file(path_a, ACL_TYPE_EXTENDED); -#elif HAVE_FREEBSD_NFS4_ACL +#elif ARCHIVE_ACL_FREEBSD_NFS4 acl_a = acl_get_file(path_a, ACL_TYPE_NFS4); if (acl_a != NULL) is_nfs4 = 1; #endif -#if !HAVE_DARWIN_ACL if (acl_a == NULL) acl_a = acl_get_file(path_a, ACL_TYPE_ACCESS); -#endif failure("acl_get_file() error: %s (%s)", path_a, strerror(errno)); if (assert(acl_a != NULL) == 0) return (-1); -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN acl_b = acl_get_file(path_b, ACL_TYPE_EXTENDED); -#elif HAVE_FREEBSD_NFS4_ACL +#elif ARCHIVE_ACL_FREEBSD_NFS4 acl_b = acl_get_file(path_b, ACL_TYPE_NFS4); #endif -#if !HAVE_DARWIN_ACL +#if !ARCHIVE_ACL_DARWIN if (acl_b == NULL) { -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_FREEBSD_NFS4 if (is_nfs4) { acl_free(acl_a); return (0); } #endif acl_b = acl_get_file(path_b, ACL_TYPE_ACCESS); } failure("acl_get_file() error: %s (%s)", path_b, strerror(errno)); if (assert(acl_b != NULL) == 0) { acl_free(acl_a); return (-1); } #endif a = acl_get_entry(acl_a, ACL_FIRST_ENTRY, &aclent_a); if (a == -1) { ret = 0; goto exit_free; } b = acl_get_entry(acl_b, ACL_FIRST_ENTRY, &aclent_b); if (b == -1) { ret = 0; goto exit_free; } -#if HAVE_DARWIN_ACL +#if ARCHIVE_ACL_DARWIN while (a == 0 && b == 0) #else /* FreeBSD, Linux */ while (a == 1 && b == 1) #endif { r = compare_acl_entry(aclent_a, aclent_b, is_nfs4); if (r != 1) { ret = r; goto exit_free; } a = acl_get_entry(acl_a, ACL_NEXT_ENTRY, &aclent_a); b = acl_get_entry(acl_b, ACL_NEXT_ENTRY, &aclent_b); } /* Entry count must match */ if (a != b) ret = 0; -#endif /* !HAVE_SUN_ACL */ +#endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL */ +#endif /* !ARCHIVE_ACL_SUNOS */ exit_free: -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS free(acl_a); free(acl_b); #else acl_free(acl_a); acl_free(acl_b); #endif return (ret); } -#endif /* HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL */ +#endif /* ARCHIVE_ACL_SUPPORT */ DEFINE_TEST(test_option_acls) { -#if !HAVE_SUN_ACL && !HAVE_DARWIN_ACL && !HAVE_POSIX_ACL +#if !ARCHIVE_ACL_SUPPORT skipping("ACLs are not supported on this platform"); -#else /* HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL */ +#else /* ARCHIVE_ACL_SUPPORT */ int acltype, r; assertMakeFile("f", 0644, "a"); acltype = setTestAcl("f"); if (acltype == 0) { skipping("Can't write ACLs on the filesystem"); return; } /* Archive it with acls */ r = systemf("%s -c --no-mac-metadata --acls -f acls.tar f >acls.out 2>acls.err", testprog); assertEqualInt(r, 0); /* Archive it without acls */ r = systemf("%s -c --no-mac-metadata --no-acls -f noacls.tar f >noacls.out 2>noacls.err", testprog); assertEqualInt(r, 0); /* Extract acls with acls */ assertMakeDir("acls_acls", 0755); clear_inheritance_flags("acls_acls", acltype); r = systemf("%s -x -C acls_acls --no-same-permissions --acls -f acls.tar >acls_acls.out 2>acls_acls.err", testprog); assertEqualInt(r, 0); r = compare_acls("f", "acls_acls/f"); assertEqualInt(r, 1); /* Extractl acls without acls */ assertMakeDir("acls_noacls", 0755); clear_inheritance_flags("acls_noacls", acltype); r = systemf("%s -x -C acls_noacls -p --no-acls -f acls.tar >acls_noacls.out 2>acls_noacls.err", testprog); assertEqualInt(r, 0); r = compare_acls("f", "acls_noacls/f"); assertEqualInt(r, 0); /* Extract noacls with acls flag */ assertMakeDir("noacls_acls", 0755); clear_inheritance_flags("noacls_acls", acltype); r = systemf("%s -x -C noacls_acls --no-same-permissions --acls -f noacls.tar >noacls_acls.out 2>noacls_acls.err", testprog); assertEqualInt(r, 0); r = compare_acls("f", "noacls_acls/f"); assertEqualInt(r, 0); /* Extract noacls with noacls */ assertMakeDir("noacls_noacls", 0755); clear_inheritance_flags("noacls_noacls", acltype); r = systemf("%s -x -C noacls_noacls -p --no-acls -f noacls.tar >noacls_noacls.out 2>noacls_noacls.err", testprog); assertEqualInt(r, 0); r = compare_acls("f", "noacls_noacls/f"); assertEqualInt(r, 0); -#endif /* HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL */ +#endif /* ARCHIVE_ACL_SUPPORT */ } Index: stable/10/contrib/libarchive/tar/test/test_option_xattrs.c =================================================================== --- stable/10/contrib/libarchive/tar/test/test_option_xattrs.c (nonexistent) +++ stable/10/contrib/libarchive/tar/test/test_option_xattrs.c (revision 316338) @@ -0,0 +1,87 @@ +/*- + * 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(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_option_xattrs) +{ +#if !ARCHIVE_XATTR_SUPPORT + skipping("Extended atributes are not supported on this platform"); +#else /* ARCHIVE_XATTR_SUPPORT */ + + const char *testattr = "user.libarchive.test"; + const char *testval = "testval"; + const void *readval; + size_t size; + int r; + + /* Create a file. */ + assertMakeFile("f", 0644, "a"); + + if (!setXattr("f", "user.libarchive.test", testval, + strlen(testval) + 1)) { + skipping("Can't set user extended attributes on this " + "filesystem"); + return; + } + + /* Archive with xattrs */ + r = systemf("%s -c --no-mac-metadata --xattrs -f xattrs.tar f >xattrs.out 2>xattrs.err", testprog); + assertEqualInt(r, 0); + + /* Archive without xattrs */ + r = systemf("%s -c --no-mac-metadata --no-xattrs -f noxattrs.tar f >noxattrs.out 2>noxattrs.err", testprog); + assertEqualInt(r, 0); + + /* Extract xattrs with xattrs */ + assertMakeDir("xattrs_xattrs", 0755); + r = systemf("%s -x -C xattrs_xattrs --no-same-permissions --xattrs -f xattrs.tar >xattrs_xattrs.out 2>xattrs_xattrs.err", testprog); + assertEqualInt(r, 0); + readval = getXattr("xattrs_xattrs/f", testattr, &size); + if(assertEqualInt(size, strlen(testval) + 1) != 0) + assertEqualMem(readval, testval, size); + + /* Extract xattrs without xattrs */ + assertMakeDir("xattrs_noxattrs", 0755); + r = systemf("%s -x -C xattrs_noxattrs -p --no-xattrs -f xattrs.tar >xattrs_noxattrs.out 2>xattrs_noxattrs.err", testprog); + assertEqualInt(r, 0); + readval = getXattr("xattrs_noxattrs/f", testattr, &size); + assert(readval == NULL); + + /* Extract noxattrs with xattrs */ + assertMakeDir("noxattrs_xattrs", 0755); + r = systemf("%s -x -C noxattrs_xattrs --no-same-permissions --xattrs -f noxattrs.tar >noxattrs_xattrs.out 2>noxattrs_xattrs.err", testprog); + assertEqualInt(r, 0); + readval = getXattr("noxattrs_xattrs/f", testattr, &size); + assert(readval == NULL); + + /* Extract noxattrs with noxattrs */ + assertMakeDir("noxattrs_noxattrs", 0755); + r = systemf("%s -x -C noxattrs_noxattrs -p --no-xattrs -f noxattrs.tar >noxattrs_noxattrs.out 2>noxattrs_noxattrs.err", testprog); + assertEqualInt(r, 0); + readval = getXattr("noxattrs_noxattrs/f", testattr, &size); + assert(readval == NULL); +#endif /* ARCHIVE_XATTR_SUPPORT */ +} Property changes on: stable/10/contrib/libarchive/tar/test/test_option_xattrs.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/test_utils/test_common.h =================================================================== --- stable/10/contrib/libarchive/test_utils/test_common.h (revision 316337) +++ stable/10/contrib/libarchive/test_utils/test_common.h (revision 316338) @@ -1,467 +1,445 @@ /* * Copyright (c) 2003-2017 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef TEST_COMMON_H #define TEST_COMMON_H /* * The goal of this file (and the matching test.c) is to * simplify the very repetitive test-*.c test programs. */ #if defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ #include "config.h" #elif defined(__FreeBSD__) /* Building as part of FreeBSD system requires a pre-built config.h. */ #include "config_freebsd.h" #elif defined(_WIN32) && !defined(__CYGWIN__) /* Win32 can't run the 'configure' script. */ #include "config_windows.h" #else /* Warn if the library hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no pre-built configuration in test.h. #endif #include /* Windows requires this before sys/stat.h */ #include #if HAVE_DIRENT_H #include #endif #ifdef HAVE_DIRECT_H #include #define dirent direct #endif #include #include #ifdef HAVE_IO_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif +#ifdef HAVE_SYS_RICHACL_H +#include +#endif #ifdef HAVE_WINDOWS_H #include #endif /* * System-specific tweaks. We really want to minimize these * as much as possible, since they make it harder to understand * the mainline code. */ /* Windows (including Visual Studio and MinGW but not Cygwin) */ #if defined(_WIN32) && !defined(__CYGWIN__) #if !defined(__BORLANDC__) #undef chdir #define chdir _chdir #define strdup _strdup #endif #endif /* Visual Studio */ #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf sprintf_s #endif #if defined(__BORLANDC__) #pragma warn -8068 /* Constant out of range in comparison. */ #endif /* Haiku OS and QNX */ #if defined(__HAIKU__) || defined(__QNXNTO__) /* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ #include #endif /* Get a real definition for __FBSDID if we can */ #if HAVE_SYS_CDEFS_H #include #endif /* If not, define it so as to avoid dangling semicolons. */ #ifndef __FBSDID #define __FBSDID(a) struct _undefined_hack #endif #ifndef O_BINARY #define O_BINARY 0 #endif -/* - * If this platform has , acl_create(), acl_init(), - * acl_set_file(), and ACL_USER, we assume it has the rest of the - * POSIX.1e draft functions used in archive_read_extract.c. - */ -#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE -#if HAVE_DECL_ACL_USER -#define HAVE_POSIX_ACL 1 -#elif HAVE_DECL_ACL_TYPE_EXTENDED -#define HAVE_DARWIN_ACL 1 -#endif -#if HAVE_DECL_ACL_TYPE_NFS4 -#define HAVE_FREEBSD_NFS4_ACL 1 -#endif -#endif - -/* - * If this platform has , acl_get(), facl_get(), acl_set(), - * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions - */ -#if HAVE_SYS_ACL_H && HAVE_ACL && HAVE_FACL && HAVE_ACLENT_T && \ - HAVE_DECL_GETACL && HAVE_DECL_GETACLCNT && HAVE_DECL_SETACL -#define HAVE_SUN_ACL 1 -#if HAVE_ACE_T && HAVE_DECL_ACE_GETACL && HAVE_DECL_ACE_GETACLCNT && \ - HAVE_DECL_ACE_SETACL -#define HAVE_SUN_NFS4_ACL 1 -#endif -#endif - -/* Define if platform supports NFSv4 ACLs */ -#if HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_NFS4_ACL || HAVE_DARWIN_ACL -#define HAVE_NFS4_ACL 1 -#endif - +#include "archive_platform_acl.h" #define ARCHIVE_TEST_ACL_TYPE_POSIX1E 1 #define ARCHIVE_TEST_ACL_TYPE_NFS4 2 +#include "archive_platform_xattr.h" + /* * Redefine DEFINE_TEST for use in defining the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); void name(void) /* An implementation of the standard assert() macro */ #define assert(e) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) /* chdir() and error if it fails */ #define assertChdir(path) \ assertion_chdir(__FILE__, __LINE__, path) /* Assert two files have the same file flags */ #define assertEqualFflags(patha, pathb) \ assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 0) /* Assert two integers are the same. Reports value of each one if not. */ #define assertEqualInt(v1,v2) \ assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* Assert two strings are the same. Reports value of each one if not. */ #define assertEqualString(v1,v2) \ assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) #define assertEqualUTF8String(v1,v2) \ assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) /* As above, but v1 and v2 are wchar_t * */ #define assertEqualWString(v1,v2) \ assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* As above, but raw blocks of bytes. */ #define assertEqualMem(v1, v2, l) \ assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) /* Assert that memory is full of a specified byte */ #define assertMemoryFilledWith(v1, l, b) \ assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) /* Assert two files are the same. */ #define assertEqualFile(f1, f2) \ assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) /* Assert that a file is empty. */ #define assertEmptyFile(pathname) \ assertion_empty_file(__FILE__, __LINE__, (pathname)) /* Assert that a file is not empty. */ #define assertNonEmptyFile(pathname) \ assertion_non_empty_file(__FILE__, __LINE__, (pathname)) #define assertFileAtime(pathname, sec, nsec) \ assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) #define assertFileAtimeRecent(pathname) \ assertion_file_atime_recent(__FILE__, __LINE__, pathname) #define assertFileBirthtime(pathname, sec, nsec) \ assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) #define assertFileBirthtimeRecent(pathname) \ assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) /* Assert that a file exists; supports printf-style arguments. */ #define assertFileExists(pathname) \ assertion_file_exists(__FILE__, __LINE__, pathname) /* Assert that a file exists. */ #define assertFileNotExists(pathname) \ assertion_file_not_exists(__FILE__, __LINE__, pathname) /* Assert that file contents match a string. */ #define assertFileContents(data, data_size, pathname) \ assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) /* Verify that a file does not contain invalid strings */ #define assertFileContainsNoInvalidStrings(pathname, strings) \ assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) #define assertFileMtime(pathname, sec, nsec) \ assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) #define assertFileMtimeRecent(pathname) \ assertion_file_mtime_recent(__FILE__, __LINE__, pathname) #define assertFileNLinks(pathname, nlinks) \ assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) #define assertFileSize(pathname, size) \ assertion_file_size(__FILE__, __LINE__, pathname, size) #define assertFileMode(pathname, mode) \ assertion_file_mode(__FILE__, __LINE__, pathname, mode) #define assertTextFileContents(text, pathname) \ assertion_text_file_contents(__FILE__, __LINE__, text, pathname) #define assertFileContainsLinesAnyOrder(pathname, lines) \ assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) #define assertIsDir(pathname, mode) \ assertion_is_dir(__FILE__, __LINE__, pathname, mode) #define assertIsHardlink(path1, path2) \ assertion_is_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsNotHardlink(path1, path2) \ assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsReg(pathname, mode) \ assertion_is_reg(__FILE__, __LINE__, pathname, mode) #define assertIsSymlink(pathname, contents) \ assertion_is_symlink(__FILE__, __LINE__, pathname, contents) /* Create a directory, report error if it fails. */ #define assertMakeDir(dirname, mode) \ assertion_make_dir(__FILE__, __LINE__, dirname, mode) #define assertMakeFile(path, mode, contents) \ assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) #define assertMakeBinFile(path, mode, csize, contents) \ assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) #define assertMakeHardlink(newfile, oldfile) \ assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) #define assertMakeSymlink(newfile, linkto) \ assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) #define assertSetNodump(path) \ assertion_set_nodump(__FILE__, __LINE__, path) #define assertUmask(mask) \ assertion_umask(__FILE__, __LINE__, mask) /* Assert that two files have unequal file flags */ #define assertUnequalFflags(patha, pathb) \ assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 1) #define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) #ifndef PROGRAM #define assertEntrySetAcls(entry, acls, count) \ assertion_entry_set_acls(__FILE__, __LINE__, entry, acls, count) #define assertEntryCompareAcls(entry, acls, count, type, mode) \ assertion_entry_compare_acls(__FILE__, __LINE__, entry, acls, count, type, mode) #endif /* * This would be simple with C99 variadic macros, but I don't want to * require that. Instead, I insert a function call before each * skipping() call to pass the file and line information down. Crude, * but effective. */ #define skipping \ skipping_setup(__FILE__, __LINE__);test_skipping /* Function declarations. These are defined in test_utility.c. */ void failure(const char *fmt, ...); int assertion_assert(const char *, int, int, const char *, void *); int assertion_chdir(const char *, int, const char *); int assertion_compare_fflags(const char *, int, const char *, const char *, int); int assertion_empty_file(const char *, int, const char *); int assertion_equal_file(const char *, int, const char *, const char *); int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); int assertion_file_atime(const char *, int, const char *, long, long); int assertion_file_atime_recent(const char *, int, const char *); int assertion_file_birthtime(const char *, int, const char *, long, long); int assertion_file_birthtime_recent(const char *, int, const char *); int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); int assertion_file_contents(const char *, int, const void *, int, const char *); int assertion_file_exists(const char *, int, const char *); int assertion_file_mode(const char *, int, const char *, int); int assertion_file_mtime(const char *, int, const char *, long, long); int assertion_file_mtime_recent(const char *, int, const char *); int assertion_file_nlinks(const char *, int, const char *, int); int assertion_file_not_exists(const char *, int, const char *); int assertion_file_size(const char *, int, const char *, long); int assertion_is_dir(const char *, int, const char *, int); int assertion_is_hardlink(const char *, int, const char *, const char *); int assertion_is_not_hardlink(const char *, int, const char *, const char *); int assertion_is_reg(const char *, int, const char *, int); int assertion_is_symlink(const char *, int, const char *, const char *); int assertion_make_dir(const char *, int, const char *, int); int assertion_make_file(const char *, int, const char *, int, int, const void *); int assertion_make_hardlink(const char *, int, const char *newpath, const char *); int assertion_make_symlink(const char *, int, const char *newpath, const char *); int assertion_non_empty_file(const char *, int, const char *); int assertion_set_nodump(const char *, int, const char *); int assertion_text_file_contents(const char *, int, const char *buff, const char *f); int assertion_umask(const char *, int, int); int assertion_utimes(const char *, int, const char *, long, long, long, long ); int assertion_version(const char*, int, const char *, const char *); void skipping_setup(const char *, int); void test_skipping(const char *fmt, ...); /* Like sprintf, then system() */ int systemf(const char * fmt, ...); /* Delay until time() returns a value after this. */ void sleepUntilAfter(time_t); /* Return true if this platform can create symlinks. */ int canSymlink(void); /* Return true if this platform can run the "bzip2" program. */ int canBzip2(void); /* Return true if this platform can run the "grzip" program. */ int canGrzip(void); /* Return true if this platform can run the "gzip" program. */ int canGzip(void); /* Return true if this platform can run the specified command. */ int canRunCommand(const char *); /* Return true if this platform can run the "lrzip" program. */ int canLrzip(void); /* Return true if this platform can run the "lz4" program. */ int canLz4(void); /* Return true if this platform can run the "lzip" program. */ int canLzip(void); /* Return true if this platform can run the "lzma" program. */ int canLzma(void); /* Return true if this platform can run the "lzop" program. */ int canLzop(void); /* Return true if this platform can run the "xz" program. */ int canXz(void); /* Return true if this filesystem can handle nodump flags. */ int canNodump(void); /* Set test ACLs */ int setTestAcl(const char *path); +/* Get extended attribute */ +const void *getXattr(const char *, const char *, size_t *); + +/* Set extended attribute */ +int setXattr(const char *, const char *, const void *, size_t); + /* Return true if the file has large i-node number(>0xffffffff). */ int is_LargeInode(const char *); -#if HAVE_SUN_ACL +#if ARCHIVE_ACL_SUNOS /* Fetch ACLs on Solaris using acl() or facl() */ void *sunacl_get(int cmd, int *aclcnt, int fd, const char *path); #endif /* Suck file into string allocated via malloc(). Call free() when done. */ /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */ char *slurpfile(size_t *, const char *fmt, ...); /* Dump block of bytes to a file. */ void dumpfile(const char *filename, void *, size_t); /* Extracts named reference file to the current directory. */ void extract_reference_file(const char *); /* Copies named reference file to the current directory. */ void copy_reference_file(const char *); /* Extracts a list of files to the current directory. * List must be NULL terminated. */ void extract_reference_files(const char **); /* Subtract umask from mode */ mode_t umasked(mode_t expected_mode); /* Path to working directory for current test */ extern const char *testworkdir; #ifndef PROGRAM /* * Special interfaces for libarchive test harness. */ #include "archive.h" #include "archive_entry.h" /* ACL structure */ struct archive_test_acl_t { int type; /* Type of ACL */ int permset; /* Permissions for this class of users. */ int tag; /* Owner, User, Owning group, group, other, etc. */ int qual; /* GID or UID of user/group, depending on tag. */ const char *name; /* Name of user/group, depending on tag. */ }; /* Set ACLs */ int assertion_entry_set_acls(const char *, int, struct archive_entry *, struct archive_test_acl_t *, int); /* Compare ACLs */ int assertion_entry_compare_acls(const char *, int, struct archive_entry *, struct archive_test_acl_t *, int, int, int); /* Special customized read-from-memory interface. */ int read_open_memory(struct archive *, const void *, size_t, size_t); /* _minimal version exercises a slightly different set of libarchive APIs. */ int read_open_memory_minimal(struct archive *, const void *, size_t, size_t); /* _seek version produces a seekable file. */ int read_open_memory_seek(struct archive *, const void *, size_t, size_t); /* Versions of above that accept an archive argument for additional info. */ #define assertA(e) assertion_assert(__FILE__, __LINE__, (e), #e, (a)) #define assertEqualIntA(a,v1,v2) \ assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a)) #define assertEqualStringA(a,v1,v2) \ assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a), 0) #else /* defined(PROGRAM) */ /* * Special interfaces for program test harness. */ /* Pathname of exe to be tested. */ extern const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ extern const char *testprog; void assertVersion(const char *prog, const char *base); #endif /* defined(PROGRAM) */ #ifdef USE_DMALLOC #include #endif #endif /* TEST_COMMON_H */ Index: stable/10/contrib/libarchive/test_utils/test_main.c =================================================================== --- stable/10/contrib/libarchive/test_utils/test_main.c (revision 316337) +++ stable/10/contrib/libarchive/test_utils/test_main.c (revision 316338) @@ -1,3720 +1,3832 @@ /* * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include "test_utils.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LINUX_FS_H #include #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include -/* ACL support */ +#ifdef HAVE_SIGNAL_H +#endif #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif -#if HAVE_DARWIN_ACL +#ifdef HAVE_SYS_EA_H +#include +#endif +#ifdef HAVE_SYS_EXTATTR_H +#include +#endif +#if HAVE_SYS_XATTR_H +#include +#elif HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_SYS_RICHACL_H +#include +#endif +#if HAVE_MEMBERSHIP_H #include #endif /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif mode_t umasked(mode_t expected_mode) { mode_t mode = umask(0); umask(mode); return expected_mode & ~mode; } /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, flags); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ // Silence unused-parameter compiler warnings. (void)expression; (void)function; (void)file; (void)line; (void)pReserved; } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalid sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not legal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %d bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n", fn); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Verify that a text file does not contains the specified strings */ int assertion_file_contains_no_invalid_strings(const char *file, int line, const char *pathname, const char *strings[]) { char *buff; int i; buff = slurpfile(NULL, "%s", pathname); if (buff == NULL) { failure_start(file, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } for (i = 0; strings[i] != NULL; ++i) { if (strstr(buff, strings[i]) != NULL) { failure_start(file, line, "Invalid string in %s: %s", pathname, strings[i]); failure_finish(NULL); free(buff); return(0); } } free(buff); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; /* FreeBSD filesystems that don't support birthtime * (e.g., UFS1) always return -1 here. */ if (filet == -1) { return (1); } filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %lld.%09lld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mode of 'pathname'. */ int assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) { int mode; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) failure_start(file, line, "assertFileMode not yet implemented for Windows"); (void)mode; /* UNUSED */ (void)r; /* UNUSED */ (void)pathname; /* UNUSED */ (void)expected_mode; /* UNUSED */ #else { struct stat st; r = lstat(pathname, &st); mode = (int)(st.st_mode & 0777); } if (r == 0 && mode == expected_mode) return (1); failure_start(file, line, "File %s has mode %o, expected %o", pathname, mode, expected_mode); #endif failure_finish(NULL); return (0); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)pathname; /* UNUSED */ (void)contents; /* UNUSED */ assertion_count(file, line); /* Windows sort-of has real symlinks, but they're only usable * by privileged users and are crippled even then, so there's * really not much point in bothering with this. */ return (0); #else char buff[300]; struct stat st; ssize_t linklen; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff)); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents) { if (is_symlink(file, line, path, contents)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) { if (0 == chmod(dirname, mode)) { assertion_file_mode(file, line, dirname, mode); return (1); } } #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (0 != chmod(path, mode)) { failure_start(file, line, "Could not chmod %s", path); failure_finish(NULL); close(fd); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); close(fd); return (0); } } close(fd); assertion_file_mode(file, line, path, mode); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Create a symlink and report any failures. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto) { #if defined(_WIN32) && !defined(__CYGWIN__) int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Compare file flags */ int assertion_compare_fflags(const char *file, int line, const char *patha, const char *pathb, int nomatch) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) struct stat sa, sb; assertion_count(file, line); if (stat(patha, &sa) < 0) return (0); if (stat(pathb, &sb) < 0) return (0); if (!nomatch && sa.st_flags != sb.st_flags) { failure_start(file, line, "File flags should be identical: " "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb, sb.st_flags); failure_finish(NULL); return (0); } if (nomatch && sa.st_flags == sb.st_flags) { failure_start(file, line, "File flags should be different: " "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb, sb.st_flags); failure_finish(NULL); return (0); } #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) int fd, r, flagsa, flagsb; assertion_count(file, line); fd = open(patha, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", patha); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flagsa); close(fd); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", patha); failure_finish(NULL); return (0); } fd = open(pathb, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathb); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flagsb); close(fd); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathb); failure_finish(NULL); return (0); } if (!nomatch && flagsa != flagsb) { failure_start(file, line, "File flags should be identical: " "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb); failure_finish(NULL); return (0); } if (nomatch && flagsa == flagsb) { failure_start(file, line, "File flags should be different: " "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb); failure_finish(NULL); return (0); } #else (void)patha; /* UNUSED */ (void)pathb; /* UNUSED */ (void)nomatch; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* Set nodump, report failures. */ int assertion_set_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } #ifdef FS_NODUMP_FL flags |= FS_NODUMP_FL; #else flags |= EXT2_NODUMP_FL; #endif r = ioctl(fd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } #ifdef PROGRAM static void assert_version_id(char **qq, size_t *ss) { char *q = *qq; size_t s = *ss; /* Version number is a series of digits and periods. */ while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { ++q; --s; } if (q[0] == 'd' && q[1] == 'e' && q[2] == 'v') { q += 3; s -= 3; } /* Skip a single trailing a,b,c, or d. */ if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') ++q; /* Version number terminated by space. */ failure("No space after version: ``%s''", q); assert(s > 1); failure("No space after version: ``%s''", q); assert(*q == ' '); ++q; --s; *qq = q; *ss = s; } /* * Check program version */ void assertVersion(const char *prog, const char *base) { int r; char *p, *q; size_t s; unsigned int prog_len = strlen(base); r = systemf("%s --version >version.stdout 2>version.stderr", prog); if (r != 0) r = systemf("%s -W version >version.stdout 2>version.stderr", prog); failure("Unable to run either %s --version or %s -W version", prog, prog); if (!assert(r == 0)) return; /* --version should generate nothing to stdout. */ assertEmptyFile("version.stderr"); /* Verify format of version message. */ q = p = slurpfile(&s, "version.stdout"); /* Version message should start with name of program, then space. */ assert(s > prog_len + 1); failure("Version must start with '%s': ``%s''", base, p); if (!assertEqualMem(q, base, prog_len)) { free(p); return; } q += prog_len; s -= prog_len; assert(*q == ' '); q++; s--; assert_version_id(&q, &s); /* Separator. */ failure("No `-' between program name and versions: ``%s''", p); assertEqualMem(q, "- ", 2); q += 2; s -= 2; failure("Not long enough for libarchive version: ``%s''", p); assert(s > 11); failure("Libarchive version must start with `libarchive': ``%s''", p); assertEqualMem(q, "libarchive ", 11); q += 11; s -= 11; assert_version_id(&q, &s); /* Skip arbitrary third-party version numbers. */ while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) { ++q; --s; } /* All terminated by end-of-line. */ assert(s >= 1); /* Skip an optional CR character (e.g., Windows) */ failure("Version output must end with \\n or \\r\\n"); if (*q == '\r') { ++q; --s; } assertEqualMem(q, "\n", 1); free(p); } #endif /* PROGRAM */ /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 -d -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ int canNodump(void) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) \ && defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) return (0); #ifdef FS_NODUMP_FL flags |= FS_NODUMP_FL; #else flags |= EXT2_NODUMP_FL; #endif r = ioctl(fd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) return (0); close(fd); #ifdef FS_NODUMP_FL if (flags & FS_NODUMP_FL) #else if (flags & EXT2_NODUMP_FL) #endif return (1); #endif return (0); } -#if HAVE_SUN_ACL +/* Get extended attribute from a path */ +const void * +getXattr(const char *path, const char *name, size_t *sizep) +{ + void *value = NULL; +#if ARCHIVE_XATTR_SUPPORT + ssize_t size; +#if ARCHIVE_XATTR_LINUX + size = lgetxattr(path, name, NULL, 0); +#elif ARCHIVE_XATTR_DARWIN + size = getxattr(path, name, NULL, 0, 0, XATTR_NOFOLLOW); +#elif ARCHIVE_XATTR_AIX + size = lgetea(path, name, NULL, 0); +#elif ARCHIVE_XATTR_FREEBSD + size = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name + 5, + NULL, 0); +#endif + + if (size >= 0) { + value = malloc(size); +#if ARCHIVE_XATTR_LINUX + size = lgetxattr(path, name, value, size); +#elif ARCHIVE_XATTR_DARWIN + size = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); +#elif ARCHIVE_XATTR_AIX + size = lgetea(path, name, value, size); +#elif ARCHIVE_XATTR_FREEBSD + size = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name + 5, + value, size); +#endif + if (size < 0) { + free(value); + value = NULL; + } + } + if (size < 0) + *sizep = 0; + else + *sizep = (size_t)size; +#else /* !ARCHIVE_XATTR_SUPPORT */ + (void)path; /* UNUSED */ + (void)name; /* UNUSED */ + *sizep = 0; +#endif /* !ARCHIVE_XATTR_SUPPORT */ + return (value); +} + +/* + * Set extended attribute on a path + * Returns 0 on error, 1 on success + */ +int +setXattr(const char *path, const char *name, const void *value, size_t size) +{ +#if ARCHIVE_XATTR_SUPPORT +#if ARCHIVE_XATTR_LINUX + if (lsetxattr(path, name, value, size, 0) == 0) +#elif ARCHIVE_XATTR_DARWIN + if (setxattr(path, name, value, size, 0, XATTR_NOFOLLOW) == 0) +#elif ARCHIVE_XATTR_AIX + if (lsetea(path, name, value, size, 0) == 0) +#elif ARCHIVE_XATTR_FREEBSD + if (extattr_set_link(path, EXTATTR_NAMESPACE_USER, name + 5, value, + size) > -1) +#else + if (0) +#endif + return (1); +#else /* !ARCHIVE_XATTR_SUPPORT */ + (void)path; /* UNUSED */ + (void)name; /* UNUSED */ + (void)value; /* UNUSED */ + (void)size; /* UNUSED */ +#endif /* !ARCHIVE_XATTR_SUPPORT */ + return (0); +} + +#if ARCHIVE_ACL_SUNOS /* Fetch ACLs on Solaris using acl() or facl() */ void * sunacl_get(int cmd, int *aclcnt, int fd, const char *path) { int cnt, cntcmd; size_t size; void *aclp; if (cmd == GETACL) { cntcmd = GETACLCNT; size = sizeof(aclent_t); } -#if HAVE_SUN_NFS4_ACL +#if ARCHIVE_ACL_SUNOS_NFS4 else if (cmd == ACE_GETACL) { cntcmd = ACE_GETACLCNT; size = sizeof(ace_t); } #endif else { errno = EINVAL; *aclcnt = -1; return (NULL); } aclp = NULL; cnt = -2; while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) { if (path != NULL) cnt = acl(path, cntcmd, 0, NULL); else cnt = facl(fd, cntcmd, 0, NULL); if (cnt > 0) { if (aclp == NULL) aclp = malloc(cnt * size); else aclp = realloc(NULL, cnt * size); if (aclp != NULL) { if (path != NULL) cnt = acl(path, cmd, cnt, aclp); else cnt = facl(fd, cmd, cnt, aclp); } } else { if (aclp != NULL) { free(aclp); aclp = NULL; } break; } } *aclcnt = cnt; return (aclp); } -#endif /* HAVE_SUN_ACL */ +#endif /* ARCHIVE_ACL_SUNOS */ /* * Set test ACLs on a path * Return values: * 0: error setting ACLs * ARCHIVE_TEST_ACL_TYPE_POSIX1E: POSIX.1E ACLs have been set * ARCHIVE_TEST_ACL_TYPE_NFS4: NFSv4 or extended ACLs have been set */ int setTestAcl(const char *path) { -#if HAVE_POSIX_ACL || HAVE_NFS4_ACL +#if ARCHIVE_ACL_SUPPORT int r = 1; -#if !HAVE_SUN_ACL +#if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN acl_t acl; #endif -#if HAVE_POSIX_ACL /* Linux, FreeBSD POSIX.1e */ +#if ARCHIVE_ACL_LIBRICHACL + struct richacl *richacl; +#endif +#if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_FREEBSD const char *acltext_posix1e = "user:1:rw-," "group:15:r-x," "user::rwx," "group::rwx," "other::r-x," "mask::rwx"; -#elif HAVE_SUN_ACL /* Solaris POSIX.1e */ +#elif ARCHIVE_ACL_SUNOS /* Solaris POSIX.1e */ aclent_t aclp_posix1e[] = { { USER_OBJ, -1, 4 | 2 | 1 }, { USER, 1, 4 | 2 }, { GROUP_OBJ, -1, 4 | 2 | 1 }, { GROUP, 15, 4 | 1 }, { CLASS_OBJ, -1, 4 | 2 | 1 }, { OTHER_OBJ, -1, 4 | 2 | 1 } }; #endif -#if HAVE_FREEBSD_NFS4_ACL /* FreeBSD NFS4 */ +#if ARCHIVE_ACL_FREEBSD /* FreeBSD NFS4 */ const char *acltext_nfs4 = "user:1:rwpaRcs::allow:1," "group:15:rxaRcs::allow:15," "owner@:rwpxaARWcCos::allow," "group@:rwpxaRcs::allow," "everyone@:rxaRcs::allow"; -#elif HAVE_SUN_NFS4_ACL /* Solaris NFS4 */ +#elif ARCHIVE_ACL_LIBRICHACL + const char *acltext_nfs4 = "owner:rwpxaARWcCoS::mask," + "group:rwpxaRcS::mask," + "other:rxaRcS::mask," + "user:1:rwpaRcS::allow," + "group:15:rxaRcS::allow," + "owner@:rwpxaARWcCoS::allow," + "group@:rwpxaRcS::allow," + "everyone@:rxaRcS::allow"; +#elif ARCHIVE_ACL_SUNOS_NFS4 /* Solaris NFS4 */ ace_t aclp_nfs4[] = { { 1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, 0, ACE_ACCESS_ALLOWED_ACE_TYPE }, { 15, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS | ACE_READ_ACL | ACE_WRITE_ACL | ACE_WRITE_OWNER | ACE_SYNCHRONIZE, ACE_OWNER, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_GROUP | ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_EVERYONE, ACE_ACCESS_ALLOWED_ACE_TYPE } }; -#elif HAVE_DARWIN_ACL /* Mac OS X */ +#elif ARCHIVE_ACL_DARWIN /* Mac OS X */ acl_entry_t aclent; acl_permset_t permset; const uid_t uid = 1; uuid_t uuid; int i; const acl_perm_t acl_perms[] = { ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE, ACL_READ_ATTRIBUTES, ACL_READ_EXTATTRIBUTES, ACL_READ_SECURITY, #if HAVE_DECL_ACL_SYNCHRONIZE ACL_SYNCHRONIZE #endif }; -#endif /* HAVE_DARWIN_ACL */ +#endif /* ARCHIVE_ACL_DARWIN */ -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_FREEBSD acl = acl_from_text(acltext_nfs4); failure("acl_from_text() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); -#elif HAVE_DARWIN_ACL +#elif ARCHIVE_ACL_LIBRICHACL + richacl = richacl_from_text(acltext_nfs4, NULL, NULL); + failure("richacl_from_text() error: %s", strerror(errno)); + if (assert(richacl != NULL) == 0) + return (0); +#elif ARCHIVE_ACL_DARWIN acl = acl_init(1); failure("acl_init() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); r = acl_create_entry(&acl, &aclent); failure("acl_create_entry() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_set_tag_type(aclent, ACL_EXTENDED_ALLOW); failure("acl_set_tag_type() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_get_permset(aclent, &permset); failure("acl_get_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; for (i = 0; i < (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); i++) { r = acl_add_perm(permset, acl_perms[i]); failure("acl_add_perm() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; } r = acl_set_permset(aclent, permset); failure("acl_set_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; - r = mbr_identifier_to_uuid(ID_TYPE_UID, &uid, sizeof(uid_t), uuid); - failure("mbr_identifier_to_uuid() error: %s", strerror(errno)); + r = mbr_uid_to_uuid(uid, uuid); + failure("mbr_uid_to_uuid() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_set_qualifier(aclent, uuid); failure("acl_set_qualifier() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; -#endif /* HAVE_DARWIN_ACL */ +#endif /* ARCHIVE_ACL_DARWIN */ -#if HAVE_NFS4_ACL -#if HAVE_FREEBSD_NFS4_ACL +#if ARCHIVE_ACL_NFS4 +#if ARCHIVE_ACL_FREEBSD r = acl_set_file(path, ACL_TYPE_NFS4, acl); acl_free(acl); -#elif HAVE_SUN_NFS4_ACL +#elif ARCHIVE_ACL_LIBRICHACL + r = richacl_set_file(path, richacl); + richacl_free(richacl); +#elif ARCHIVE_ACL_SUNOS_NFS4 r = acl(path, ACE_SETACL, (int)(sizeof(aclp_nfs4)/sizeof(aclp_nfs4[0])), aclp_nfs4); -#elif HAVE_DARWIN_ACL +#elif ARCHIVE_ACL_DARWIN r = acl_set_file(path, ACL_TYPE_EXTENDED, acl); acl_free(acl); #endif if (r == 0) return (ARCHIVE_TEST_ACL_TYPE_NFS4); -#endif /* HAVE_NFS4_ACL */ +#endif /* ARCHIVE_ACL_NFS4 */ -#if HAVE_POSIX_ACL || HAVE_SUN_ACL -#if HAVE_POSIX_ACL +#if ARCHIVE_ACL_POSIX1E +#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL acl = acl_from_text(acltext_posix1e); failure("acl_from_text() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); r = acl_set_file(path, ACL_TYPE_ACCESS, acl); acl_free(acl); -#elif HAVE_SUN_ACL +#elif ARCHIVE_ACL_SUNOS r = acl(path, SETACL, (int)(sizeof(aclp_posix1e)/sizeof(aclp_posix1e[0])), aclp_posix1e); #endif if (r == 0) return (ARCHIVE_TEST_ACL_TYPE_POSIX1E); else return (0); -#endif /* HAVE_POSIX_ACL || HAVE_SUN_ACL */ -#if HAVE_DARWIN_ACL +#endif /* ARCHIVE_ACL_POSIX1E */ +#if ARCHIVE_ACL_DARWIN testacl_free: acl_free(acl); #endif -#endif /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */ +#endif /* ARCHIVE_ACL_SUPPORT */ (void)path; /* UNUSED */ return (0); } /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } #ifndef PROGRAM /* Set ACLs */ int assertion_entry_set_acls(const char *file, int line, struct archive_entry *ae, struct archive_test_acl_t *acls, int n) { int i, r, ret; assertion_count(file, line); ret = 0; archive_entry_acl_clear(ae); for (i = 0; i < n; i++) { r = archive_entry_acl_add_entry(ae, acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name); if (r != 0) { ret = 1; failure_start(file, line, "type=%#010x, ", "permset=%#010x, tag=%d, qual=%d name=%s", acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name); failure_finish(NULL); } } return (ret); } static int archive_test_acl_match(struct archive_test_acl_t *acl, int type, int permset, int tag, int qual, const char *name) { if (type != acl->type) return (0); if (permset != acl->permset) return (0); if (tag != acl->tag) return (0); if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) return (1); if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (1); if (tag == ARCHIVE_ENTRY_ACL_EVERYONE) return (1); if (tag == ARCHIVE_ENTRY_ACL_OTHER) return (1); if (qual != acl->qual) return (0); if (name == NULL) { if (acl->name == NULL || acl->name[0] == '\0') return (1); return (0); } if (acl->name == NULL) { if (name[0] == '\0') return (1); return (0); } return (0 == strcmp(name, acl->name)); } /* Compare ACLs */ int assertion_entry_compare_acls(const char *file, int line, struct archive_entry *ae, struct archive_test_acl_t *acls, int cnt, int want_type, int mode) { int *marker; int i, r, n, ret; int type, permset, tag, qual; int matched; const char *name; assertion_count(file, line); ret = 0; n = 0; marker = malloc(sizeof(marker[0]) * cnt); for (i = 0; i < cnt; i++) { if ((acls[i].type & want_type) != 0) { marker[n] = i; n++; } } if (n == 0) { failure_start(file, line, "No ACL's to compare, type mask: %d", want_type); return (1); } while (0 == (r = archive_entry_acl_next(ae, want_type, &type, &permset, &tag, &qual, &name))) { for (i = 0, matched = 0; i < n && !matched; i++) { if (archive_test_acl_match(&acls[marker[i]], type, permset, tag, qual, name)) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_USER_OBJ) { if (!matched) { failure_start(file, line, "No match for " "user_obj perm"); failure_finish(NULL); ret = 1; } if ((permset << 6) != (mode & 0700)) { failure_start(file, line, "USER_OBJ permset " "(%02o) != user mode (%02o)", permset, 07 & (mode >> 6)); failure_finish(NULL); ret = 1; } } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) { if (!matched) { failure_start(file, line, "No match for " "group_obj perm"); failure_finish(NULL); ret = 1; } if ((permset << 3) != (mode & 0070)) { failure_start(file, line, "GROUP_OBJ permset " "(%02o) != group mode (%02o)", permset, 07 & (mode >> 3)); failure_finish(NULL); ret = 1; } } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_OTHER) { if (!matched) { failure_start(file, line, "No match for " "other perm"); failure_finish(NULL); ret = 1; } if ((permset << 0) != (mode & 0007)) { failure_start(file, line, "OTHER permset " "(%02o) != other mode (%02o)", permset, mode & 07); failure_finish(NULL); ret = 1; } } else if (matched != 1) { failure_start(file, line, "Could not find match for " "ACL (type=%#010x,permset=%#010x,tag=%d,qual=%d," "name=``%s'')", type, permset, tag, qual, name); failure_finish(NULL); ret = 1; } } if (r != ARCHIVE_EOF) { failure_start(file, line, "Should not exit before EOF"); failure_finish(NULL); ret = 1; } if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 && (mode_t)(mode & 0777) != (archive_entry_mode(ae) & 0777)) { failure_start(file, line, "Mode (%02o) and entry mode (%02o) " "mismatch", mode, archive_entry_mode(ae)); failure_finish(NULL); ret = 1; } if (n != 0) { failure_start(file, line, "Could not find match for ACL " "(type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s'')", acls[marker[0]].type, acls[marker[0]].permset, acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name); failure_finish(NULL); ret = 1; /* Number of ACLs not matched should == 0 */ } free(marker); return (ret); } #endif /* !defined(PROGRAM) */ /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { char workdir[1024]; char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { size_t tried_size, buff_size; char *buff, *tried, *pwd = NULL, *p = NULL; #ifdef PATH_MAX buff_size = PATH_MAX; #else buff_size = 8192; #endif buff = calloc(buff_size, 1); if (buff == NULL) { fprintf(stderr, "Unable to allocate memory\n"); exit(1); } /* Allocate a buffer to hold the various directories we checked. */ tried_size = buff_size * 2; tried = calloc(tried_size, 1); if (tried == NULL) { fprintf(stderr, "Unable to allocate memory\n"); exit(1); } /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); free(tried); /* Copy result into a fresh buffer to reduce memory usage. */ p = strdup(buff); free(buff); return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: stable/10/lib/libarchive/Makefile =================================================================== --- stable/10/lib/libarchive/Makefile (revision 316337) +++ stable/10/lib/libarchive/Makefile (revision 316338) @@ -1,420 +1,421 @@ # $FreeBSD$ .include LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive LIB= archive DPADD= ${LIBZ} ${LIBBZ2} ${LIBLZMA} ${LIBPTHREAD} ${LIBBSDXML} LDADD= -lz -lbz2 -llzma -lpthread -lbsdxml CFLAGS+= -DHAVE_BZLIB_H=1 -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system. # It has no real relation to the libarchive version number. SHLIB_MAJOR= 6 CFLAGS+= -DPLATFORM_CONFIG_H=\"${.CURDIR}/config_freebsd.h\" CFLAGS+= -I${.OBJDIR} .if ${MK_OPENSSL} != "no" CFLAGS+= -DWITH_OPENSSL DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .else DPADD+= ${LIBMD} LDADD+= -lmd .endif .if ${MK_ICONV} != "no" # TODO: This can be changed back to CFLAGS once iconv works correctly # with statically linked binaries. SHARED_CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST= .endif .if ${MACHINE_ARCH:Marm*} != "" || ${MACHINE_ARCH:Mmips*} != "" || \ ${MACHINE_ARCH:Msparc64*} != "" || ${MACHINE_ARCH:Mia64*} != "" NO_WCAST_ALIGN= yes .if ${MACHINE_ARCH:M*64*} == "" CFLAGS+= -DPPMD_32BIT .endif .endif NO_WCAST_ALIGN.clang= .PATH: ${LIBARCHIVEDIR}/libarchive # Headers to be installed in /usr/include INCS= archive.h archive_entry.h # Sources to be compiled. SRCS= archive_acl.c \ archive_check_magic.c \ archive_cmdline.c \ archive_cryptor.c \ + archive_disk_acl_freebsd.c \ archive_digest.c \ archive_entry.c \ archive_entry_copy_stat.c \ archive_entry_link_resolver.c \ archive_entry_sparse.c \ archive_entry_stat.c \ archive_entry_strmode.c \ archive_entry_xattr.c \ archive_getdate.c \ archive_hmac.c \ archive_match.c \ archive_options.c \ archive_pack_dev.c \ archive_pathmatch.c \ archive_ppmd7.c \ archive_random.c \ archive_rb.c \ archive_read.c \ archive_read_add_passphrase.c \ archive_read_append_filter.c \ archive_read_data_into_fd.c \ archive_read_disk_entry_from_file.c \ archive_read_disk_posix.c \ archive_read_disk_set_standard_lookup.c \ archive_read_extract.c \ archive_read_extract2.c \ archive_read_open_fd.c \ archive_read_open_file.c \ archive_read_open_filename.c \ archive_read_open_memory.c \ archive_read_set_format.c \ archive_read_set_options.c \ archive_read_support_filter_all.c \ archive_read_support_filter_bzip2.c \ archive_read_support_filter_compress.c \ archive_read_support_filter_gzip.c \ archive_read_support_filter_grzip.c \ archive_read_support_filter_lrzip.c \ archive_read_support_filter_lz4.c \ archive_read_support_filter_lzop.c \ archive_read_support_filter_none.c \ archive_read_support_filter_program.c \ archive_read_support_filter_rpm.c \ archive_read_support_filter_uu.c \ archive_read_support_filter_xz.c \ archive_read_support_format_7zip.c \ archive_read_support_format_all.c \ archive_read_support_format_ar.c \ archive_read_support_format_by_code.c \ archive_read_support_format_cab.c \ archive_read_support_format_cpio.c \ archive_read_support_format_empty.c \ archive_read_support_format_iso9660.c \ archive_read_support_format_lha.c \ archive_read_support_format_mtree.c \ archive_read_support_format_rar.c \ archive_read_support_format_raw.c \ archive_read_support_format_tar.c \ archive_read_support_format_warc.c \ archive_read_support_format_xar.c \ archive_read_support_format_zip.c \ archive_string.c \ archive_string_sprintf.c \ archive_util.c \ + archive_version_details.c \ archive_virtual.c \ archive_write.c \ archive_write_add_filter.c \ - archive_write_disk_acl.c \ archive_write_disk_set_standard_lookup.c \ archive_write_disk_posix.c \ archive_write_open_fd.c \ archive_write_open_file.c \ archive_write_open_filename.c \ archive_write_open_memory.c \ archive_write_add_filter_b64encode.c \ archive_write_add_filter_by_name.c \ archive_write_add_filter_bzip2.c \ archive_write_add_filter_compress.c \ archive_write_add_filter_grzip.c \ archive_write_add_filter_gzip.c \ archive_write_add_filter_lrzip.c \ archive_write_add_filter_lz4.c \ archive_write_add_filter_lzop.c \ archive_write_add_filter_none.c \ archive_write_add_filter_program.c \ archive_write_add_filter_uuencode.c \ archive_write_add_filter_xz.c \ archive_write_set_format.c \ archive_write_set_format_7zip.c \ archive_write_set_format_ar.c \ archive_write_set_format_by_name.c \ archive_write_set_format_cpio.c \ archive_write_set_format_cpio_newc.c \ archive_write_set_format_filter_by_ext.c \ archive_write_set_format_gnutar.c \ archive_write_set_format_iso9660.c \ archive_write_set_format_mtree.c \ archive_write_set_format_pax.c \ archive_write_set_format_raw.c \ archive_write_set_format_shar.c \ archive_write_set_format_ustar.c \ archive_write_set_format_v7tar.c \ archive_write_set_format_warc.c \ archive_write_set_format_xar.c \ archive_write_set_format_zip.c \ archive_write_set_passphrase.c \ archive_write_set_options.c \ filter_fork_posix.c # Man pages to be installed. MAN= archive_entry.3 \ archive_entry_acl.3 \ archive_entry_linkify.3 \ archive_entry_paths.3 \ archive_entry_perms.3 \ archive_entry_stat.3 \ archive_entry_time.3 \ archive_read.3 \ archive_read_data.3 \ archive_read_disk.3 \ archive_read_extract.3 \ archive_read_filter.3 \ archive_read_format.3 \ archive_read_free.3 \ archive_read_header.3 \ archive_read_new.3 \ archive_read_open.3 \ archive_read_set_options.3 \ archive_util.3 \ archive_write.3 \ archive_write_blocksize.3 \ archive_write_data.3 \ archive_write_disk.3 \ archive_write_filter.3 \ archive_write_finish_entry.3 \ archive_write_format.3 \ archive_write_free.3 \ archive_write_header.3 \ archive_write_new.3 \ archive_write_open.3 \ archive_write_set_options.3 \ cpio.5 \ libarchive.3 \ libarchive_changes.3 \ libarchive_internals.3 \ libarchive-formats.5 \ tar.5 # Symlink the man pages under each function name. MLINKS+= archive_entry.3 archive_entry_clear.3 MLINKS+= archive_entry.3 archive_entry_clone.3 MLINKS+= archive_entry.3 archive_entry_free.3 MLINKS+= archive_entry.3 archive_entry_new.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_add_entry.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_add_entry_w.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_clear.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_count.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_next.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_next_w.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_reset.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_text_w.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_new.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_set_strategy.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_free.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_hardlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_link.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_link_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_pathname_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_sourcepath.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_symlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_hardlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_pathname_w.3 MLINKS+= archive_entry_paths.3 archive_entry_set_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_set_link.3 MLINKS+= archive_entry_paths.3 archive_entry_set_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_set_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_symlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_update_symlink_utf8.3 MLINKS+= archive_entry_paths.3 archive_entry_update_hardlink_utf8.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_fflags_text.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_fflags_text_w.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_gname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_uname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_fflags.3 MLINKS+= archive_entry_perms.3 archive_entry_fflags_text.3 MLINKS+= archive_entry_perms.3 archive_entry_gid.3 MLINKS+= archive_entry_perms.3 archive_entry_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_gname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_set_fflags.3 MLINKS+= archive_entry_perms.3 archive_entry_set_gid.3 MLINKS+= archive_entry_perms.3 archive_entry_set_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_perm.3 MLINKS+= archive_entry_perms.3 archive_entry_set_perm.3 MLINKS+= archive_entry_perms.3 archive_entry_set_uid.3 MLINKS+= archive_entry_perms.3 archive_entry_set_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_strmode.3 MLINKS+= archive_entry_perms.3 archive_entry_uid.3 MLINKS+= archive_entry_perms.3 archive_entry_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_uname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_update_gname_utf8.3 MLINKS+= archive_entry_perms.3 archive_entry_update_uname_utf8.3 MLINKS+= archive_entry_stat.3 archive_entry_copy_stat.3 MLINKS+= archive_entry_stat.3 archive_entry_dev.3 MLINKS+= archive_entry_stat.3 archive_entry_dev_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_devmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_devminor.3 MLINKS+= archive_entry_stat.3 archive_entry_filetype.3 MLINKS+= archive_entry_stat.3 archive_entry_ino.3 MLINKS+= archive_entry_stat.3 archive_entry_ino64.3 MLINKS+= archive_entry_stat.3 archive_entry_ino_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_mode.3 MLINKS+= archive_entry_stat.3 archive_entry_nlink.3 MLINKS+= archive_entry_stat.3 archive_entry_rdev.3 MLINKS+= archive_entry_stat.3 archive_entry_rdevmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_rdevminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_dev.3 MLINKS+= archive_entry_stat.3 archive_entry_set_devmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_devminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_filetype.3 MLINKS+= archive_entry_stat.3 archive_entry_set_ino.3 MLINKS+= archive_entry_stat.3 archive_entry_set_ino64.3 MLINKS+= archive_entry_stat.3 archive_entry_set_mode.3 MLINKS+= archive_entry_stat.3 archive_entry_set_nlink.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdev.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdevmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdevminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_size.3 MLINKS+= archive_entry_stat.3 archive_entry_size.3 MLINKS+= archive_entry_stat.3 archive_entry_size_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_unset_size.3 MLINKS+= archive_entry_time.3 archive_entry_atime.3 MLINKS+= archive_entry_time.3 archive_entry_atime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_atime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_ctime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_ctime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_mtime.3 MLINKS+= archive_entry_time.3 archive_entry_mtime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_mtime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_set_atime.3 MLINKS+= archive_entry_time.3 archive_entry_set_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_set_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_set_mtime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_atime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_mtime.3 MLINKS+= archive_read_data.3 archive_read_data_block.3 MLINKS+= archive_read_data.3 archive_read_data_into_fd.3 MLINKS+= archive_read_data.3 archive_read_data_skip.3 MLINKS+= archive_read_header.3 archive_read_next_header.3 MLINKS+= archive_read_header.3 archive_read_next_header2.3 MLINKS+= archive_read_extract.3 archive_read_extract2.3 MLINKS+= archive_read_extract.3 archive_read_extract_set_progress_callback.3 MLINKS+= archive_read_extract.3 archive_read_extract_set_skip_file.3 MLINKS+= archive_read_open.3 archive_read_open2.3 MLINKS+= archive_read_open.3 archive_read_open_FILE.3 MLINKS+= archive_read_open.3 archive_read_open_fd.3 MLINKS+= archive_read_open.3 archive_read_open_file.3 MLINKS+= archive_read_open.3 archive_read_open_filename.3 MLINKS+= archive_read_open.3 archive_read_open_memory.3 MLINKS+= archive_read_free.3 archive_read_close.3 MLINKS+= archive_read_free.3 archive_read_finish.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_all.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_bzip2.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_compress.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_gzip.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_lzma.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_none.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_xz.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_program.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_program_signature.3 MLINKS+= archive_read_format.3 archive_read_support_format_7zip.3 MLINKS+= archive_read_format.3 archive_read_support_format_all.3 MLINKS+= archive_read_format.3 archive_read_support_format_ar.3 MLINKS+= archive_read_format.3 archive_read_support_format_by_code.3 MLINKS+= archive_read_format.3 archive_read_support_format_cab.3 MLINKS+= archive_read_format.3 archive_read_support_format_cpio.3 MLINKS+= archive_read_format.3 archive_read_support_format_empty.3 MLINKS+= archive_read_format.3 archive_read_support_format_iso9660.3 MLINKS+= archive_read_format.3 archive_read_support_format_lha.3 MLINKS+= archive_read_format.3 archive_read_support_format_mtree.3 MLINKS+= archive_read_format.3 archive_read_support_format_rar.3 MLINKS+= archive_read_format.3 archive_read_support_format_raw.3 MLINKS+= archive_read_format.3 archive_read_support_format_tar.3 MLINKS+= archive_read_format.3 archive_read_support_format_xar.3 MLINKS+= archive_read_format.3 archive_read_support_format_zip.3 MLINKS+= archive_read_disk.3 archive_read_disk_entry_from_file.3 MLINKS+= archive_read_disk.3 archive_read_disk_gname.3 MLINKS+= archive_read_disk.3 archive_read_disk_new.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_gname_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_standard_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_hybrid.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_logical.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_physical.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_uname_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_uname.3 MLINKS+= archive_read_set_options.3 archive_read_set_filter_option.3 MLINKS+= archive_read_set_options.3 archive_read_set_format_option.3 MLINKS+= archive_read_set_options.3 archive_read_set_option.3 MLINKS+= archive_util.3 archive_clear_error.3 MLINKS+= archive_util.3 archive_compression.3 MLINKS+= archive_util.3 archive_compression_name.3 MLINKS+= archive_util.3 archive_copy_error.3 MLINKS+= archive_util.3 archive_errno.3 MLINKS+= archive_util.3 archive_error_string.3 MLINKS+= archive_util.3 archive_file_count.3 MLINKS+= archive_util.3 archive_filter_code.3 MLINKS+= archive_util.3 archive_filter_count.3 MLINKS+= archive_util.3 archive_filter_name.3 MLINKS+= archive_util.3 archive_format.3 MLINKS+= archive_util.3 archive_format_name.3 MLINKS+= archive_util.3 archive_position.3 MLINKS+= archive_util.3 archive_set_error.3 MLINKS+= archive_write_blocksize.3 archive_write_get_bytes_in_last_block.3 MLINKS+= archive_write_blocksize.3 archive_write_get_bytes_per_block.3 MLINKS+= archive_write_blocksize.3 archive_write_set_bytes_in_last_block.3 MLINKS+= archive_write_blocksize.3 archive_write_set_bytes_per_block.3 MLINKS+= archive_write_disk.3 archive_write_data_block.3 MLINKS+= archive_write_disk.3 archive_write_disk_new.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_group_lookup.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_options.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_skip_file.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_standard_lookup.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_user_lookup.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_bzip2.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_compress.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_gzip.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_lzip.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_lzma.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_none.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_program.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_xz.3 MLINKS+= archive_write_format.3 archive_write_set_format_cpio.3 MLINKS+= archive_write_format.3 archive_write_set_format_pax.3 MLINKS+= archive_write_format.3 archive_write_set_format_pax_restricted.3 MLINKS+= archive_write_format.3 archive_write_set_format_shar.3 MLINKS+= archive_write_format.3 archive_write_set_format_shar_dump.3 MLINKS+= archive_write_format.3 archive_write_set_format_ustar.3 MLINKS+= archive_write_free.3 archive_write_close.3 MLINKS+= archive_write_free.3 archive_write_fail.3 MLINKS+= archive_write_free.3 archive_write_finish.3 MLINKS+= archive_write_open.3 archive_write_open_FILE.3 MLINKS+= archive_write_open.3 archive_write_open_fd.3 MLINKS+= archive_write_open.3 archive_write_open_file.3 MLINKS+= archive_write_open.3 archive_write_open_filename.3 MLINKS+= archive_write_open.3 archive_write_open_memory.3 MLINKS+= archive_write_set_options.3 archive_write_set_filter_option.3 MLINKS+= archive_write_set_options.3 archive_write_set_format_option.3 MLINKS+= archive_write_set_options.3 archive_write_set_option.3 MLINKS+= libarchive.3 archive.3 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/10/lib/libarchive/config_freebsd.h =================================================================== --- stable/10/lib/libarchive/config_freebsd.h (revision 316337) +++ stable/10/lib/libarchive/config_freebsd.h (revision 316338) @@ -1,256 +1,249 @@ /*- * 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 have ACL and extattr support. */ +/* FreeBSD 5.0 and later has ACL and extattr support. */ #if __FreeBSD__ > 4 -#define HAVE_ACL_CREATE_ENTRY 1 -#define HAVE_ACL_GET_FD_NP 1 -#define HAVE_ACL_GET_LINK_NP 1 +#define ARCHIVE_ACL_FREEBSD 1 +#define ARCHIVE_XATTR_FREEBSD 1 #define HAVE_ACL_GET_PERM_NP 1 -#define HAVE_ACL_INIT 1 -#define HAVE_ACL_IS_TRIVIAL_NP 1 -#define HAVE_ACL_PERMSET_T 1 -#define HAVE_ACL_SET_FD 1 -#define HAVE_ACL_SET_FD_NP 1 -#define HAVE_ACL_SET_FILE 1 -#define HAVE_ACL_SET_LINK_NP 1 #define HAVE_ARC4RANDOM_BUF 1 -#define HAVE_DECL_ACL_USER 1 -#define HAVE_DECL_ACL_TYPE_NFS4 1 -#define HAVE_EXTATTR_GET_FILE 1 -#define HAVE_EXTATTR_LIST_FILE 1 -#define HAVE_EXTATTR_SET_FD 1 -#define HAVE_EXTATTR_SET_FILE 1 #define HAVE_STRUCT_XVFSCONF 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 -#endif +#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_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 316337) +++ stable/10/lib/libarchive/tests/Makefile (revision 316338) @@ -1,568 +1,569 @@ # $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_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_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_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_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_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_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_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_pax_libarchive_2x.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_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_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_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_raw.bufr.uu FILES+= test_read_format_raw.data.Z.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_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_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_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_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_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_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_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/tests/Makefile =================================================================== --- stable/10/usr.bin/bsdcat/tests/Makefile (revision 316337) +++ stable/10/usr.bin/bsdcat/tests/Makefile (revision 316338) @@ -1,68 +1,69 @@ # $FreeBSD$ PACKAGE= tests _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test BINDIR= ${TESTSDIR} PROGS+= bsdcat_test CFLAGS+= -DPLATFORM_CONFIG_H=\"${SRCTOP}/lib/libarchive/config_freebsd.h\" CFLAGS+= -I${SRCTOP}/lib/libarchive -I${.OBJDIR} CFLAGS+= -I${.OBJDIR} CFLAGS+= -I${_LIBARCHIVEDIR}/cat -I${_LIBARCHIVEDIR}/cat/test +CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive_fe -I${_LIBARCHIVEDIR}/test_utils # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC .PATH: ${_LIBARCHIVEDIR}/cat/test TESTS_SRCS= \ test_0.c \ test_empty_gz.c \ test_empty_lz4.c \ test_empty_xz.c \ test_error.c \ test_error_mixed.c \ test_expand_Z.c \ test_expand_bz2.c \ test_expand_gz.c \ test_expand_lz4.c \ test_expand_mixed.c \ test_expand_plain.c \ test_expand_xz.c \ test_help.c \ test_version.c SRCS.bsdcat_test= list.h \ ${TESTS_SRCS} .PATH: ${_LIBARCHIVEDIR}/test_utils SRCS.bsdcat_test+= test_main.c \ test_utils.c LIBADD.bsdcat_test= archive list.h: ${TESTS_SRCS} Makefile @(cd ${_LIBARCHIVEDIR}/tar/test && \ grep -h DEFINE_TEST ${.ALLSRC:N*Makefile}) > ${.TARGET}.tmp @mv ${.TARGET}.tmp ${.TARGET} CLEANFILES+= list.h list.h.tmp FILES+= test_empty.gz.uu FILES+= test_empty.lz4.uu FILES+= test_empty.xz.uu FILES+= test_expand.Z.uu FILES+= test_expand.bz2.uu FILES+= test_expand.gz.uu FILES+= test_expand.lz4.uu FILES+= test_expand.plain.uu FILES+= test_expand.xz.uu .include Index: stable/10/usr.bin/cpio/tests/Makefile =================================================================== --- stable/10/usr.bin/cpio/tests/Makefile (revision 316337) +++ stable/10/usr.bin/cpio/tests/Makefile (revision 316338) @@ -1,123 +1,124 @@ # $FreeBSD$ SRCTOP= ${.CURDIR:H:H:H} TESTSDIR= ${TESTSBASE}/usr.bin/cpio LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test BINDIR= ${TESTSDIR} PROGS+= bsdcpio_test CFLAGS+= -DPLATFORM_CONFIG_H=\"${SRCTOP}/lib/libarchive/config_freebsd.h\" CFLAGS+= -I${SRCTOP}/lib/libarchive -I${.OBJDIR} CFLAGS+= -I${.OBJDIR} CFLAGS+= -I${LIBARCHIVEDIR}/cpio -I${LIBARCHIVEDIR}/cpio/test +CFLAGS+= -I${LIBARCHIVEDIR}/libarchive CFLAGS+= -I${LIBARCHIVEDIR}/libarchive_fe -I${LIBARCHIVEDIR}/test_utils # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC .PATH: ${LIBARCHIVEDIR}/cpio CPIO_SRCS= cmdline.c .PATH: ${LIBARCHIVEDIR}/libarchive_fe CPIO_SRCS+= err.c .PATH: ${LIBARCHIVEDIR}/cpio/test TESTS_SRCS= \ test_0.c \ test_basic.c \ test_cmdline.c \ test_extract_cpio_Z.c \ test_extract_cpio_bz2.c \ test_extract_cpio_grz.c \ test_extract_cpio_gz.c \ test_extract_cpio_lrz.c \ test_extract_cpio_lz.c \ test_extract_cpio_lz4.c \ test_extract_cpio_lzma.c \ test_extract_cpio_lzo.c \ test_extract_cpio_xz.c \ test_format_newc.c \ test_gcpio_compat.c \ test_missing_file.c \ test_option_0.c \ test_option_B_upper.c \ test_option_C_upper.c \ test_option_J_upper.c \ test_option_L_upper.c \ test_option_Z_upper.c \ test_option_a.c \ test_option_b64encode.c \ test_option_c.c \ test_option_d.c \ test_option_f.c \ test_option_grzip.c \ test_option_help.c \ test_option_l.c \ test_option_lrzip.c \ test_option_lz4.c \ test_option_lzma.c \ test_option_lzop.c \ test_option_m.c \ test_option_passphrase.c \ test_option_t.c \ test_option_u.c \ test_option_uuencode.c \ test_option_version.c \ test_option_xz.c \ test_option_y.c \ test_option_z.c \ test_owner_parse.c \ test_passthrough_dotdot.c \ test_passthrough_reverse.c SRCS.bsdcpio_test= list.h \ ${CPIO_SRCS} \ ${TESTS_SRCS} .PATH: ${LIBARCHIVEDIR}/test_utils SRCS.bsdcpio_test+= test_main.c \ test_utils.c DPADD.bsdcpio_test= ${LIBARCHIVE} LDADD.bsdcpio_test= -larchive list.h: ${TESTS_SRCS} Makefile @(cd ${LIBARCHIVEDIR}/tar/test && \ grep -h DEFINE_TEST ${.ALLSRC:N*Makefile}) > ${.TARGET}.tmp @mv ${.TARGET}.tmp ${.TARGET} CLEANFILES+= list.h list.h.tmp FILES+= test_extract.cpio.Z.uu FILES+= test_extract.cpio.bz2.uu FILES+= test_extract.cpio.grz.uu FILES+= test_extract.cpio.gz.uu FILES+= test_extract.cpio.lrz.uu FILES+= test_extract.cpio.lz.uu FILES+= test_extract.cpio.lz4.uu FILES+= test_extract.cpio.lzma.uu FILES+= test_extract.cpio.lzo.uu FILES+= test_extract.cpio.xz.uu FILES+= test_gcpio_compat_ref.bin.uu FILES+= test_gcpio_compat_ref.crc.uu FILES+= test_gcpio_compat_ref.newc.uu FILES+= test_gcpio_compat_ref.ustar.uu FILES+= test_gcpio_compat_ref_nosym.bin.uu FILES+= test_gcpio_compat_ref_nosym.crc.uu FILES+= test_gcpio_compat_ref_nosym.newc.uu FILES+= test_gcpio_compat_ref_nosym.ustar.uu FILES+= test_option_f.cpio.uu FILES+= test_option_m.cpio.uu FILES+= test_option_passphrase.zip.uu FILES+= test_option_t.cpio.uu FILES+= test_option_t.stdout.uu FILES+= test_option_tv.stdout.uu .include Index: stable/10/usr.bin/tar/tests/Makefile =================================================================== --- stable/10/usr.bin/tar/tests/Makefile (revision 316337) +++ stable/10/usr.bin/tar/tests/Makefile (revision 316338) @@ -1,120 +1,122 @@ # $FreeBSD$ SRCTOP= ${.CURDIR:H:H:H} TESTSDIR= ${TESTSBASE}/usr.bin/tar LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test BINDIR= ${TESTSDIR} CFLAGS+= -DPLATFORM_CONFIG_H=\"${SRCTOP}/lib/libarchive/config_freebsd.h\" CFLAGS+= -I${SRCTOP}/lib/libarchive -I${.OBJDIR} CFLAGS+= -I${LIBARCHIVEDIR}/tar -I${LIBARCHIVEDIR}/tar/test +CFLAGS+= -I${LIBARCHIVEDIR}/libarchive CFLAGS+= -I${LIBARCHIVEDIR}/test_utils # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC PROGS+= bsdtar_test .PATH: ${LIBARCHIVEDIR}/tar/test TESTS_SRCS= \ test_0.c \ test_basic.c \ test_copy.c \ test_empty_mtree.c \ test_extract_tar_bz2.c \ test_extract_tar_grz.c \ test_extract_tar_gz.c \ test_extract_tar_lrz.c \ test_extract_tar_lz.c \ test_extract_tar_lz4.c \ test_extract_tar_lzma.c \ test_extract_tar_lzo.c \ test_extract_tar_xz.c \ test_format_newc.c \ test_help.c \ test_leading_slash.c \ test_missing_file.c \ test_option_C_upper.c \ test_option_H_upper.c \ test_option_L_upper.c \ test_option_O_upper.c \ test_option_T_upper.c \ test_option_U_upper.c \ test_option_X_upper.c \ test_option_a.c \ test_option_acls.c \ test_option_b.c \ test_option_b64encode.c \ test_option_exclude.c \ test_option_fflags.c \ test_option_gid_gname.c \ test_option_grzip.c \ test_option_j.c \ test_option_k.c \ test_option_keep_newer_files.c \ test_option_lrzip.c \ test_option_lz4.c \ test_option_lzma.c \ test_option_lzop.c \ test_option_n.c \ test_option_newer_than.c \ test_option_nodump.c \ test_option_older_than.c \ test_option_passphrase.c \ test_option_q.c \ test_option_r.c \ test_option_s.c \ test_option_uid_uname.c \ test_option_uuencode.c \ + test_option_xattrs.c \ test_option_xz.c \ test_option_z.c \ test_patterns.c \ test_print_longpath.c \ test_stdio.c \ test_strip_components.c \ test_symlink_dir.c \ test_version.c SRCS.bsdtar_test= \ ${TESTS_SRCS} \ list.h .PATH: ${LIBARCHIVEDIR}/test_utils SRCS.bsdtar_test+= test_main.c \ test_utils.c DPADD.bsdtar_test= ${LIBARCHIVE} LDADD.bsdtar_test= -larchive list.h: ${TESTS_SRCS} Makefile @(cd ${LIBARCHIVEDIR}/tar/test && \ grep -h DEFINE_TEST ${.ALLSRC:N*Makefile}) > ${.TARGET}.tmp @mv ${.TARGET}.tmp ${.TARGET} CLEANFILES+= list.h list.h.tmp FILES+= test_extract.tar.Z.uu FILES+= test_extract.tar.bz2.uu FILES+= test_extract.tar.grz.uu FILES+= test_extract.tar.gz.uu FILES+= test_extract.tar.lrz.uu FILES+= test_extract.tar.lz.uu FILES+= test_extract.tar.lz4.uu FILES+= test_extract.tar.lzma.uu FILES+= test_extract.tar.lzo.uu FILES+= test_extract.tar.xz.uu FILES+= test_leading_slash.tar.uu FILES+= test_option_keep_newer_files.tar.Z.uu FILES+= test_option_passphrase.zip.uu FILES+= test_option_s.tar.Z.uu FILES+= test_patterns_2.tar.uu FILES+= test_patterns_3.tar.uu FILES+= test_patterns_4.tar.uu FILES+= test_print_longpath.tar.Z.uu .include Index: stable/10 =================================================================== --- stable/10 (revision 316337) +++ stable/10 (revision 316338) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r315636,315876,316095