Index: vendor/libarchive/dist/Makefile.am =================================================================== --- vendor/libarchive/dist/Makefile.am (revision 310621) +++ vendor/libarchive/dist/Makefile.am (revision 310622) @@ -1,1266 +1,1268 @@ ## Process this file with automake to produce Makefile.in AUTOMAKE_OPTIONS= foreign subdir-objects ACLOCAL_AMFLAGS = -I build/autoconf # # What to build and install # lib_LTLIBRARIES= libarchive.la noinst_LTLIBRARIES= libarchive_fe.la bin_PROGRAMS= $(bsdtar_programs) $(bsdcpio_programs) $(bsdcat_programs) man_MANS= $(libarchive_man_MANS) $(bsdtar_man_MANS) $(bsdcpio_man_MANS) $(bsdcat_man_MANS) BUILT_SOURCES= libarchive/test/list.h tar/test/list.h cpio/test/list.h cat/test/list.h # # What to test: We always test libarchive, test bsdtar and bsdcpio only # if we built them. # check_PROGRAMS= libarchive_test $(bsdtar_test_programs) $(bsdcpio_test_programs) $(bsdcat_test_programs) TESTS= libarchive_test $(bsdtar_test_programs) $(bsdcpio_test_programs) $(bsdcat_test_programs) TESTS_ENVIRONMENT= $(libarchive_TESTS_ENVIRONMENT) $(bsdtar_TESTS_ENVIRONMENT) $(bsdcpio_TESTS_ENVIRONMENT) $(bsdcat_TESTS_ENVIRONMENT) # Always build and test both bsdtar and bsdcpio as part of 'distcheck' DISTCHECK_CONFIGURE_FLAGS = --enable-bsdtar --enable-bsdcpio # The next line is commented out by default in shipping libarchive releases. # It is uncommented by default in trunk. DEV_CFLAGS=-Werror -Wextra -Wunused -Wshadow -Wmissing-prototypes -Wcast-qual -g AM_CFLAGS=$(DEV_CFLAGS) PLATFORMCPPFLAGS = @PLATFORMCPPFLAGS@ AM_CPPFLAGS=$(PLATFORMCPPFLAGS) # # What to include in the distribution # EXTRA_DIST= \ CMakeLists.txt \ README.md \ build/autogen.sh \ build/bump-version.sh \ build/clean.sh \ build/cmake \ build/version \ contrib \ doc \ examples \ $(libarchive_EXTRA_DIST) \ $(libarchive_test_EXTRA_DIST) \ $(bsdtar_EXTRA_DIST) \ $(bsdtar_test_EXTRA_DIST) \ $(bsdcpio_EXTRA_DIST) \ $(bsdcpio_test_EXTRA_DIST) \ $(bsdcat_EXTRA_DIST) \ $(bsdcat_test_EXTRA_DIST) # a) Clean out some unneeded files and directories # b) Collect all documentation and format it for distribution. dist-hook: rm -rf `find $(distdir) -name CVS -type d` rm -rf `find $(distdir) -name .svn -type d` rm -f `find $(distdir) -name '*~'` rm -f `find $(distdir) -name '*.out'` rm -f `find $(distdir) -name '*.core'` -rm -f $(distdir)/*/Makefile $(distdir)/*/*/Makefile cd $(distdir)/doc && /bin/sh update.sh # # Extra rules for cleanup # DISTCLEANFILES= \ libarchive/test/list.h \ tar/test/list.h \ cpio/test/list.h \ cat/test/list.h distclean-local: -rm -rf .ref -rm -rf autom4te.cache/ -rm -f *~ -[ -f libarchive/Makefile ] && cd libarchive && make clean -[ -f libarchive/test/Makefile ] && cd libarchive/test && make clean -[ -f tar/Makefile ] && cd tar && make clean -[ -f tar/test/Makefile ] && cd tar/test && make clean -[ -f cpio/Makefile ] && cd cpio && make clean -[ -f cpio/test/Makefile ] && cd cpio/test && make clean -[ -f cat/Makefile ] && cd cat && make clean -[ -f cpio/test/Makefile ] && cd cat/test && make clean # # Libarchive headers, source, etc. # # include_HEADERS= libarchive/archive.h libarchive/archive_entry.h libarchive_la_SOURCES= \ libarchive/archive_acl.c \ libarchive/archive_acl_private.h \ libarchive/archive_check_magic.c \ libarchive/archive_cmdline.c \ libarchive/archive_cmdline_private.h \ libarchive/archive_crc32.h \ libarchive/archive_cryptor.c \ libarchive/archive_cryptor_private.h \ libarchive/archive_digest.c \ libarchive/archive_digest_private.h \ libarchive/archive_endian.h \ libarchive/archive_entry.c \ libarchive/archive_entry.h \ libarchive/archive_entry_copy_stat.c \ libarchive/archive_entry_link_resolver.c \ libarchive/archive_entry_locale.h \ libarchive/archive_entry_private.h \ libarchive/archive_entry_sparse.c \ libarchive/archive_entry_stat.c \ libarchive/archive_entry_strmode.c \ libarchive/archive_entry_xattr.c \ libarchive/archive_getdate.c \ libarchive/archive_getdate.h \ libarchive/archive_hmac.c \ libarchive/archive_hmac_private.h \ libarchive/archive_match.c \ libarchive/archive_openssl_evp_private.h \ libarchive/archive_openssl_hmac_private.h \ libarchive/archive_options.c \ libarchive/archive_options_private.h \ libarchive/archive_pack_dev.h \ libarchive/archive_pack_dev.c \ libarchive/archive_pathmatch.c \ libarchive/archive_pathmatch.h \ libarchive/archive_platform.h \ libarchive/archive_ppmd_private.h \ libarchive/archive_ppmd7.c \ libarchive/archive_ppmd7_private.h \ libarchive/archive_private.h \ libarchive/archive_random.c \ libarchive/archive_random_private.h \ libarchive/archive_rb.c \ libarchive/archive_rb.h \ libarchive/archive_read.c \ libarchive/archive_read_add_passphrase.c \ libarchive/archive_read_append_filter.c \ libarchive/archive_read_data_into_fd.c \ libarchive/archive_read_disk_entry_from_file.c \ libarchive/archive_read_disk_posix.c \ libarchive/archive_read_disk_private.h \ libarchive/archive_read_disk_set_standard_lookup.c \ libarchive/archive_read_extract.c \ libarchive/archive_read_extract2.c \ libarchive/archive_read_open_fd.c \ libarchive/archive_read_open_file.c \ libarchive/archive_read_open_filename.c \ libarchive/archive_read_open_memory.c \ libarchive/archive_read_private.h \ libarchive/archive_read_set_format.c \ libarchive/archive_read_set_options.c \ libarchive/archive_read_support_filter_all.c \ libarchive/archive_read_support_filter_bzip2.c \ libarchive/archive_read_support_filter_compress.c \ libarchive/archive_read_support_filter_grzip.c \ libarchive/archive_read_support_filter_gzip.c \ libarchive/archive_read_support_filter_lrzip.c \ libarchive/archive_read_support_filter_lz4.c \ libarchive/archive_read_support_filter_lzop.c \ libarchive/archive_read_support_filter_none.c \ libarchive/archive_read_support_filter_program.c \ libarchive/archive_read_support_filter_rpm.c \ libarchive/archive_read_support_filter_uu.c \ libarchive/archive_read_support_filter_xz.c \ libarchive/archive_read_support_format_7zip.c \ libarchive/archive_read_support_format_all.c \ libarchive/archive_read_support_format_ar.c \ libarchive/archive_read_support_format_by_code.c \ libarchive/archive_read_support_format_cab.c \ libarchive/archive_read_support_format_cpio.c \ libarchive/archive_read_support_format_empty.c \ libarchive/archive_read_support_format_iso9660.c \ libarchive/archive_read_support_format_lha.c \ libarchive/archive_read_support_format_mtree.c \ libarchive/archive_read_support_format_rar.c \ libarchive/archive_read_support_format_raw.c \ libarchive/archive_read_support_format_tar.c \ libarchive/archive_read_support_format_warc.c \ libarchive/archive_read_support_format_xar.c \ libarchive/archive_read_support_format_zip.c \ libarchive/archive_string.c \ libarchive/archive_string.h \ libarchive/archive_string_composition.h \ libarchive/archive_string_sprintf.c \ libarchive/archive_util.c \ libarchive/archive_virtual.c \ libarchive/archive_write.c \ libarchive/archive_write_disk_acl.c \ libarchive/archive_write_disk_posix.c \ libarchive/archive_write_disk_private.h \ libarchive/archive_write_disk_set_standard_lookup.c \ libarchive/archive_write_open_fd.c \ libarchive/archive_write_open_file.c \ libarchive/archive_write_open_filename.c \ libarchive/archive_write_open_memory.c \ libarchive/archive_write_private.h \ libarchive/archive_write_add_filter.c \ libarchive/archive_write_add_filter_b64encode.c \ libarchive/archive_write_add_filter_by_name.c \ libarchive/archive_write_add_filter_bzip2.c \ libarchive/archive_write_add_filter_compress.c \ libarchive/archive_write_add_filter_grzip.c \ libarchive/archive_write_add_filter_gzip.c \ libarchive/archive_write_add_filter_lrzip.c \ libarchive/archive_write_add_filter_lz4.c \ libarchive/archive_write_add_filter_lzop.c \ libarchive/archive_write_add_filter_none.c \ libarchive/archive_write_add_filter_program.c \ libarchive/archive_write_add_filter_uuencode.c \ libarchive/archive_write_add_filter_xz.c \ libarchive/archive_write_set_format.c \ libarchive/archive_write_set_format_7zip.c \ libarchive/archive_write_set_format_ar.c \ libarchive/archive_write_set_format_by_name.c \ libarchive/archive_write_set_format_cpio.c \ libarchive/archive_write_set_format_cpio_newc.c \ libarchive/archive_write_set_format_filter_by_ext.c \ libarchive/archive_write_set_format_iso9660.c \ libarchive/archive_write_set_format_mtree.c \ libarchive/archive_write_set_format_pax.c \ libarchive/archive_write_set_format_raw.c \ libarchive/archive_write_set_format_shar.c \ libarchive/archive_write_set_format_ustar.c \ libarchive/archive_write_set_format_v7tar.c \ libarchive/archive_write_set_format_gnutar.c \ libarchive/archive_write_set_format_warc.c \ libarchive/archive_write_set_format_xar.c \ libarchive/archive_write_set_format_zip.c \ libarchive/archive_write_set_options.c \ libarchive/archive_write_set_passphrase.c \ libarchive/archive_xxhash.h \ libarchive/config_freebsd.h \ libarchive/filter_fork_posix.c \ libarchive/filter_fork.h \ libarchive/xxhash.c if INC_WINDOWS_FILES libarchive_la_SOURCES+= \ libarchive/archive_entry_copy_bhfi.c \ libarchive/archive_read_disk_windows.c \ libarchive/archive_windows.h \ libarchive/archive_windows.c \ libarchive/archive_write_disk_windows.c \ libarchive/filter_fork_windows.c endif # -no-undefined marks that libarchive doesn't rely on symbols # defined in the application. This is mandatory for cygwin. libarchive_la_LDFLAGS= -no-undefined -version-info $(ARCHIVE_LIBTOOL_VERSION) libarchive_la_LIBADD= $(LTLIBICONV) # Manpages to install libarchive_man_MANS= \ libarchive/archive_entry.3 \ libarchive/archive_entry_acl.3 \ libarchive/archive_entry_linkify.3 \ libarchive/archive_entry_paths.3 \ libarchive/archive_entry_perms.3 \ libarchive/archive_entry_stat.3 \ libarchive/archive_entry_time.3 \ libarchive/archive_read.3 \ libarchive/archive_read_add_passphrase.3 \ libarchive/archive_read_data.3 \ libarchive/archive_read_disk.3 \ libarchive/archive_read_extract.3 \ libarchive/archive_read_filter.3 \ libarchive/archive_read_format.3 \ libarchive/archive_read_free.3 \ libarchive/archive_read_header.3 \ libarchive/archive_read_new.3 \ libarchive/archive_read_open.3 \ libarchive/archive_read_set_options.3 \ libarchive/archive_util.3 \ libarchive/archive_write.3 \ libarchive/archive_write_blocksize.3 \ libarchive/archive_write_data.3 \ libarchive/archive_write_disk.3 \ libarchive/archive_write_filter.3 \ libarchive/archive_write_finish_entry.3 \ libarchive/archive_write_format.3 \ libarchive/archive_write_free.3 \ libarchive/archive_write_header.3 \ libarchive/archive_write_new.3 \ libarchive/archive_write_open.3 \ libarchive/archive_write_set_options.3 \ libarchive/archive_write_set_passphrase.3 \ libarchive/cpio.5 \ libarchive/libarchive.3 \ libarchive/libarchive_changes.3 \ libarchive/libarchive_internals.3 \ libarchive/libarchive-formats.5 \ libarchive/mtree.5 \ libarchive/tar.5 # Additional libarchive files to include in the distribution libarchive_EXTRA_DIST= \ libarchive/archive_windows.c \ libarchive/archive_windows.h \ libarchive/filter_fork_windows.c \ libarchive/CMakeLists.txt \ $(libarchive_man_MANS) # pkgconfig pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = build/pkgconfig/libarchive.pc # Sources needed by all test programs test_utils_SOURCES= \ test_utils/test_utils.c \ test_utils/test_utils.h # # # libarchive_test program # # libarchive_test_SOURCES= \ $(libarchive_la_SOURCES) \ $(test_utils_SOURCES) \ libarchive/test/main.c \ libarchive/test/read_open_memory.c \ libarchive/test/test.h \ libarchive/test/test_acl_freebsd_posix1e.c \ libarchive/test/test_acl_freebsd_nfs4.c \ libarchive/test/test_acl_nfs4.c \ libarchive/test/test_acl_pax.c \ libarchive/test/test_acl_posix1e.c \ libarchive/test/test_archive_api_feature.c \ libarchive/test/test_archive_clear_error.c \ libarchive/test/test_archive_cmdline.c \ libarchive/test/test_archive_digest.c \ libarchive/test/test_archive_getdate.c \ libarchive/test/test_archive_match_owner.c \ libarchive/test/test_archive_match_path.c \ libarchive/test/test_archive_match_time.c \ libarchive/test/test_archive_pathmatch.c \ libarchive/test/test_archive_read_add_passphrase.c \ libarchive/test/test_archive_read_close_twice.c \ libarchive/test/test_archive_read_close_twice_open_fd.c \ libarchive/test/test_archive_read_close_twice_open_filename.c \ libarchive/test/test_archive_read_multiple_data_objects.c \ libarchive/test/test_archive_read_next_header_empty.c \ libarchive/test/test_archive_read_next_header_raw.c \ libarchive/test/test_archive_read_open2.c \ libarchive/test/test_archive_read_set_filter_option.c \ libarchive/test/test_archive_read_set_format_option.c \ libarchive/test/test_archive_read_set_option.c \ libarchive/test/test_archive_read_set_options.c \ libarchive/test/test_archive_read_support.c \ libarchive/test/test_archive_set_error.c \ libarchive/test/test_archive_string.c \ libarchive/test/test_archive_string_conversion.c \ libarchive/test/test_archive_write_add_filter_by_name.c \ libarchive/test/test_archive_write_set_filter_option.c \ libarchive/test/test_archive_write_set_format_by_name.c \ libarchive/test/test_archive_write_set_format_filter_by_ext.c \ libarchive/test/test_archive_write_set_format_option.c \ libarchive/test/test_archive_write_set_option.c \ libarchive/test/test_archive_write_set_options.c \ libarchive/test/test_archive_write_set_passphrase.c \ libarchive/test/test_bad_fd.c \ libarchive/test/test_compat_bzip2.c \ libarchive/test/test_compat_cpio.c \ libarchive/test/test_compat_gtar.c \ libarchive/test/test_compat_gzip.c \ libarchive/test/test_compat_lz4.c \ libarchive/test/test_compat_lzip.c \ libarchive/test/test_compat_lzma.c \ libarchive/test/test_compat_lzop.c \ libarchive/test/test_compat_mac.c \ libarchive/test/test_compat_pax_libarchive_2x.c \ libarchive/test/test_compat_perl_archive_tar.c \ libarchive/test/test_compat_plexus_archiver_tar.c \ libarchive/test/test_compat_solaris_tar_acl.c \ libarchive/test/test_compat_solaris_pax_sparse.c \ libarchive/test/test_compat_star_acl_posix1e.c \ libarchive/test/test_compat_tar_hardlink.c \ libarchive/test/test_compat_uudecode.c \ libarchive/test/test_compat_uudecode_large.c \ libarchive/test/test_compat_xz.c \ libarchive/test/test_compat_zip.c \ libarchive/test/test_empty_write.c \ libarchive/test/test_entry.c \ libarchive/test/test_entry_strmode.c \ libarchive/test/test_extattr_freebsd.c \ libarchive/test/test_filter_count.c \ libarchive/test/test_fuzz.c \ libarchive/test/test_gnutar_filename_encoding.c \ libarchive/test/test_link_resolver.c \ libarchive/test/test_open_failure.c \ libarchive/test/test_open_fd.c \ libarchive/test/test_open_file.c \ libarchive/test/test_open_filename.c \ libarchive/test/test_pax_filename_encoding.c \ libarchive/test/test_read_data_large.c \ libarchive/test/test_read_disk.c \ libarchive/test/test_read_disk_directory_traversals.c \ libarchive/test/test_read_disk_entry_from_file.c \ libarchive/test/test_read_extract.c \ libarchive/test/test_read_file_nonexistent.c \ libarchive/test/test_read_filter_compress.c \ libarchive/test/test_read_filter_grzip.c \ libarchive/test/test_read_filter_lrzip.c \ libarchive/test/test_read_filter_lzop.c \ libarchive/test/test_read_filter_lzop_multiple_parts.c \ libarchive/test/test_read_filter_program.c \ libarchive/test/test_read_filter_program_signature.c \ libarchive/test/test_read_filter_uudecode.c \ libarchive/test/test_read_format_7zip.c \ libarchive/test/test_read_format_7zip_encryption_data.c \ libarchive/test/test_read_format_7zip_encryption_partially.c \ libarchive/test/test_read_format_7zip_encryption_header.c \ libarchive/test/test_read_format_7zip_malformed.c \ libarchive/test/test_read_format_ar.c \ libarchive/test/test_read_format_cab.c \ libarchive/test/test_read_format_cab_filename.c \ libarchive/test/test_read_format_cpio_afio.c \ libarchive/test/test_read_format_cpio_bin.c \ libarchive/test/test_read_format_cpio_bin_Z.c \ libarchive/test/test_read_format_cpio_bin_be.c \ libarchive/test/test_read_format_cpio_bin_bz2.c \ libarchive/test/test_read_format_cpio_bin_gz.c \ libarchive/test/test_read_format_cpio_bin_le.c \ libarchive/test/test_read_format_cpio_bin_lzip.c \ libarchive/test/test_read_format_cpio_bin_lzma.c \ libarchive/test/test_read_format_cpio_bin_xz.c \ libarchive/test/test_read_format_cpio_filename.c \ libarchive/test/test_read_format_cpio_odc.c \ libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.c \ libarchive/test/test_read_format_cpio_svr4_gzip.c \ libarchive/test/test_read_format_cpio_svr4_gzip_rpm.c \ libarchive/test/test_read_format_cpio_svr4c_Z.c \ libarchive/test/test_read_format_empty.c \ libarchive/test/test_read_format_gtar_filename.c \ libarchive/test/test_read_format_gtar_gz.c \ libarchive/test/test_read_format_gtar_lzma.c \ libarchive/test/test_read_format_gtar_sparse.c \ libarchive/test/test_read_format_gtar_sparse_skip_entry.c \ libarchive/test/test_read_format_iso_Z.c \ libarchive/test/test_read_format_iso_multi_extent.c \ libarchive/test/test_read_format_iso_xorriso.c \ libarchive/test/test_read_format_isojoliet_bz2.c \ libarchive/test/test_read_format_isojoliet_long.c \ libarchive/test/test_read_format_isojoliet_rr.c \ libarchive/test/test_read_format_isojoliet_versioned.c \ libarchive/test/test_read_format_isorr_bz2.c \ libarchive/test/test_read_format_isorr_ce.c \ libarchive/test/test_read_format_isorr_new_bz2.c \ libarchive/test/test_read_format_isorr_rr_moved.c \ libarchive/test/test_read_format_isozisofs_bz2.c \ libarchive/test/test_read_format_lha.c \ libarchive/test/test_read_format_lha_bugfix_0.c \ libarchive/test/test_read_format_lha_filename.c \ libarchive/test/test_read_format_mtree.c \ libarchive/test/test_read_format_mtree_crash747.c \ libarchive/test/test_read_format_pax_bz2.c \ libarchive/test/test_read_format_rar.c \ libarchive/test/test_read_format_rar_encryption_data.c \ libarchive/test/test_read_format_rar_encryption_partially.c \ libarchive/test/test_read_format_rar_encryption_header.c \ libarchive/test/test_read_format_rar_invalid1.c \ libarchive/test/test_read_format_raw.c \ libarchive/test/test_read_format_tar.c \ libarchive/test/test_read_format_tar_concatenated.c \ libarchive/test/test_read_format_tar_empty_pax.c \ libarchive/test/test_read_format_tar_empty_filename.c \ libarchive/test/test_read_format_tar_filename.c \ libarchive/test/test_read_format_tbz.c \ libarchive/test/test_read_format_tgz.c \ libarchive/test/test_read_format_tlz.c \ libarchive/test/test_read_format_txz.c \ libarchive/test/test_read_format_tz.c \ libarchive/test/test_read_format_ustar_filename.c \ libarchive/test/test_read_format_warc.c \ libarchive/test/test_read_format_xar.c \ libarchive/test/test_read_format_zip.c \ libarchive/test/test_read_format_zip_comment_stored.c \ libarchive/test/test_read_format_zip_encryption_data.c \ libarchive/test/test_read_format_zip_encryption_partially.c \ libarchive/test/test_read_format_zip_encryption_header.c \ libarchive/test/test_read_format_zip_filename.c \ libarchive/test/test_read_format_zip_high_compression.c \ + libarchive/test/test_read_format_zip_jar.c \ libarchive/test/test_read_format_zip_mac_metadata.c \ libarchive/test/test_read_format_zip_malformed.c \ libarchive/test/test_read_format_zip_msdos.c \ libarchive/test/test_read_format_zip_nested.c \ libarchive/test/test_read_format_zip_nofiletype.c \ libarchive/test/test_read_format_zip_padded.c \ libarchive/test/test_read_format_zip_sfx.c \ libarchive/test/test_read_format_zip_traditional_encryption_data.c \ libarchive/test/test_read_format_zip_winzip_aes.c \ libarchive/test/test_read_format_zip_winzip_aes_large.c \ libarchive/test/test_read_format_zip_zip64.c \ libarchive/test/test_read_large.c \ libarchive/test/test_read_pax_truncated.c \ libarchive/test/test_read_position.c \ libarchive/test/test_read_set_format.c \ libarchive/test/test_read_too_many_filters.c \ libarchive/test/test_read_truncated.c \ libarchive/test/test_read_truncated_filter.c \ libarchive/test/test_sparse_basic.c \ libarchive/test/test_tar_filenames.c \ libarchive/test/test_tar_large.c \ libarchive/test/test_ustar_filenames.c \ libarchive/test/test_ustar_filename_encoding.c \ libarchive/test/test_warn_missing_hardlink_target.c \ libarchive/test/test_write_disk.c \ libarchive/test/test_write_disk_appledouble.c \ libarchive/test/test_write_disk_failures.c \ libarchive/test/test_write_disk_hardlink.c \ libarchive/test/test_write_disk_hfs_compression.c \ libarchive/test/test_write_disk_lookup.c \ libarchive/test/test_write_disk_mac_metadata.c \ libarchive/test/test_write_disk_no_hfs_compression.c \ libarchive/test/test_write_disk_perms.c \ libarchive/test/test_write_disk_secure.c \ libarchive/test/test_write_disk_secure744.c \ libarchive/test/test_write_disk_secure745.c \ libarchive/test/test_write_disk_secure746.c \ libarchive/test/test_write_disk_sparse.c \ libarchive/test/test_write_disk_symlink.c \ libarchive/test/test_write_disk_times.c \ libarchive/test/test_write_filter_b64encode.c \ libarchive/test/test_write_filter_bzip2.c \ libarchive/test/test_write_filter_compress.c \ libarchive/test/test_write_filter_gzip.c \ libarchive/test/test_write_filter_gzip_timestamp.c \ libarchive/test/test_write_filter_lrzip.c \ libarchive/test/test_write_filter_lz4.c \ libarchive/test/test_write_filter_lzip.c \ libarchive/test/test_write_filter_lzma.c \ libarchive/test/test_write_filter_lzop.c \ libarchive/test/test_write_filter_program.c \ libarchive/test/test_write_filter_uuencode.c \ libarchive/test/test_write_filter_xz.c \ libarchive/test/test_write_format_7zip.c \ libarchive/test/test_write_format_7zip_empty.c \ libarchive/test/test_write_format_7zip_large.c \ libarchive/test/test_write_format_ar.c \ libarchive/test/test_write_format_cpio.c \ libarchive/test/test_write_format_cpio_empty.c \ libarchive/test/test_write_format_cpio_newc.c \ libarchive/test/test_write_format_cpio_odc.c \ libarchive/test/test_write_format_gnutar.c \ libarchive/test/test_write_format_gnutar_filenames.c \ libarchive/test/test_write_format_iso9660.c \ libarchive/test/test_write_format_iso9660_boot.c \ libarchive/test/test_write_format_iso9660_empty.c \ libarchive/test/test_write_format_iso9660_filename.c \ libarchive/test/test_write_format_iso9660_zisofs.c \ libarchive/test/test_write_format_mtree.c \ libarchive/test/test_write_format_mtree_absolute_path.c \ libarchive/test/test_write_format_mtree_classic.c \ libarchive/test/test_write_format_mtree_classic_indent.c\ libarchive/test/test_write_format_mtree_fflags.c \ libarchive/test/test_write_format_mtree_no_separator.c \ libarchive/test/test_write_format_mtree_quoted_filename.c\ libarchive/test/test_write_format_pax.c \ libarchive/test/test_write_format_raw.c \ libarchive/test/test_write_format_raw_b64.c \ libarchive/test/test_write_format_shar_empty.c \ libarchive/test/test_write_format_tar.c \ libarchive/test/test_write_format_tar_empty.c \ libarchive/test/test_write_format_tar_sparse.c \ libarchive/test/test_write_format_tar_ustar.c \ libarchive/test/test_write_format_tar_v7tar.c \ libarchive/test/test_write_format_warc.c \ libarchive/test/test_write_format_warc_empty.c \ libarchive/test/test_write_format_xar.c \ libarchive/test/test_write_format_xar_empty.c \ libarchive/test/test_write_format_zip.c \ libarchive/test/test_write_format_zip_compression_store.c \ libarchive/test/test_write_format_zip_empty.c \ libarchive/test/test_write_format_zip_empty_zip64.c \ libarchive/test/test_write_format_zip_file.c \ libarchive/test/test_write_format_zip_file_zip64.c \ libarchive/test/test_write_format_zip_large.c \ libarchive/test/test_write_format_zip_zip64.c \ libarchive/test/test_write_open_memory.c \ libarchive/test/test_write_read_format_zip.c \ libarchive/test/test_zip_filename_encoding.c libarchive_test_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/test_utils -I$(top_builddir)/libarchive/test -DLIBARCHIVE_STATIC $(PLATFORMCPPFLAGS) libarchive_test_LDADD= $(LTLIBICONV) # The "list.h" file just lists all of the tests defined in all of the sources. # Building it automatically provides a sanity-check on libarchive_test_SOURCES # above. libarchive/test/list.h: Makefile $(MKDIR_P) libarchive/test cat $(top_srcdir)/libarchive/test/test_*.c | grep '^DEFINE_TEST' > libarchive/test/list.h libarchive_TESTS_ENVIRONMENT= LIBARCHIVE_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/libarchive/test LRZIP=NOCONFIG libarchive_test_EXTRA_DIST=\ libarchive/test/list.h \ libarchive/test/test_acl_pax.tar.uu \ libarchive/test/test_archive_string_conversion.txt.Z.uu \ libarchive/test/test_compat_bzip2_1.tbz.uu \ libarchive/test/test_compat_bzip2_2.tbz.uu \ libarchive/test/test_compat_cpio_1.cpio.uu \ libarchive/test/test_compat_gtar_1.tar.uu \ libarchive/test/test_compat_gtar_2.tar.uu \ libarchive/test/test_compat_gzip_1.tgz.uu \ libarchive/test/test_compat_gzip_2.tgz.uu \ libarchive/test/test_compat_lz4_1.tar.lz4.uu \ libarchive/test/test_compat_lz4_2.tar.lz4.uu \ libarchive/test/test_compat_lz4_3.tar.lz4.uu \ libarchive/test/test_compat_lz4_B4.tar.lz4.uu \ libarchive/test/test_compat_lz4_B4BD.tar.lz4.uu \ libarchive/test/test_compat_lz4_B4BDBX.tar.lz4.uu \ libarchive/test/test_compat_lz4_B5.tar.lz4.uu \ libarchive/test/test_compat_lz4_B5BD.tar.lz4.uu \ libarchive/test/test_compat_lz4_B6.tar.lz4.uu \ libarchive/test/test_compat_lz4_B6BD.tar.lz4.uu \ libarchive/test/test_compat_lz4_B7.tar.lz4.uu \ libarchive/test/test_compat_lz4_B7BD.tar.lz4.uu \ libarchive/test/test_compat_lzip_1.tlz.uu \ libarchive/test/test_compat_lzip_2.tlz.uu \ libarchive/test/test_compat_lzma_1.tlz.uu \ libarchive/test/test_compat_lzma_2.tlz.uu \ libarchive/test/test_compat_lzma_3.tlz.uu \ libarchive/test/test_compat_lzop_1.tar.lzo.uu \ libarchive/test/test_compat_lzop_2.tar.lzo.uu \ libarchive/test/test_compat_lzop_3.tar.lzo.uu \ libarchive/test/test_compat_mac-1.tar.Z.uu \ libarchive/test/test_compat_mac-2.tar.Z.uu \ libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu \ libarchive/test/test_compat_perl_archive_tar.tar.uu \ libarchive/test/test_compat_plexus_archiver_tar.uu \ libarchive/test/test_compat_solaris_pax_sparse_1.pax.Z.uu \ libarchive/test/test_compat_solaris_pax_sparse_2.pax.Z.uu \ libarchive/test/test_compat_solaris_tar_acl.tar.uu \ libarchive/test/test_compat_star_acl_posix1e.tar.uu \ libarchive/test/test_compat_tar_hardlink_1.tar.uu \ libarchive/test/test_compat_uudecode_large.tar.Z.uu \ libarchive/test/test_compat_xz_1.txz.uu \ libarchive/test/test_compat_zip_1.zip.uu \ libarchive/test/test_compat_zip_2.zip.uu \ libarchive/test/test_compat_zip_3.zip.uu \ libarchive/test/test_compat_zip_4.zip.uu \ libarchive/test/test_compat_zip_5.zip.uu \ libarchive/test/test_compat_zip_6.zip.uu \ libarchive/test/test_compat_zip_7.xps.uu \ libarchive/test/test_fuzz.cab.uu \ libarchive/test/test_fuzz.lzh.uu \ libarchive/test/test_fuzz_1.iso.Z.uu \ libarchive/test/test_pax_filename_encoding.tar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part1.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part2.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part3.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part4.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part5.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part6.rar.uu \ libarchive/test/test_rar_multivolume_single_file.part1.rar.uu \ libarchive/test/test_rar_multivolume_single_file.part2.rar.uu \ libarchive/test/test_rar_multivolume_single_file.part3.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part01.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part02.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part03.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part04.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part05.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part06.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part07.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part08.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part09.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part10.rar.uu \ libarchive/test/test_read_filter_grzip.tar.grz.uu \ libarchive/test/test_read_filter_lrzip.tar.lrz.uu \ libarchive/test/test_read_filter_lzop.tar.lzo.uu \ libarchive/test/test_read_filter_lzop_multiple_parts.tar.lzo.uu \ libarchive/test/test_read_format_mtree_crash747.mtree.bz2.uu \ libarchive/test/test_read_format_7zip_bcj2_bzip2.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_copy_1.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_copy_2.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_copy_lzma.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_deflate.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma1_1.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma1_2.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma2_1.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma2_2.7z.uu \ libarchive/test/test_read_format_7zip_bcj_bzip2.7z.uu \ libarchive/test/test_read_format_7zip_bcj_copy.7z.uu \ libarchive/test/test_read_format_7zip_bcj_deflate.7z.uu \ libarchive/test/test_read_format_7zip_bcj_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_bzip2.7z.uu \ libarchive/test/test_read_format_7zip_copy.7z.uu \ libarchive/test/test_read_format_7zip_copy_2.7z.uu \ libarchive/test/test_read_format_7zip_deflate.7z.uu \ libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_empty_archive.7z.uu \ libarchive/test/test_read_format_7zip_empty_file.7z.uu \ libarchive/test/test_read_format_7zip_encryption.7z.uu \ libarchive/test/test_read_format_7zip_encryption_header.7z.uu \ libarchive/test/test_read_format_7zip_encryption_partially.7z.uu \ libarchive/test/test_read_format_7zip_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_2.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_malformed.7z.uu \ libarchive/test/test_read_format_7zip_malformed2.7z.uu \ libarchive/test/test_read_format_7zip_ppmd.7z.uu \ libarchive/test/test_read_format_7zip_symbolic_name.7z.uu \ libarchive/test/test_read_format_ar.ar.uu \ libarchive/test/test_read_format_cab_1.cab.uu \ libarchive/test/test_read_format_cab_2.cab.uu \ libarchive/test/test_read_format_cab_3.cab.uu \ libarchive/test/test_read_format_cab_filename_cp932.cab.uu \ libarchive/test/test_read_format_cpio_bin_be.cpio.uu \ libarchive/test/test_read_format_cpio_bin_le.cpio.uu \ libarchive/test/test_read_format_cpio_filename_cp866.cpio.uu \ libarchive/test/test_read_format_cpio_filename_eucjp.cpio.uu \ libarchive/test/test_read_format_cpio_filename_koi8r.cpio.uu \ libarchive/test/test_read_format_cpio_filename_utf8_jp.cpio.uu \ libarchive/test/test_read_format_cpio_filename_utf8_ru.cpio.uu \ libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.rpm.uu \ libarchive/test/test_read_format_cpio_svr4_gzip_rpm.rpm.uu \ libarchive/test/test_read_format_gtar_filename_cp866.tar.Z.uu \ libarchive/test/test_read_format_gtar_filename_eucjp.tar.Z.uu \ libarchive/test/test_read_format_gtar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_gtar_sparse_1_13.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix00.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu \ libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu \ libarchive/test/test_read_format_iso.iso.Z.uu \ libarchive/test/test_read_format_iso_2.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet_by_nero.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet_long.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet_rockridge.iso.Z.uu \ libarchive/test/test_read_format_iso_multi_extent.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_ce.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_new.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_rr_moved.iso.Z.uu \ libarchive/test/test_read_format_iso_xorriso.iso.Z.uu \ libarchive/test/test_read_format_iso_zisofs.iso.Z.uu \ libarchive/test/test_read_format_lha_bugfix_0.lzh.uu \ libarchive/test/test_read_format_lha_filename_cp932.lzh.uu \ libarchive/test/test_read_format_lha_header0.lzh.uu \ libarchive/test/test_read_format_lha_header1.lzh.uu \ libarchive/test/test_read_format_lha_header2.lzh.uu \ libarchive/test/test_read_format_lha_header3.lzh.uu \ libarchive/test/test_read_format_lha_lh0.lzh.uu \ libarchive/test/test_read_format_lha_lh6.lzh.uu \ libarchive/test/test_read_format_lha_lh7.lzh.uu \ libarchive/test/test_read_format_lha_withjunk.lzh.uu \ libarchive/test/test_read_format_mtree.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic2.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic3.mtree.uu \ libarchive/test/test_read_format_rar.rar.uu \ libarchive/test/test_read_format_rar_binary_data.rar.uu \ libarchive/test/test_read_format_rar_compress_best.rar.uu \ libarchive/test/test_read_format_rar_compress_normal.rar.uu \ libarchive/test/test_read_format_rar_encryption_data.rar.uu \ libarchive/test/test_read_format_rar_encryption_header.rar.uu \ libarchive/test/test_read_format_rar_encryption_partially.rar.uu \ libarchive/test/test_read_format_rar_invalid1.rar.uu \ libarchive/test/test_read_format_rar_multi_lzss_blocks.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0001.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0002.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0003.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0004.rar.uu \ libarchive/test/test_read_format_rar_noeof.rar.uu \ libarchive/test/test_read_format_rar_ppmd_lzss_conversion.rar.uu \ libarchive/test/test_read_format_rar_sfx.exe.uu \ libarchive/test/test_read_format_rar_subblock.rar.uu \ libarchive/test/test_read_format_rar_unicode.rar.uu \ libarchive/test/test_read_format_rar_windows.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ libarchive/test/test_read_format_raw.data.uu \ libarchive/test/test_read_format_tar_concatenated.tar.uu \ libarchive/test/test_read_format_tar_empty_filename.tar.uu \ libarchive/test/test_read_format_tar_empty_pax.tar.Z.uu \ libarchive/test/test_read_format_tar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_cp866.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_eucjp.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_warc.warc.uu \ libarchive/test/test_read_format_zip.zip.uu \ libarchive/test/test_read_format_zip_comment_stored_1.zip.uu \ libarchive/test/test_read_format_zip_comment_stored_2.zip.uu \ libarchive/test/test_read_format_zip_encryption_data.zip.uu \ libarchive/test/test_read_format_zip_encryption_header.zip.uu \ libarchive/test/test_read_format_zip_encryption_partially.zip.uu \ libarchive/test/test_read_format_zip_filename_cp866.zip.uu \ libarchive/test/test_read_format_zip_filename_cp932.zip.uu \ libarchive/test/test_read_format_zip_filename_koi8r.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_jp.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_ru.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_ru2.zip.uu \ libarchive/test/test_read_format_zip_high_compression.zip.uu \ libarchive/test/test_read_format_zip_length_at_end.zip.uu \ + libarchive/test/test_read_format_zip_jar.jar.uu \ libarchive/test/test_read_format_zip_mac_metadata.zip.uu \ libarchive/test/test_read_format_zip_malformed1.zip.uu \ libarchive/test/test_read_format_zip_msdos.zip.uu \ libarchive/test/test_read_format_zip_nested.zip.uu \ libarchive/test/test_read_format_zip_nofiletype.zip.uu \ libarchive/test/test_read_format_zip_padded1.zip.uu \ libarchive/test/test_read_format_zip_padded2.zip.uu \ libarchive/test/test_read_format_zip_padded3.zip.uu \ libarchive/test/test_read_format_zip_sfx.uu \ libarchive/test/test_read_format_zip_symlink.zip.uu \ libarchive/test/test_read_format_zip_traditional_encryption_data.zip.uu \ libarchive/test/test_read_format_zip_ux.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes128.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256_large.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256_stored.zip.uu \ libarchive/test/test_read_format_zip_zip64a.zip.uu \ libarchive/test/test_read_format_zip_zip64b.zip.uu \ libarchive/test/test_read_large_splitted_rar_aa.uu \ libarchive/test/test_read_large_splitted_rar_ab.uu \ libarchive/test/test_read_large_splitted_rar_ac.uu \ libarchive/test/test_read_large_splitted_rar_ad.uu \ libarchive/test/test_read_large_splitted_rar_ae.uu \ libarchive/test/test_read_splitted_rar_aa.uu \ libarchive/test/test_read_splitted_rar_ab.uu \ libarchive/test/test_read_splitted_rar_ac.uu \ libarchive/test/test_read_splitted_rar_ad.uu \ libarchive/test/test_read_too_many_filters.gz.uu \ libarchive/test/test_splitted_rar_seek_support_aa.uu \ libarchive/test/test_splitted_rar_seek_support_ab.uu \ libarchive/test/test_splitted_rar_seek_support_ac.uu \ libarchive/test/test_write_disk_appledouble.cpio.gz.uu \ libarchive/test/test_write_disk_hfs_compression.tgz.uu \ libarchive/test/test_write_disk_mac_metadata.tar.gz.uu \ libarchive/test/test_write_disk_no_hfs_compression.tgz.uu \ libarchive/test/CMakeLists.txt \ libarchive/test/README # # Common code for libarchive frontends (cpio, tar) # libarchive_fe_la_SOURCES= \ libarchive_fe/err.c \ libarchive_fe/err.h \ libarchive_fe/lafe_platform.h \ libarchive_fe/line_reader.c \ libarchive_fe/line_reader.h \ libarchive_fe/passphrase.c \ libarchive_fe/passphrase.h libarchive_fe_la_CPPFLAGS= -I$(top_srcdir)/libarchive # # # bsdtar source, docs, etc. # # bsdtar_SOURCES= \ tar/bsdtar.c \ tar/bsdtar.h \ tar/bsdtar_platform.h \ tar/cmdline.c \ tar/creation_set.c \ tar/read.c \ tar/subst.c \ tar/util.c \ tar/write.c if INC_WINDOWS_FILES bsdtar_SOURCES+= \ tar/bsdtar_windows.h \ tar/bsdtar_windows.c endif bsdtar_DEPENDENCIES= libarchive.la libarchive_fe.la if STATIC_BSDTAR bsdtar_ldstatic= -static bsdtar_ccstatic= -DLIBARCHIVE_STATIC else bsdtar_ldstatic= bsdtar_ccstatic= endif bsdtar_LDADD= libarchive.la libarchive_fe.la $(LTLIBICONV) bsdtar_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdtar_ccstatic) $(PLATFORMCPPFLAGS) bsdtar_LDFLAGS= $(bsdtar_ldstatic) bsdtar_EXTRA_DIST= \ tar/bsdtar.1 \ tar/bsdtar_windows.h \ tar/bsdtar_windows.c \ tar/CMakeLists.txt \ tar/config_freebsd.h if BUILD_BSDTAR bsdtar_man_MANS= tar/bsdtar.1 bsdtar_programs= bsdtar else bsdtar_man_MANS= bsdtar_programs= endif # # bsdtar_test # bsdtar_test_SOURCES= \ $(test_utils_SOURCES) \ tar/test/main.c \ tar/test/test.h \ tar/test/test_0.c \ tar/test/test_basic.c \ tar/test/test_copy.c \ tar/test/test_empty_mtree.c \ tar/test/test_extract_tar_Z.c \ tar/test/test_extract_tar_bz2.c \ tar/test/test_extract_tar_grz.c \ tar/test/test_extract_tar_gz.c \ tar/test/test_extract_tar_lrz.c \ tar/test/test_extract_tar_lz.c \ tar/test/test_extract_tar_lz4.c \ tar/test/test_extract_tar_lzma.c \ tar/test/test_extract_tar_lzo.c \ tar/test/test_extract_tar_xz.c \ tar/test/test_format_newc.c \ tar/test/test_help.c \ tar/test/test_leading_slash.c \ tar/test/test_missing_file.c \ tar/test/test_option_C_upper.c \ tar/test/test_option_H_upper.c \ tar/test/test_option_L_upper.c \ tar/test/test_option_O_upper.c \ tar/test/test_option_T_upper.c \ tar/test/test_option_U_upper.c \ tar/test/test_option_X_upper.c \ tar/test/test_option_a.c \ tar/test/test_option_b.c \ tar/test/test_option_b64encode.c \ tar/test/test_option_exclude.c \ tar/test/test_option_gid_gname.c \ tar/test/test_option_grzip.c \ tar/test/test_option_j.c \ tar/test/test_option_k.c \ tar/test/test_option_keep_newer_files.c \ tar/test/test_option_lrzip.c \ tar/test/test_option_lz4.c \ tar/test/test_option_lzma.c \ tar/test/test_option_lzop.c \ tar/test/test_option_n.c \ tar/test/test_option_newer_than.c \ tar/test/test_option_nodump.c \ tar/test/test_option_older_than.c \ tar/test/test_option_passphrase.c \ tar/test/test_option_q.c \ tar/test/test_option_r.c \ tar/test/test_option_s.c \ tar/test/test_option_uid_uname.c \ tar/test/test_option_uuencode.c \ tar/test/test_option_xz.c \ tar/test/test_option_z.c \ tar/test/test_patterns.c \ tar/test/test_print_longpath.c \ tar/test/test_stdio.c \ tar/test/test_strip_components.c \ tar/test/test_symlink_dir.c \ tar/test/test_version.c \ tar/test/test_windows.c bsdtar_test_CPPFLAGS=\ -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \ -I$(top_srcdir)/test_utils \ -I$(top_srcdir)/tar -I$(top_builddir)/tar/test \ $(PLATFORMCPPFLAGS) tar/test/list.h: Makefile $(MKDIR_P) tar/test cat $(top_srcdir)/tar/test/test_*.c | grep '^DEFINE_TEST' > tar/test/list.h if BUILD_BSDTAR bsdtar_test_programs= bsdtar_test bsdtar_TESTS_ENVIRONMENT= BSDTAR=`cd $(top_builddir);/bin/pwd`/bsdtar$(EXEEXT) BSDTAR_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/tar/test else bsdtar_test_programs= bsdtar_TESTS_ENVIRONMENT= endif bsdtar_test_EXTRA_DIST= \ tar/test/list.h \ tar/test/test_extract.tar.Z.uu \ tar/test/test_extract.tar.bz2.uu \ tar/test/test_extract.tar.grz.uu \ tar/test/test_extract.tar.gz.uu \ tar/test/test_extract.tar.lrz.uu \ tar/test/test_extract.tar.lz.uu \ tar/test/test_extract.tar.lz4.uu \ tar/test/test_extract.tar.lzma.uu \ tar/test/test_extract.tar.lzo.uu \ tar/test/test_extract.tar.xz.uu \ tar/test/test_leading_slash.tar.uu \ tar/test/test_option_keep_newer_files.tar.Z.uu \ tar/test/test_option_passphrase.zip.uu \ tar/test/test_option_s.tar.Z.uu \ tar/test/test_patterns_2.tar.uu \ tar/test/test_patterns_3.tar.uu \ tar/test/test_patterns_4.tar.uu \ tar/test/test_print_longpath.tar.Z.uu \ tar/test/CMakeLists.txt # # # bsdcpio source, docs, etc. # # bsdcpio_SOURCES= \ cpio/cmdline.c \ cpio/cpio.c \ cpio/cpio.h \ cpio/cpio_platform.h if INC_WINDOWS_FILES bsdcpio_SOURCES+= \ cpio/cpio_windows.h \ cpio/cpio_windows.c endif bsdcpio_DEPENDENCIES = libarchive.la libarchive_fe.la if STATIC_BSDCPIO bsdcpio_ldstatic= -static bsdcpio_ccstatic= -DLIBARCHIVE_STATIC else bsdcpio_ldstatic= bsdcpio_ccstatic= endif bsdcpio_LDADD= libarchive_fe.la libarchive.la $(LTLIBICONV) bsdcpio_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdcpio_ccstatic) $(PLATFORMCPPFLAGS) bsdcpio_LDFLAGS= $(bsdcpio_ldstatic) bsdcpio_EXTRA_DIST= \ cpio/bsdcpio.1 \ cpio/cpio_windows.h \ cpio/cpio_windows.c \ cpio/CMakeLists.txt \ cpio/config_freebsd.h if BUILD_BSDCPIO # Manpages to install bsdcpio_man_MANS= cpio/bsdcpio.1 bsdcpio_programs= bsdcpio else bsdcpio_man_MANS= bsdcpio_programs= endif # # bsdcpio_test # bsdcpio_test_SOURCES= \ $(test_utils_SOURCES) \ cpio/cmdline.c \ cpio/test/main.c \ cpio/test/test.h \ cpio/test/test_0.c \ cpio/test/test_basic.c \ cpio/test/test_cmdline.c \ cpio/test/test_extract_cpio_Z.c \ cpio/test/test_extract_cpio_bz2.c \ cpio/test/test_extract_cpio_grz.c \ cpio/test/test_extract_cpio_gz.c \ cpio/test/test_extract_cpio_lrz.c \ cpio/test/test_extract_cpio_lz.c \ cpio/test/test_extract_cpio_lz4.c \ cpio/test/test_extract_cpio_lzma.c \ cpio/test/test_extract_cpio_lzo.c \ cpio/test/test_extract_cpio_xz.c \ cpio/test/test_format_newc.c \ cpio/test/test_gcpio_compat.c \ cpio/test/test_missing_file.c \ cpio/test/test_option_0.c \ cpio/test/test_option_B_upper.c \ cpio/test/test_option_C_upper.c \ cpio/test/test_option_J_upper.c \ cpio/test/test_option_L_upper.c \ cpio/test/test_option_Z_upper.c \ cpio/test/test_option_a.c \ cpio/test/test_option_b64encode.c \ cpio/test/test_option_c.c \ cpio/test/test_option_d.c \ cpio/test/test_option_f.c \ cpio/test/test_option_grzip.c \ cpio/test/test_option_help.c \ cpio/test/test_option_l.c \ cpio/test/test_option_lrzip.c \ cpio/test/test_option_lz4.c \ cpio/test/test_option_lzma.c \ cpio/test/test_option_lzop.c \ cpio/test/test_option_m.c \ cpio/test/test_option_passphrase.c \ cpio/test/test_option_t.c \ cpio/test/test_option_u.c \ cpio/test/test_option_uuencode.c \ cpio/test/test_option_version.c \ cpio/test/test_option_xz.c \ cpio/test/test_option_y.c \ cpio/test/test_option_z.c \ cpio/test/test_owner_parse.c \ cpio/test/test_passthrough_dotdot.c \ cpio/test/test_passthrough_reverse.c bsdcpio_test_CPPFLAGS= \ -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \ -I$(top_srcdir)/test_utils \ -I$(top_srcdir)/cpio -I$(top_builddir)/cpio/test \ $(PLATFORMCPPFLAGS) bsdcpio_test_LDADD=libarchive_fe.la cpio/test/list.h: Makefile $(MKDIR_P) cpio/test cat $(top_srcdir)/cpio/test/test_*.c | grep '^DEFINE_TEST' > cpio/test/list.h if BUILD_BSDCPIO bsdcpio_test_programs= bsdcpio_test bsdcpio_TESTS_ENVIRONMENT= BSDCPIO=`cd $(top_builddir);/bin/pwd`/bsdcpio$(EXEEXT) BSDCPIO_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/cpio/test else bsdcpio_test_programs= bsdcpio_TESTS_ENVIRONMENT= endif bsdcpio_test_EXTRA_DIST= \ cpio/test/list.h \ cpio/test/test_extract.cpio.Z.uu \ cpio/test/test_extract.cpio.bz2.uu \ cpio/test/test_extract.cpio.grz.uu \ cpio/test/test_extract.cpio.gz.uu \ cpio/test/test_extract.cpio.lrz.uu \ cpio/test/test_extract.cpio.lz.uu \ cpio/test/test_extract.cpio.lz4.uu \ cpio/test/test_extract.cpio.lzma.uu \ cpio/test/test_extract.cpio.lzo.uu \ cpio/test/test_extract.cpio.xz.uu \ cpio/test/test_gcpio_compat_ref.bin.uu \ cpio/test/test_gcpio_compat_ref.crc.uu \ cpio/test/test_gcpio_compat_ref.newc.uu \ cpio/test/test_gcpio_compat_ref.ustar.uu \ cpio/test/test_gcpio_compat_ref_nosym.bin.uu \ cpio/test/test_gcpio_compat_ref_nosym.crc.uu \ cpio/test/test_gcpio_compat_ref_nosym.newc.uu \ cpio/test/test_gcpio_compat_ref_nosym.ustar.uu \ cpio/test/test_option_f.cpio.uu \ cpio/test/test_option_m.cpio.uu \ cpio/test/test_option_passphrase.zip.uu \ cpio/test/test_option_t.cpio.uu \ cpio/test/test_option_t.stdout.uu \ cpio/test/test_option_tv.stdout.uu \ cpio/test/CMakeLists.txt # # # bsdcat source, docs, etc. # # bsdcat_SOURCES= \ cat/bsdcat.c \ cat/bsdcat.h \ cat/bsdcat_platform.h \ cat/cmdline.c if INC_WINDOWS_FILES bsdcat_SOURCES+= endif bsdcat_DEPENDENCIES = libarchive.la libarchive_fe.la if STATIC_BSDCAT bsdcat_ldstatic= -static bsdcat_ccstatic= -DLIBARCHIVE_STATIC else bsdcat_ldstatic= bsdcat_ccstatic= endif bsdcat_LDADD= libarchive_fe.la libarchive.la $(LTLIBICONV) bsdcat_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdcat_ccstatic) $(PLATFORMCPPFLAGS) bsdcat_LDFLAGS= $(bsdcat_ldstatic) bsdcat_EXTRA_DIST= \ cat/bsdcat.1 \ cat/CMakeLists.txt if BUILD_BSDCAT # Manpages to install bsdcat_man_MANS= cat/bsdcat.1 bsdcat_programs= bsdcat else bsdcat_man_MANS= bsdcat_programs= endif # # bsdcat_test # bsdcat_test_SOURCES= \ $(test_utils_SOURCES) \ cat/test/main.c \ cat/test/test.h \ cat/test/test_0.c \ cat/test/test_empty_gz.c \ cat/test/test_empty_lz4.c \ cat/test/test_empty_xz.c \ cat/test/test_error.c \ cat/test/test_error_mixed.c \ cat/test/test_expand_Z.c \ cat/test/test_expand_bz2.c \ cat/test/test_expand_gz.c \ cat/test/test_expand_lz4.c \ cat/test/test_expand_mixed.c \ cat/test/test_expand_plain.c \ cat/test/test_expand_xz.c \ cat/test/test_help.c \ cat/test/test_version.c bsdcat_test_CPPFLAGS= \ -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \ -I$(top_srcdir)/test_utils \ -I$(top_srcdir)/cat -I$(top_builddir)/cat/test \ $(PLATFORMCPPFLAGS) bsdcat_test_LDADD=libarchive_fe.la cat/test/list.h: Makefile cat $(top_srcdir)/cat/test/test_*.c | grep '^DEFINE_TEST' > cat/test/list.h if BUILD_BSDCAT bsdcat_test_programs= bsdcat_test bsdcat_TESTS_ENVIRONMENT= BSDCAT=`cd $(top_builddir);/bin/pwd`/bsdcat$(EXEEXT) BSDCAT_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/cat/test else bsdcat_test_programs= bsdcat_TESTS_ENVIRONMENT= endif bsdcat_test_EXTRA_DIST= \ cat/test/list.h \ cat/test/test_empty.gz.uu \ cat/test/test_empty.lz4.uu \ cat/test/test_empty.xz.uu \ cat/test/test_expand.Z.uu \ cat/test/test_expand.bz2.uu \ cat/test/test_expand.gz.uu \ cat/test/test_expand.lz4.uu \ cat/test/test_expand.plain.uu \ cat/test/test_expand.xz.uu \ cat/test/CMakeLists.txt Index: vendor/libarchive/dist/cpio/test/test_owner_parse.c =================================================================== --- vendor/libarchive/dist/cpio/test/test_owner_parse.c (revision 310621) +++ vendor/libarchive/dist/cpio/test/test_owner_parse.c (revision 310622) @@ -1,121 +1,121 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #include "../cpio.h" #include "err.h" #if !defined(_WIN32) #define ROOT "root" static const int root_uids[] = { 0 }; static const int root_gids[] = { 0, 1 }; #elif defined(__CYGWIN__) /* On cygwin, the Administrator user most likely exists (unless * it has been renamed or is in a non-English localization), but * its primary group membership depends on how the user set up * their /etc/passwd. Likely values are 513 (None), 545 (Users), * or 544 (Administrators). Just check for one of those... - * TODO: Handle non-English localizations...e.g. French 'Administrateur' + * TODO: Handle non-English localizations... e.g. French 'Administrateur' * Use CreateWellKnownSID() and LookupAccountName()? */ #define ROOT "Administrator" static const int root_uids[] = { 500 }; static const int root_gids[] = { 513, 545, 544 }; #endif #if defined(ROOT) static int int_in_list(int i, const int *l, size_t n) { while (n-- > 0) if (*l++ == i) return (1); failure("%d", i); return (0); } #endif DEFINE_TEST(test_owner_parse) { #if !defined(ROOT) skipping("No uid/gid configuration for this OS"); #else int uid, gid; assert(NULL == owner_parse(ROOT, &uid, &gid)); assert(int_in_list(uid, root_uids, sizeof(root_uids)/sizeof(root_uids[0]))); assertEqualInt(-1, gid); assert(NULL == owner_parse(ROOT ":", &uid, &gid)); assert(int_in_list(uid, root_uids, sizeof(root_uids)/sizeof(root_uids[0]))); assert(int_in_list(gid, root_gids, sizeof(root_gids)/sizeof(root_gids[0]))); assert(NULL == owner_parse(ROOT ".", &uid, &gid)); assert(int_in_list(uid, root_uids, sizeof(root_uids)/sizeof(root_uids[0]))); assert(int_in_list(gid, root_gids, sizeof(root_gids)/sizeof(root_gids[0]))); assert(NULL == owner_parse("111", &uid, &gid)); assertEqualInt(111, uid); assertEqualInt(-1, gid); assert(NULL == owner_parse("112:", &uid, &gid)); assertEqualInt(112, uid); /* Can't assert gid, since we don't know gid for user #112. */ assert(NULL == owner_parse("113.", &uid, &gid)); assertEqualInt(113, uid); /* Can't assert gid, since we don't know gid for user #113. */ assert(NULL == owner_parse(":114", &uid, &gid)); assertEqualInt(-1, uid); assertEqualInt(114, gid); assert(NULL == owner_parse(".115", &uid, &gid)); assertEqualInt(-1, uid); assertEqualInt(115, gid); assert(NULL == owner_parse("116:117", &uid, &gid)); assertEqualInt(116, uid); assertEqualInt(117, gid); /* * TODO: Lookup current user/group name, build strings and * use those to verify username/groupname lookups for ordinary * users. */ assert(NULL != owner_parse(":nonexistentgroup", &uid, &gid)); assert(NULL != owner_parse(ROOT ":nonexistentgroup", &uid, &gid)); assert(NULL != owner_parse("nonexistentuser:nonexistentgroup", &uid, &gid)); #endif } Index: vendor/libarchive/dist/libarchive/archive_read.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read.c (revision 310622) @@ -1,1738 +1,1738 @@ /*- * Copyright (c) 2003-2011 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This file contains the "essential" portions of the read API, that * is, stuff that will probably always be used by any client that * actually needs to read an archive. Optional pieces have been, as * far as possible, separated out into separate files to avoid * needlessly bloating statically-linked clients. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:23Z kientzle $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #define minimum(a, b) (a < b ? a : b) static int choose_filters(struct archive_read *); static int choose_format(struct archive_read *); static int close_filters(struct archive_read *); static struct archive_vtable *archive_read_vtable(void); static int64_t _archive_filter_bytes(struct archive *, int); static int _archive_filter_code(struct archive *, int); static const char *_archive_filter_name(struct archive *, int); static int _archive_filter_count(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_free(struct archive *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static int64_t advance_file_pointer(struct archive_read_filter *, int64_t); static struct archive_vtable * archive_read_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_filter_bytes = _archive_filter_bytes; av.archive_filter_code = _archive_filter_code; av.archive_filter_name = _archive_filter_name; av.archive_filter_count = _archive_filter_count; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; inited = 1; } return (&av); } /* * Allocate, initialize and return a struct archive object. */ struct archive * archive_read_new(void) { struct archive_read *a; a = (struct archive_read *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->entry = archive_entry_new2(&a->archive); a->archive.vtable = archive_read_vtable(); a->passphrases.last = &a->passphrases.first; return (&a->archive); } /* * Record the do-not-extract-to file. This belongs in archive_read_extract.c. */ void archive_read_extract_set_skip_file(struct archive *_a, int64_t d, int64_t i) { struct archive_read *a = (struct archive_read *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file")) return; a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; } /* * Open the archive */ int archive_read_open(struct archive *a, void *client_data, archive_open_callback *client_opener, archive_read_callback *client_reader, archive_close_callback *client_closer) { /* Old archive_read_open() is just a thin shell around * archive_read_open1. */ archive_read_set_open_callback(a, client_opener); archive_read_set_read_callback(a, client_reader); archive_read_set_close_callback(a, client_closer); archive_read_set_callback_data(a, client_data); return archive_read_open1(a); } int archive_read_open2(struct archive *a, void *client_data, archive_open_callback *client_opener, archive_read_callback *client_reader, archive_skip_callback *client_skipper, archive_close_callback *client_closer) { /* Old archive_read_open2() is just a thin shell around * archive_read_open1. */ archive_read_set_callback_data(a, client_data); archive_read_set_open_callback(a, client_opener); archive_read_set_read_callback(a, client_reader); archive_read_set_skip_callback(a, client_skipper); archive_read_set_close_callback(a, client_closer); return archive_read_open1(a); } static ssize_t client_read_proxy(struct archive_read_filter *self, const void **buff) { ssize_t r; r = (self->archive->client.reader)(&self->archive->archive, self->data, buff); return (r); } static int64_t client_skip_proxy(struct archive_read_filter *self, int64_t request) { if (request < 0) __archive_errx(1, "Negative skip requested."); if (request == 0) return 0; if (self->archive->client.skipper != NULL) { /* Seek requests over 1GiB are broken down into * multiple seeks. This avoids overflows when the * requests get passed through 32-bit arguments. */ int64_t skip_limit = (int64_t)1 << 30; int64_t total = 0; for (;;) { int64_t get, ask = request; if (ask > skip_limit) ask = skip_limit; get = (self->archive->client.skipper) (&self->archive->archive, self->data, ask); total += get; if (get == 0 || get == request) return (total); if (get > request) return ARCHIVE_FATAL; request -= get; } } else if (self->archive->client.seeker != NULL && request > 64 * 1024) { /* If the client provided a seeker but not a skipper, * we can use the seeker to skip forward. * * Note: This isn't always a good idea. The client * skipper is allowed to skip by less than requested * if it needs to maintain block alignment. The * seeker is not allowed to play such games, so using * the seeker here may be a performance loss compared * to just reading and discarding. That's why we * only do this for skips of over 64k. */ int64_t before = self->position; int64_t after = (self->archive->client.seeker) (&self->archive->archive, self->data, request, SEEK_CUR); if (after != before + request) return ARCHIVE_FATAL; return after - before; } return 0; } static int64_t client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence) { /* DO NOT use the skipper here! If we transparently handled * forward seek here by using the skipper, that will break * other libarchive code that assumes a successful forward * seek means it can also seek backwards. */ if (self->archive->client.seeker == NULL) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Current client reader does not support seeking a device"); return (ARCHIVE_FAILED); } return (self->archive->client.seeker)(&self->archive->archive, self->data, offset, whence); } static int client_close_proxy(struct archive_read_filter *self) { int r = ARCHIVE_OK, r2; unsigned int i; if (self->archive->client.closer == NULL) return (r); for (i = 0; i < self->archive->client.nodes; i++) { r2 = (self->archive->client.closer) ((struct archive *)self->archive, self->archive->client.dataset[i].data); if (r > r2) r = r2; } return (r); } static int client_open_proxy(struct archive_read_filter *self) { int r = ARCHIVE_OK; if (self->archive->client.opener != NULL) r = (self->archive->client.opener)( (struct archive *)self->archive, self->data); return (r); } static int client_switch_proxy(struct archive_read_filter *self, unsigned int iindex) { int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; void *data2 = NULL; /* Don't do anything if already in the specified data node */ if (self->archive->client.cursor == iindex) return (ARCHIVE_OK); self->archive->client.cursor = iindex; data2 = self->archive->client.dataset[self->archive->client.cursor].data; if (self->archive->client.switcher != NULL) { r1 = r2 = (self->archive->client.switcher) ((struct archive *)self->archive, self->data, data2); self->data = data2; } else { /* Attempt to call close and open instead */ if (self->archive->client.closer != NULL) r1 = (self->archive->client.closer) ((struct archive *)self->archive, self->data); self->data = data2; if (self->archive->client.opener != NULL) r2 = (self->archive->client.opener) ((struct archive *)self->archive, self->data); } return (r1 < r2) ? r1 : r2; } int archive_read_set_open_callback(struct archive *_a, archive_open_callback *client_opener) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_open_callback"); a->client.opener = client_opener; return ARCHIVE_OK; } int archive_read_set_read_callback(struct archive *_a, archive_read_callback *client_reader) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_read_callback"); a->client.reader = client_reader; return ARCHIVE_OK; } int archive_read_set_skip_callback(struct archive *_a, archive_skip_callback *client_skipper) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_skip_callback"); a->client.skipper = client_skipper; return ARCHIVE_OK; } int archive_read_set_seek_callback(struct archive *_a, archive_seek_callback *client_seeker) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_seek_callback"); a->client.seeker = client_seeker; return ARCHIVE_OK; } int archive_read_set_close_callback(struct archive *_a, archive_close_callback *client_closer) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_close_callback"); a->client.closer = client_closer; return ARCHIVE_OK; } int archive_read_set_switch_callback(struct archive *_a, archive_switch_callback *client_switcher) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_switch_callback"); a->client.switcher = client_switcher; return ARCHIVE_OK; } int archive_read_set_callback_data(struct archive *_a, void *client_data) { return archive_read_set_callback_data2(_a, client_data, 0); } int archive_read_set_callback_data2(struct archive *_a, void *client_data, unsigned int iindex) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_callback_data2"); if (a->client.nodes == 0) { a->client.dataset = (struct archive_read_data_node *) calloc(1, sizeof(*a->client.dataset)); if (a->client.dataset == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory."); return ARCHIVE_FATAL; } a->client.nodes = 1; } if (iindex > a->client.nodes - 1) { archive_set_error(&a->archive, EINVAL, "Invalid index specified."); return ARCHIVE_FATAL; } a->client.dataset[iindex].data = client_data; a->client.dataset[iindex].begin_position = -1; a->client.dataset[iindex].total_size = -1; return ARCHIVE_OK; } int archive_read_add_callback_data(struct archive *_a, void *client_data, unsigned int iindex) { struct archive_read *a = (struct archive_read *)_a; void *p; unsigned int i; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_add_callback_data"); if (iindex > a->client.nodes) { archive_set_error(&a->archive, EINVAL, "Invalid index specified."); return ARCHIVE_FATAL; } p = realloc(a->client.dataset, sizeof(*a->client.dataset) * (++(a->client.nodes))); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory."); return ARCHIVE_FATAL; } a->client.dataset = (struct archive_read_data_node *)p; for (i = a->client.nodes - 1; i > iindex && i > 0; i--) { a->client.dataset[i].data = a->client.dataset[i-1].data; a->client.dataset[i].begin_position = -1; a->client.dataset[i].total_size = -1; } a->client.dataset[iindex].data = client_data; a->client.dataset[iindex].begin_position = -1; a->client.dataset[iindex].total_size = -1; return ARCHIVE_OK; } int archive_read_append_callback_data(struct archive *_a, void *client_data) { struct archive_read *a = (struct archive_read *)_a; return archive_read_add_callback_data(_a, client_data, a->client.nodes); } int archive_read_prepend_callback_data(struct archive *_a, void *client_data) { return archive_read_add_callback_data(_a, client_data, 0); } int archive_read_open1(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter, *tmp; int slot, e = ARCHIVE_OK; unsigned int i; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); archive_clear_error(&a->archive); if (a->client.reader == NULL) { archive_set_error(&a->archive, EINVAL, "No reader function provided to archive_read_open"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* Open data source. */ if (a->client.opener != NULL) { e = (a->client.opener)(&a->archive, a->client.dataset[0].data); if (e != 0) { /* If the open failed, call the closer to clean up. */ if (a->client.closer) { for (i = 0; i < a->client.nodes; i++) (a->client.closer)(&a->archive, a->client.dataset[i].data); } return (e); } } filter = calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = NULL; filter->upstream = NULL; filter->archive = a; filter->data = a->client.dataset[0].data; filter->open = client_open_proxy; filter->read = client_read_proxy; filter->skip = client_skip_proxy; filter->seek = client_seek_proxy; filter->close = client_close_proxy; filter->sswitch = client_switch_proxy; filter->name = "none"; filter->code = ARCHIVE_FILTER_NONE; a->client.dataset[0].begin_position = 0; if (!a->filter || !a->bypass_filter_bidding) { a->filter = filter; /* Build out the input pipeline. */ e = choose_filters(a); if (e < ARCHIVE_WARN) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } else { /* Need to add "NONE" type filter at the end of the filter chain */ tmp = a->filter; while (tmp->upstream) tmp = tmp->upstream; tmp->upstream = filter; } if (!a->format) { slot = choose_format(a); if (slot < 0) { close_filters(a); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->format = &(a->formats[slot]); } a->archive.state = ARCHIVE_STATE_HEADER; /* Ensure libarchive starts from the first node in a multivolume set */ client_switch_proxy(a->filter, 0); return (e); } /* * Allow each registered stream transform to bid on whether * it wants to handle this stream. Repeat until we've finished * building the pipeline. */ /* We won't build a filter pipeline with more stages than this. */ #define MAX_NUMBER_FILTERS 25 static int choose_filters(struct archive_read *a) { int number_bidders, i, bid, best_bid, number_filters; struct archive_read_filter_bidder *bidder, *best_bidder; struct archive_read_filter *filter; ssize_t avail; int r; for (number_filters = 0; number_filters < MAX_NUMBER_FILTERS; ++number_filters) { number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); best_bid = 0; best_bidder = NULL; bidder = a->bidders; for (i = 0; i < number_bidders; i++, bidder++) { if (bidder->bid != NULL) { bid = (bidder->bid)(bidder, a->filter); if (bid > best_bid) { best_bid = bid; best_bidder = bidder; } } } /* If no bidder, we're done. */ if (best_bidder == NULL) { /* Verify the filter by asking it for some data. */ __archive_read_filter_ahead(a->filter, 1, &avail); if (avail < 0) { __archive_read_free_filters(a); return (ARCHIVE_FATAL); } a->archive.compression_name = a->filter->name; a->archive.compression_code = a->filter->code; return (ARCHIVE_OK); } filter = (struct archive_read_filter *)calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = best_bidder; filter->archive = a; filter->upstream = a->filter; a->filter = filter; r = (best_bidder->init)(a->filter); if (r != ARCHIVE_OK) { __archive_read_free_filters(a); return (ARCHIVE_FATAL); } } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Input requires too many filters for decoding"); return (ARCHIVE_FATAL); } /* * Read header of next entry. */ static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read *a = (struct archive_read *)_a; int r1 = ARCHIVE_OK, r2; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header"); archive_entry_clear(entry); archive_clear_error(&a->archive); /* * If client didn't consume entire data, skip any remainder * (This is especially important for GNU incremental directories.) */ if (a->archive.state == ARCHIVE_STATE_DATA) { r1 = archive_read_data_skip(&a->archive); if (r1 == ARCHIVE_EOF) archive_set_error(&a->archive, EIO, "Premature end-of-file."); if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } /* Record start-of-header offset in uncompressed stream. */ a->header_position = a->filter->position; ++_a->file_count; r2 = (a->format->read_header)(a, entry); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r2) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; --_a->file_count;/* Revert a file counter. */ break; case ARCHIVE_OK: a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_WARN: a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); a->data_start_node = a->client.cursor; /* EOF always wins; otherwise return the worst error. */ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1; } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read *a = (struct archive_read *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } /* * Allow each registered format to bid on whether it wants to handle * the next entry. Return index of winning bidder. */ static int choose_format(struct archive_read *a) { int slots; int i; int bid, best_bid; int best_bid_slot; slots = sizeof(a->formats) / sizeof(a->formats[0]); best_bid = -1; best_bid_slot = -1; /* Set up a->format for convenience of bidders. */ a->format = &(a->formats[0]); for (i = 0; i < slots; i++, a->format++) { if (a->format->bid) { bid = (a->format->bid)(a, best_bid); if (bid == ARCHIVE_FATAL) return (ARCHIVE_FATAL); if (a->filter->position != 0) __archive_read_seek(a, 0, SEEK_SET); if ((bid > best_bid) || (best_bid_slot < 0)) { best_bid = bid; best_bid_slot = i; } } } /* * There were no bidders; this is a serious programmer error * and demands a quick and definitive abort. */ if (best_bid_slot < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No formats registered"); return (ARCHIVE_FATAL); } /* * There were bidders, but no non-zero bids; this means we * can't support this stream. */ if (best_bid < 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized archive format"); return (ARCHIVE_FATAL); } return (best_bid_slot); } /* * Return the file offset (within the uncompressed data stream) where * the last header started. */ int64_t archive_read_header_position(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_header_position"); return (a->header_position); } /* * Returns 1 if the archive contains at least one encrypted entry. * If the archive format not support encryption at all * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. * If for any other reason (e.g. not enough data read so far) * we cannot say whether there are encrypted entries, then * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned. * In general, this function will return values below zero when the - * reader is uncertain or totally uncapable of encryption support. + * reader is uncertain or totally incapable of encryption support. * When this function returns 0 you can be sure that the reader * supports encryption detection but no encrypted entries have * been found yet. * * NOTE: If the metadata/header of an archive is also encrypted, you * cannot rely on the number of encrypted entries. That is why this * function does not return the number of encrypted entries but# * just shows that there are some. */ int archive_read_has_encrypted_entries(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int format_supports_encryption = archive_read_format_capabilities(_a) & (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); if (!_a || !format_supports_encryption) { /* Format in general doesn't support encryption */ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; } /* A reader potentially has read enough data now. */ if (a->format && a->format->has_encrypted_entries) { return (a->format->has_encrypted_entries)(a); } /* For any other reason we cannot say how many entries are there. */ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } /* * Returns a bitmask of capabilities that are supported by the archive format reader. * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned. */ int archive_read_format_capabilities(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; if (a && a->format && a->format->format_capabilties) { return (a->format->format_capabilties)(a); } return ARCHIVE_READ_FORMAT_CAPS_NONE; } /* * Read data from an archive entry, using a read(2)-style interface. * This is a convenience routine that just calls * archive_read_data_block and copies the results into the client * buffer, filling any gaps with zero bytes. Clients using this * API can be completely ignorant of sparse-file issues; sparse files * will simply be padded with nulls. * * DO NOT intermingle calls to this function and archive_read_data_block * to read a single entry body. */ ssize_t archive_read_data(struct archive *_a, void *buff, size_t s) { struct archive *a = (struct archive *)_a; char *dest; const void *read_buf; size_t bytes_read; size_t len; int r; bytes_read = 0; dest = (char *)buff; while (s > 0) { if (a->read_data_remaining == 0) { read_buf = a->read_data_block; a->read_data_is_posix_read = 1; a->read_data_requested = s; r = archive_read_data_block(a, &read_buf, &a->read_data_remaining, &a->read_data_offset); a->read_data_block = read_buf; if (r == ARCHIVE_EOF) return (bytes_read); /* * Error codes are all negative, so the status * return here cannot be confused with a valid * byte count. (ARCHIVE_OK is zero.) */ if (r < ARCHIVE_OK) return (r); } if (a->read_data_offset < a->read_data_output_offset) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Encountered out-of-order sparse blocks"); return (ARCHIVE_RETRY); } /* Compute the amount of zero padding needed. */ if (a->read_data_output_offset + (int64_t)s < a->read_data_offset) { len = s; } else if (a->read_data_output_offset < a->read_data_offset) { len = (size_t)(a->read_data_offset - a->read_data_output_offset); } else len = 0; /* Add zeroes. */ memset(dest, 0, len); s -= len; a->read_data_output_offset += len; dest += len; bytes_read += len; /* Copy data if there is any space left. */ if (s > 0) { len = a->read_data_remaining; if (len > s) len = s; memcpy(dest, a->read_data_block, len); s -= len; a->read_data_block += len; a->read_data_remaining -= len; a->read_data_output_offset += len; a->read_data_offset += len; dest += len; bytes_read += len; } } a->read_data_is_posix_read = 0; a->read_data_requested = 0; return (bytes_read); } /* * Reset the read_data_* variables, used for starting a new entry. */ void __archive_reset_read_data(struct archive * a) { a->read_data_output_offset = 0; a->read_data_remaining = 0; a->read_data_is_posix_read = 0; a->read_data_requested = 0; /* extra resets, from rar.c */ a->read_data_block = NULL; a->read_data_offset = 0; } /* * Skip over all remaining data in this entry. */ int archive_read_data_skip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r; const void *buff; size_t size; int64_t offset; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_skip"); if (a->format->read_data_skip != NULL) r = (a->format->read_data_skip)(a); else { while ((r = archive_read_data_block(&a->archive, &buff, &size, &offset)) == ARCHIVE_OK) ; } if (r == ARCHIVE_EOF) r = ARCHIVE_OK; a->archive.state = ARCHIVE_STATE_HEADER; return (r); } int64_t archive_seek_data(struct archive *_a, int64_t offset, int whence) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_seek_data_block"); if (a->format->seek_data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " "No format_seek_data_block function registered"); return (ARCHIVE_FATAL); } return (a->format->seek_data)(a, offset, whence); } /* * Read the next block of entry data from the archive. * This is a zero-copy interface; the client receives a pointer, * size, and file offset of the next available block of data. * * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if * the end of entry is encountered. */ static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (a->format->read_data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " "No format->read_data function registered"); return (ARCHIVE_FATAL); } return (a->format->read_data)(a, buff, size, offset); } static int close_filters(struct archive_read *a) { struct archive_read_filter *f = a->filter; int r = ARCHIVE_OK; /* Close each filter in the pipeline. */ while (f != NULL) { struct archive_read_filter *t = f->upstream; if (!f->closed && f->close != NULL) { int r1 = (f->close)(f); f->closed = 1; if (r1 < r) r = r1; } free(f->buffer); f->buffer = NULL; f = t; } return r; } void __archive_read_free_filters(struct archive_read *a) { /* Make sure filters are closed and their buffers are freed */ close_filters(a); while (a->filter != NULL) { struct archive_read_filter *t = a->filter->upstream; free(a->filter); a->filter = t; } } /* * return the count of # of filters in use */ static int _archive_filter_count(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *p = a->filter; int count = 0; while(p) { count++; p = p->upstream; } return count; } /* * Close the file and all I/O. */ static int _archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state == ARCHIVE_STATE_CLOSED) return (ARCHIVE_OK); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; /* TODO: Clean up the formatters. */ /* Release the filter objects. */ r1 = close_filters(a); if (r1 < r) r = r1; return (r); } /* * Release memory and other resources. */ static int _archive_read_free(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_passphrase *p; int i, n; int slots; int r = ARCHIVE_OK; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED && a->archive.state != ARCHIVE_STATE_FATAL) r = archive_read_close(&a->archive); /* Call cleanup functions registered by optional components. */ if (a->cleanup_archive_extract != NULL) r = (a->cleanup_archive_extract)(a); /* Cleanup format-specific data. */ slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < slots; i++) { a->format = &(a->formats[i]); if (a->formats[i].cleanup) (a->formats[i].cleanup)(a); } /* Free the filters */ __archive_read_free_filters(a); /* Release the bidder objects. */ n = sizeof(a->bidders)/sizeof(a->bidders[0]); for (i = 0; i < n; i++) { if (a->bidders[i].free != NULL) { int r1 = (a->bidders[i].free)(&a->bidders[i]); if (r1 < r) r = r1; } } /* Release passphrase list. */ p = a->passphrases.first; while (p != NULL) { struct archive_read_passphrase *np = p->next; /* A passphrase should be cleaned. */ memset(p->passphrase, 0, strlen(p->passphrase)); free(p->passphrase); free(p); p = np; } archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a->client.dataset); free(a); return (r); } static struct archive_read_filter * get_filter(struct archive *_a, int n) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *f = a->filter; /* We use n == -1 for 'the last filter', which is always the * client proxy. */ if (n == -1 && f != NULL) { struct archive_read_filter *last = f; f = f->upstream; while (f != NULL) { last = f; f = f->upstream; } return (last); } if (n < 0) return NULL; while (n > 0 && f != NULL) { f = f->upstream; --n; } return (f); } static int _archive_filter_code(struct archive *_a, int n) { struct archive_read_filter *f = get_filter(_a, n); return f == NULL ? -1 : f->code; } static const char * _archive_filter_name(struct archive *_a, int n) { struct archive_read_filter *f = get_filter(_a, n); return f != NULL ? f->name : NULL; } static int64_t _archive_filter_bytes(struct archive *_a, int n) { struct archive_read_filter *f = get_filter(_a, n); return f == NULL ? -1 : f->position; } /* * Used internally by read format handlers to register their bid and * initialization functions. */ int __archive_read_register_format(struct archive_read *a, void *format_data, const char *name, int (*bid)(struct archive_read *, int), int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), int (*read_data_skip)(struct archive_read *), int64_t (*seek_data)(struct archive_read *, int64_t, int), int (*cleanup)(struct archive_read *), int (*format_capabilities)(struct archive_read *), int (*has_encrypted_entries)(struct archive_read *)) { int i, number_slots; archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_format"); number_slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < number_slots; i++) { if (a->formats[i].bid == bid) return (ARCHIVE_WARN); /* We've already installed */ if (a->formats[i].bid == NULL) { a->formats[i].bid = bid; a->formats[i].options = options; a->formats[i].read_header = read_header; a->formats[i].read_data = read_data; a->formats[i].read_data_skip = read_data_skip; a->formats[i].seek_data = seek_data; a->formats[i].cleanup = cleanup; a->formats[i].data = format_data; a->formats[i].name = name; a->formats[i].format_capabilties = format_capabilities; a->formats[i].has_encrypted_entries = has_encrypted_entries; return (ARCHIVE_OK); } } archive_set_error(&a->archive, ENOMEM, "Not enough slots for format registration"); return (ARCHIVE_FATAL); } /* * Used internally by decompression routines to register their bid and * initialization functions. */ int __archive_read_get_bidder(struct archive_read *a, struct archive_read_filter_bidder **bidder) { int i, number_slots; number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]); for (i = 0; i < number_slots; i++) { if (a->bidders[i].bid == NULL) { memset(a->bidders + i, 0, sizeof(a->bidders[0])); *bidder = (a->bidders + i); return (ARCHIVE_OK); } } archive_set_error(&a->archive, ENOMEM, "Not enough slots for filter registration"); return (ARCHIVE_FATAL); } /* * The next section implements the peek/consume internal I/O * system used by archive readers. This system allows simple * read-ahead for consumers while preserving zero-copy operation * most of the time. * * The two key operations: * * The read-ahead function returns a pointer to a block of data * that satisfies a minimum request. * * The consume function advances the file pointer. * * In the ideal case, filters generate blocks of data * and __archive_read_ahead() just returns pointers directly into * those blocks. Then __archive_read_consume() just bumps those * pointers. Only if your request would span blocks does the I/O * layer use a copy buffer to provide you with a contiguous block of * data. * * A couple of useful idioms: * * "I just want some data." Ask for 1 byte and pay attention to * the "number of bytes available" from __archive_read_ahead(). * Consume whatever you actually use. * * "I want to output a large block of data." As above, ask for 1 byte, * emit all that's available (up to whatever limit you have), consume * it all, then repeat until you're done. This effectively means that * you're passing along the blocks that came from your provider. * * "I want to peek ahead by a large amount." Ask for 4k or so, then * double and repeat until you get an error or have enough. Note * that the I/O layer will likely end up expanding its copy buffer * to fit your request, so use this technique cautiously. This * technique is used, for example, by some of the format tasting * code that has uncertain look-ahead needs. */ /* * Looks ahead in the input stream: * * If 'avail' pointer is provided, that returns number of bytes available * in the current buffer, which may be much larger than requested. * * If end-of-file, *avail gets set to zero. * * If error, *avail gets error code. * * If request can be met, returns pointer to data. * * If minimum request cannot be met, returns NULL. * * Note: If you just want "some data", ask for 1 byte and pay attention * to *avail, which will have the actual amount available. If you * know exactly how many bytes you need, just ask for that and treat * a NULL return as an error. * * Important: This does NOT move the file pointer. See * __archive_read_consume() below. */ const void * __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) { return (__archive_read_filter_ahead(a->filter, min, avail)); } const void * __archive_read_filter_ahead(struct archive_read_filter *filter, size_t min, ssize_t *avail) { ssize_t bytes_read; size_t tocopy; if (filter->fatal) { if (avail) *avail = ARCHIVE_FATAL; return (NULL); } /* * Keep pulling more data until we can satisfy the request. */ for (;;) { /* * If we can satisfy from the copy buffer (and the * copy buffer isn't empty), we're done. In particular, * note that min == 0 is a perfectly well-defined * request. */ if (filter->avail >= min && filter->avail > 0) { if (avail != NULL) *avail = filter->avail; return (filter->next); } /* * We can satisfy directly from client buffer if everything * currently in the copy buffer is still in the client buffer. */ if (filter->client_total >= filter->client_avail + filter->avail && filter->client_avail + filter->avail >= min) { /* "Roll back" to client buffer. */ filter->client_avail += filter->avail; filter->client_next -= filter->avail; /* Copy buffer is now empty. */ filter->avail = 0; filter->next = filter->buffer; /* Return data from client buffer. */ if (avail != NULL) *avail = filter->client_avail; return (filter->client_next); } /* Move data forward in copy buffer if necessary. */ if (filter->next > filter->buffer && filter->next + min > filter->buffer + filter->buffer_size) { if (filter->avail > 0) memmove(filter->buffer, filter->next, filter->avail); filter->next = filter->buffer; } /* If we've used up the client data, get more. */ if (filter->client_avail <= 0) { if (filter->end_of_file) { if (avail != NULL) *avail = 0; return (NULL); } bytes_read = (filter->read)(filter, &filter->client_buff); if (bytes_read < 0) { /* Read error. */ filter->client_total = filter->client_avail = 0; filter->client_next = filter->client_buff = NULL; filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_read == 0) { /* Check for another client object first */ if (filter->archive->client.cursor != filter->archive->client.nodes - 1) { if (client_switch_proxy(filter, filter->archive->client.cursor + 1) == ARCHIVE_OK) continue; } /* Premature end-of-file. */ filter->client_total = filter->client_avail = 0; filter->client_next = filter->client_buff = NULL; filter->end_of_file = 1; /* Return whatever we do have. */ if (avail != NULL) *avail = filter->avail; return (NULL); } filter->client_total = bytes_read; filter->client_avail = filter->client_total; filter->client_next = filter->client_buff; } else { /* * We can't satisfy the request from the copy * buffer or the existing client data, so we * need to copy more client data over to the * copy buffer. */ /* Ensure the buffer is big enough. */ if (min > filter->buffer_size) { size_t s, t; char *p; /* Double the buffer; watch for overflow. */ s = t = filter->buffer_size; if (s == 0) s = min; while (s < min) { t *= 2; if (t <= s) { /* Integer overflow! */ archive_set_error( &filter->archive->archive, ENOMEM, "Unable to allocate copy" " buffer"); filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } s = t; } /* Now s >= min, so allocate a new buffer. */ p = (char *)malloc(s); if (p == NULL) { archive_set_error( &filter->archive->archive, ENOMEM, "Unable to allocate copy buffer"); filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } /* Move data into newly-enlarged buffer. */ if (filter->avail > 0) memmove(p, filter->next, filter->avail); free(filter->buffer); filter->next = filter->buffer = p; filter->buffer_size = s; } /* We can add client data to copy buffer. */ /* First estimate: copy to fill rest of buffer. */ tocopy = (filter->buffer + filter->buffer_size) - (filter->next + filter->avail); /* Don't waste time buffering more than we need to. */ if (tocopy + filter->avail > min) tocopy = min - filter->avail; /* Don't copy more than is available. */ if (tocopy > filter->client_avail) tocopy = filter->client_avail; memcpy(filter->next + filter->avail, filter->client_next, tocopy); /* Remove this data from client buffer. */ filter->client_next += tocopy; filter->client_avail -= tocopy; /* add it to copy buffer. */ filter->avail += tocopy; } } } /* * Move the file pointer forward. */ int64_t __archive_read_consume(struct archive_read *a, int64_t request) { return (__archive_read_filter_consume(a->filter, request)); } int64_t __archive_read_filter_consume(struct archive_read_filter * filter, int64_t request) { int64_t skipped; if (request < 0) return ARCHIVE_FATAL; if (request == 0) return 0; skipped = advance_file_pointer(filter, request); if (skipped == request) return (skipped); /* We hit EOF before we satisfied the skip request. */ if (skipped < 0) /* Map error code to 0 for error message below. */ skipped = 0; archive_set_error(&filter->archive->archive, ARCHIVE_ERRNO_MISC, "Truncated input file (needed %jd bytes, only %jd available)", (intmax_t)request, (intmax_t)skipped); return (ARCHIVE_FATAL); } /* * Advance the file pointer by the amount requested. * Returns the amount actually advanced, which may be less than the * request if EOF is encountered first. * Returns a negative value if there's an I/O error. */ static int64_t advance_file_pointer(struct archive_read_filter *filter, int64_t request) { int64_t bytes_skipped, total_bytes_skipped = 0; ssize_t bytes_read; size_t min; if (filter->fatal) return (-1); /* Use up the copy buffer first. */ if (filter->avail > 0) { min = (size_t)minimum(request, (int64_t)filter->avail); filter->next += min; filter->avail -= min; request -= min; filter->position += min; total_bytes_skipped += min; } /* Then use up the client buffer. */ if (filter->client_avail > 0) { min = (size_t)minimum(request, (int64_t)filter->client_avail); filter->client_next += min; filter->client_avail -= min; request -= min; filter->position += min; total_bytes_skipped += min; } if (request == 0) return (total_bytes_skipped); /* If there's an optimized skip function, use it. */ if (filter->skip != NULL) { bytes_skipped = (filter->skip)(filter, request); if (bytes_skipped < 0) { /* error */ filter->fatal = 1; return (bytes_skipped); } filter->position += bytes_skipped; total_bytes_skipped += bytes_skipped; request -= bytes_skipped; if (request == 0) return (total_bytes_skipped); } /* Use ordinary reads as necessary to complete the request. */ for (;;) { bytes_read = (filter->read)(filter, &filter->client_buff); if (bytes_read < 0) { filter->client_buff = NULL; filter->fatal = 1; return (bytes_read); } if (bytes_read == 0) { if (filter->archive->client.cursor != filter->archive->client.nodes - 1) { if (client_switch_proxy(filter, filter->archive->client.cursor + 1) == ARCHIVE_OK) continue; } filter->client_buff = NULL; filter->end_of_file = 1; return (total_bytes_skipped); } if (bytes_read >= request) { filter->client_next = ((const char *)filter->client_buff) + request; filter->client_avail = (size_t)(bytes_read - request); filter->client_total = bytes_read; total_bytes_skipped += request; filter->position += request; return (total_bytes_skipped); } filter->position += bytes_read; total_bytes_skipped += bytes_read; request -= bytes_read; } } /** * Returns ARCHIVE_FAILED if seeking isn't supported. */ int64_t __archive_read_seek(struct archive_read *a, int64_t offset, int whence) { return __archive_read_filter_seek(a->filter, offset, whence); } int64_t __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, int whence) { struct archive_read_client *client; int64_t r; unsigned int cursor; if (filter->closed || filter->fatal) return (ARCHIVE_FATAL); if (filter->seek == NULL) return (ARCHIVE_FAILED); client = &(filter->archive->client); switch (whence) { case SEEK_CUR: /* Adjust the offset and use SEEK_SET instead */ offset += filter->position; case SEEK_SET: cursor = 0; while (1) { if (client->dataset[cursor].begin_position < 0 || client->dataset[cursor].total_size < 0 || client->dataset[cursor].begin_position + client->dataset[cursor].total_size - 1 > offset || cursor + 1 >= client->nodes) break; r = client->dataset[cursor].begin_position + client->dataset[cursor].total_size; client->dataset[++cursor].begin_position = r; } while (1) { r = client_switch_proxy(filter, cursor); if (r != ARCHIVE_OK) return r; if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0) return r; client->dataset[cursor].total_size = r; if (client->dataset[cursor].begin_position + client->dataset[cursor].total_size - 1 > offset || cursor + 1 >= client->nodes) break; r = client->dataset[cursor].begin_position + client->dataset[cursor].total_size; client->dataset[++cursor].begin_position = r; } offset -= client->dataset[cursor].begin_position; if (offset < 0 || offset > client->dataset[cursor].total_size) return ARCHIVE_FATAL; if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0) return r; break; case SEEK_END: cursor = 0; while (1) { if (client->dataset[cursor].begin_position < 0 || client->dataset[cursor].total_size < 0 || cursor + 1 >= client->nodes) break; r = client->dataset[cursor].begin_position + client->dataset[cursor].total_size; client->dataset[++cursor].begin_position = r; } while (1) { r = client_switch_proxy(filter, cursor); if (r != ARCHIVE_OK) return r; if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0) return r; client->dataset[cursor].total_size = r; r = client->dataset[cursor].begin_position + client->dataset[cursor].total_size; if (cursor + 1 >= client->nodes) break; client->dataset[++cursor].begin_position = r; } while (1) { if (r + offset >= client->dataset[cursor].begin_position) break; offset += client->dataset[cursor].total_size; if (cursor == 0) break; cursor--; r = client->dataset[cursor].begin_position + client->dataset[cursor].total_size; } offset = (r + offset) - client->dataset[cursor].begin_position; if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK) return r; r = client_seek_proxy(filter, offset, SEEK_SET); if (r < ARCHIVE_OK) return r; break; default: return (ARCHIVE_FATAL); } r += client->dataset[cursor].begin_position; if (r >= 0) { /* * Ouch. Clearing the buffer like this hurts, especially * at bid time. A lot of our efficiency at bid time comes * from having bidders reuse the data we've already read. * * TODO: If the seek request is in data we already * have, then don't call the seek callback. * * TODO: Zip seeks to end-of-file at bid time. If * other formats also start doing this, we may need to * find a way for clients to fudge the seek offset to * a block boundary. * * Hmmm... If whence was SEEK_END, we know the file * size is (r - offset). Can we use that to simplify * the TODO items above? */ filter->avail = filter->client_avail = 0; filter->next = filter->buffer; filter->position = r; filter->end_of_file = 0; } return r; } Index: vendor/libarchive/dist/libarchive/archive_read_disk_posix.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_disk_posix.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read_disk_posix.c (revision 310622) @@ -1,2671 +1,2671 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LINUX_MAGIC_H #include #endif #ifdef HAVE_LINUX_FS_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_DIRECT_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef HAVE_FCHDIR #error fchdir function required. #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ /* * TODO: * 1) Loop checking. * 3) Arbitrary logical traversals by closing/reopening intermediate fds. */ struct restore_time { const char *name; time_t mtime; long mtime_nsec; time_t atime; long atime_nsec; mode_t filetype; int noatime; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; struct archive_string name; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to return back to the parent of a symlink. */ int symlink_parent_fd; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; int noatime; #if defined(USE_READDIR_R) size_t name_max; #endif long incr_xfer_size; long max_xfer_size; long min_xfer_size; long xfer_align; /* * Buffer used for reading file contents. */ /* Exactly allocated memory pointer. */ unsigned char *allocation_ptr; /* Pointer adjusted to the filesystem alignment . */ unsigned char *buff; size_t buff_size; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; DIR *d; #define INVALID_DIR_HANDLE NULL struct dirent *de; #if defined(USE_READDIR_R) struct dirent *dirent; size_t dirent_allocated; #endif int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* Dynamically-sized buffer for holding path */ struct archive_string path; /* Last path element */ const char *basename; /* Leading dir length */ size_t dirname_length; int depth; int openCount; int maxOpenCount; int initial_dir_fd; int working_dir_fd; struct stat lst; struct stat st; int descend; int nlink; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesystem; int entry_fd; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; unsigned char *entry_buff; size_t entry_buff_size; }; /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define onWorkingDir 64 /* We are on the working dir where we are * reading directory entry at this time. */ #define needsRestoreTimes 128 #define onInitialDir 256 /* We are on the initial dir. */ static int tree_dir_next_posix(struct tree *t); #ifdef HAVE_DIRENT_D_NAMLEN /* BSD extension; avoids need for a strlen() call. */ #define D_NAMELEN(dp) (dp)->d_namlen #else #define D_NAMELEN(dp) (strlen((dp)->d_name)) #endif /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const char *, int, int); static struct tree *tree_reopen(struct tree *, const char *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const char *, int, int64_t, int64_t, struct restore_time *); static int tree_enter_initial_dir(struct tree *); static int tree_enter_working_dir(struct tree *); static int tree_current_dir_fd(struct tree *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * * TODO: On platforms that support it, use openat()-style operations * to eliminate the chdir() operations entirely while still supporting * arbitrarily deep traversals. This makes access_path troublesome to * support, of course, which means we'll need a rich enough interface * that clients can function without it. (In particular, we'll need * tree_current_open() that returns an open file descriptor.) * */ static const char *tree_current_path(struct tree *); static const char *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const struct stat *tree_current_stat(struct tree *); static const struct stat *tree_current_lstat(struct tree *); static int tree_current_is_symblic_link_target(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const struct stat *); static int _archive_read_disk_open(struct archive *, const char *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); static int tree_dup(int); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->enable_copyfile = 1; a->traverse_mount_points = 1; a->open_on_current_dir = open_on_current_dir; a->tree_current_dir_fd = tree_current_dir_fd; a->tree_enter_working_dir = tree_enter_working_dir; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { #ifndef HAVE_UTIMES static int warning_done = 0; #endif struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); #ifdef HAVE_UTIMES a->restore_time = 1; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); #else if (warning_done) /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore access time on this system"); warning_done = 1; return (ARCHIVE_WARN); #endif } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { a->restore_time = 0; if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } if (flags & ARCHIVE_READDISK_HONOR_NODUMP) a->honor_nodump = 1; else a->honor_nodump = 0; if (flags & ARCHIVE_READDISK_MAC_COPYFILE) a->enable_copyfile = 1; else a->enable_copyfile = 0; if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) a->traverse_mount_points = 0; else a->traverse_mount_points = 1; if (flags & ARCHIVE_READDISK_NO_XATTR) a->suppress_xattr = 1; else a->suppress_xattr = 0; return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } /* * Allocate memory for the reading buffer adjusted to the filesystem * alignment. */ static int setup_suitable_read_buffer(struct archive_read_disk *a) { struct tree *t = a->tree; struct filesystem *cf = t->current_filesystem; size_t asize; size_t s; if (cf->allocation_ptr == NULL) { /* If we couldn't get a filesystem alignment, * we use 4096 as default value but we won't use * O_DIRECT to open() and openat() operations. */ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align; if (cf->max_xfer_size != -1) asize = cf->max_xfer_size + xfer_align; else { long incr = cf->incr_xfer_size; /* Some platform does not set a proper value to * incr_xfer_size.*/ if (incr < 0) incr = cf->min_xfer_size; if (cf->min_xfer_size < 0) { incr = xfer_align; asize = xfer_align; } else asize = cf->min_xfer_size; /* Increase a buffer size up to 64K bytes in * a proper incremant size. */ while (asize < 1024*64) asize += incr; /* Take a margin to adjust to the filesystem * alignment. */ asize += xfer_align; } cf->allocation_ptr = malloc(asize); if (cf->allocation_ptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* * Calculate proper address for the filesystem. */ s = (uintptr_t)cf->allocation_ptr; s %= xfer_align; if (s > 0) s = xfer_align - s; /* * Set a read buffer pointer in the proper alignment of * the current filesystem. */ cf->buff = cf->allocation_ptr + s; cf->buff_size = asize - xfer_align; } return (ARCHIVE_OK); } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; int r; ssize_t bytes; size_t buffbytes; int empty_sparse_region = 0; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Open the current file. */ if (t->entry_fd < 0) { int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. * * Carefully consider this to be enabled. */ #if defined(O_DIRECT) && 0/* Disabled for now */ if (t->current_filesystem->xfer_align != -1 && t->nlink == 1) flags |= O_DIRECT; #endif #if defined(O_NOATIME) /* * Linux has O_NOATIME flag; use it if we need. */ if ((t->flags & needsRestoreTimes) != 0 && t->restore_time.noatime == 0) flags |= O_NOATIME; do { #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, * if successful, set 1 to t->restore_time.noatime * not to restore an atime of the file later. * if failed by EPERM, retry it without O_NOATIME flag. */ if (flags & O_NOATIME) { if (t->entry_fd >= 0) t->restore_time.noatime = 1; else if (errno == EPERM) { flags &= ~O_NOATIME; continue; } } } while (0); #endif if (t->entry_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't open %s", tree_current_path(t)); r = ARCHIVE_FAILED; tree_enter_initial_dir(t); goto abort_read_data; } tree_enter_initial_dir(t); } /* * Allocate read buffer if not allocated. */ if (t->current_filesystem->allocation_ptr == NULL) { r = setup_suitable_read_buffer(a); if (r != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } t->entry_buff = t->current_filesystem->buff; t->entry_buff_size = t->current_filesystem->buff_size; buffbytes = t->entry_buff_size; if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; if (t->current_sparse->length == 0) empty_sparse_region = 1; /* * Skip hole. * TODO: Should we consider t->current_filesystem->xfer_align? */ if (t->current_sparse->offset > t->entry_total) { if (lseek(t->entry_fd, (off_t)t->current_sparse->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } bytes = t->current_sparse->offset - t->entry_total; t->entry_remaining_bytes -= bytes; t->entry_total += bytes; } /* * Read file contents. */ if (buffbytes > 0) { bytes = read(t->entry_fd, t->entry_buff, buffbytes); if (bytes < 0) { archive_set_error(&a->archive, errno, "Read error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } else bytes = 0; /* * Return an EOF unless we've read a leading empty sparse region, which * is used to represent fully-sparse files. */ if (bytes == 0 && !empty_sparse_region) { /* Get EOF */ t->entry_eof = 1; r = ARCHIVE_EOF; goto abort_read_data; } *buff = t->entry_buff; *size = bytes; *offset = t->entry_total; t->entry_total += bytes; t->entry_remaining_bytes -= bytes; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; t->entry_eof = 1; } t->current_sparse->offset += bytes; t->current_sparse->length -= bytes; if (t->current_sparse->length == 0 && !t->entry_eof) t->current_sparse++; return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fd >= 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; int descend, r; st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%s: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Couldn't visit directory", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); case 0: tree_enter_initial_dir(t); return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); } break; } } while (lst == NULL); #ifdef __APPLE__ if (a->enable_copyfile) { /* If we're using copyfile(), ignore "._XXX" files. */ const char *bname = strrchr(tree_current_path(t), '/'); if (bname == NULL) bname = tree_current_path(t); else ++bname; if (bname[0] == '.' && bname[1] == '_') return (ARCHIVE_RETRY); } #endif archive_entry_copy_pathname(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; if (!a->traverse_mount_points) { if (t->initial_filesystem_id != t->current_filesystem_id) descend = 0; } t->descend = descend; /* * Honor nodump flag. * If the file is marked with nodump flag, do not return this entry. */ if (a->honor_nodump) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) if (st->st_flags & UF_NODUMP) return (ARCHIVE_RETRY); #elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { int stflags; t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) return (ARCHIVE_RETRY); } } #endif } archive_entry_copy_stat(entry, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, - * especially, invokng a callback. */ + * especially, invoking a callback. */ t->restore_time.mtime = archive_entry_mtime(entry); t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry); t->restore_time.atime = archive_entry_atime(entry); t->restore_time.atime_nsec = archive_entry_atime_nsec(entry); t->restore_time.filetype = archive_entry_filetype(entry); t->restore_time.noatime = t->current_filesystem->noatime; /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } /* * Populate the archive_entry with metadata from the disk. */ archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } for (;;) { r = next_entry(a, t, entry); if (t->entry_fd >= 0) { close(t->entry_fd); t->entry_fd = -1; } if (r == ARCHIVE_RETRY) { archive_entry_clear(entry); continue; } break; } /* Return to the initial directory. */ tree_enter_initial_dir(t); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: /* Overwrite the sourcepath based on the initial directory. */ archive_entry_copy_sourcepath(entry, tree_current_path(t)); t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->nlink = archive_entry_nlink(entry); t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); t->sparse_list[i].offset = offset; t->sparse_list[i].length = length; } if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = archive_entry_size(entry); } else { t->sparse_list[i].offset = archive_entry_size(entry); t->sparse_list[i].length = 0; } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); t->stack->flags |= isDir; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); return (_archive_read_disk_open(_a, pathname)); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_string path; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); /* Make a char string from a wchar_t string. */ archive_string_init(&path); if (archive_string_append_from_wcs(&path, pathname, wcslen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a char string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open(_a, path.s); archive_string_free(&path); return (ret); } static int _archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) a->tree = tree_reopen(a->tree, pathname, a->restore_time); else a->tree = tree_open(pathname, a->symlink_mode, a->restore_time); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * This is the new filesystem which we have to generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; t->current_filesystem->allocation_ptr = NULL; t->current_filesystem->buff = NULL; /* Setup the current filesystem properties which depend on * platform specific. */ return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } #if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN) static int get_xfer_size(struct tree *t, int fd, const char *path) { t->current_filesystem->xfer_align = -1; errno = 0; if (fd >= 0) { t->current_filesystem->incr_xfer_size = fpathconf(fd, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = fpathconf(fd, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = fpathconf(fd, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = fpathconf(fd, _PC_REC_XFER_ALIGN); } else if (path != NULL) { t->current_filesystem->incr_xfer_size = pathconf(path, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = pathconf(path, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = pathconf(path, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = pathconf(path, _PC_REC_XFER_ALIGN); } /* At least we need an alignment size. */ if (t->current_filesystem->xfer_align == -1) return ((errno == EINVAL)?1:-1); else return (0); } #else static int get_xfer_size(struct tree *t, int fd, const char *path) { (void)t; /* UNUSED */ (void)fd; /* UNUSED */ (void)path; /* UNUSED */ return (1);/* Not supported */ } #endif #if defined(HAVE_STATFS) && defined(HAVE_FSTATFS) && defined(MNT_LOCAL) \ && !defined(ST_LOCAL) /* * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) /* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make * this accurate; some platforms have both and we need the one that's * used by getvfsbyname() * * Then the following would become: * #if defined(GETVFSBYNAME_ARG_TYPE) * GETVFSBYNAME_ARG_TYPE vfc; * #endif */ # if defined(HAVE_STRUCT_XVFSCONF) struct xvfsconf vfc; # else struct vfsconf vfc; # endif #endif int r, xr = 0; #if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX) long nm; #endif t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); } if (r == -1 || xr == -1) { archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_bsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; } if (sfs.f_flags & MNT_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) r = getvfsbyname(sfs.f_fstypename, &vfc); if (r == -1) { archive_set_error(&a->archive, errno, "getvfsbyname failed"); return (ARCHIVE_FAILED); } if (vfc.vfc_flags & VFCF_SYNTHETIC) t->current_filesystem->synthetic = 1; else t->current_filesystem->synthetic = 0; #endif #if defined(MNT_NOATIME) if (sfs.f_flags & MNT_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; #else # if defined(_PC_NAME_MAX) /* Mac OS X does not have f_namemax in struct statfs. */ if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); # else nm = -1; # endif if (nm == -1) t->current_filesystem->name_max = NAME_MAX; else t->current_filesystem->name_max = nm; #endif #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL) /* * Gather current filesystem properties on NetBSD */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1; if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } if (tree_current_is_symblic_link_target(t)) { r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* Usuall come here unless NetBSD supports _PC_REC_XFER_ALIGN * for pathconf() function. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; #if defined(HAVE_STRUCT_STATVFS_F_IOSIZE) t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; #else t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } if (sfs.f_flag & ST_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\ defined(HAVE_STATFS) && defined(HAVE_FSTATFS) /* * Note: statfs is deprecated since LSB 3.2 */ #ifndef CIFS_SUPER_MAGIC #define CIFS_SUPER_MAGIC 0xFF534D42 #endif #ifndef DEVFS_SUPER_MAGIC #define DEVFS_SUPER_MAGIC 0x1373 #endif /* * Gather current filesystem properties on Linux */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_STATVFS) struct statvfs svfs; #endif int r, vr = 0, xr = 0; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_FSTATVFS) vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */ #endif r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(tree_current_access_path(t), &svfs); #endif r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATFS #if defined(HAVE_FSTATVFS) vr = fstatvfs(tree_current_dir_fd(t), &svfs); #endif r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(".", &svfs); #endif r = statfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1 || vr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ #if defined(HAVE_STATVFS) t->current_filesystem->xfer_align = svfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = svfs.f_bsize; t->current_filesystem->incr_xfer_size = svfs.f_bsize; #else t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } switch (sfs.f_type) { case AFS_SUPER_MAGIC: case CIFS_SUPER_MAGIC: case CODA_SUPER_MAGIC: case NCP_SUPER_MAGIC:/* NetWare */ case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: t->current_filesystem->remote = 1; t->current_filesystem->synthetic = 0; break; case DEVFS_SUPER_MAGIC: case PROC_SUPER_MAGIC: case USBDEVICE_SUPER_MAGIC: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 1; break; default: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 0; break; } #if defined(ST_NOATIME) #if defined(HAVE_STATVFS) if (svfs.f_flag & ST_NOATIME) #else if (sfs.f_flag & ST_NOATIME) #endif t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namelen; #endif return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATVFS_H) &&\ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) /* * Gather current filesystem properties on other posix platform. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatvfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; } #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; #endif return (ARCHIVE_OK); } #else /* * Generic: Gather current filesystem properties. * TODO: Is this generic function really needed? */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; #if defined(_PC_NAME_MAX) && defined(USE_READDIR_R) long nm; #endif t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ t->current_filesystem->noatime = 0; (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */ t->current_filesystem->xfer_align = -1;/* Unknown */ t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = -1; t->current_filesystem->incr_xfer_size = -1; #if defined(USE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) # endif /* _PC_NAME_MAX */ /* * Some sysmtes (HP-UX or others?) incorrectly defined * NAME_MAX macro to be a smaller value. */ # if defined(NAME_MAX) && NAME_MAX >= 255 t->current_filesystem->name_max = NAME_MAX; # else /* No way to get a trusted value of maximum filename * length. */ t->current_filesystem->name_max = PATH_MAX; # endif /* NAME_MAX */ # if defined(_PC_NAME_MAX) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #endif static int close_and_restore_time(int fd, struct tree *t, struct restore_time *rt) { #ifndef HAVE_UTIMES (void)t; /* UNUSED */ (void)rt; /* UNUSED */ return (close(fd)); #else #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) struct timespec timespecs[2]; #endif struct timeval times[2]; if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) { if (fd >= 0) return (close(fd)); else return (0); } #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) timespecs[1].tv_sec = rt->mtime; timespecs[1].tv_nsec = rt->mtime_nsec; timespecs[0].tv_sec = rt->atime; timespecs[0].tv_nsec = rt->atime_nsec; /* futimens() is defined in POSIX.1-2008. */ if (futimens(fd, timespecs) == 0) return (close(fd)); #endif times[1].tv_sec = rt->mtime; times[1].tv_usec = rt->mtime_nsec / 1000; times[0].tv_sec = rt->atime; times[0].tv_usec = rt->atime_nsec / 1000; #if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (futimes(fd, times) == 0) return (close(fd)); #endif close(fd); #if defined(HAVE_FUTIMESAT) if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0) return (0); #endif #ifdef HAVE_LUTIMES if (lutimes(rt->name, times) != 0) #else if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0) #endif return (-1); #endif return (0); } static int open_on_current_dir(struct tree *t, const char *path, int flags) { #ifdef HAVE_OPENAT return (openat(tree_current_dir_fd(t), path, flags)); #else if (tree_enter_working_dir(t) != 0) return (-1); return (open(path, flags)); #endif } static int tree_dup(int fd) { int new_fd; #ifdef F_DUPFD_CLOEXEC static volatile int can_dupfd_cloexec = 1; if (can_dupfd_cloexec) { new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd != -1) return (new_fd); /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, * but it cannot be used. So we have to try dup(). */ /* We won't try F_DUPFD_CLOEXEC. */ can_dupfd_cloexec = 0; } #endif /* F_DUPFD_CLOEXEC */ new_fd = dup(fd); __archive_ensure_cloexec_flag(new_fd); return (new_fd); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const char *path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); te->symlink_parent_fd = -1; archive_strcpy(&te->name, path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->restore_time.name = te->name.s; if (rt != NULL) { te->restore_time.mtime = rt->mtime; te->restore_time.mtime_nsec = rt->mtime_nsec; te->restore_time.atime = rt->atime; te->restore_time.atime_nsec = rt->atime_nsec; te->restore_time.filetype = rt->filetype; te->restore_time.noatime = rt->noatime; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const char *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == '/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_string_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/') archive_strappend_char(&t->path, '/'); t->basename = t->path.s + archive_strlen(&t->path); archive_strncat(&t->path, name, name_length); t->restore_time.name = t->basename; } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const char *path, int symlink_mode, int restore_time) { struct tree *t; if ((t = calloc(1, sizeof(*t))) == NULL) return (NULL); archive_string_init(&t->path); archive_string_ensure(&t->path, 31); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { t->flags = (restore_time)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; t->tree_errno = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_DIR_HANDLE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&t->path); t->entry_fd = -1; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* First item is set up a lot like a symlink traversal. */ tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } static int tree_descent(struct tree *t) { int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; } else { t->depth++; /* If it is a link, set up fd for the ascent. */ if (t->stack->flags & isDirLink) { t->stack->symlink_parent_fd = t->working_dir_fd; t->openCount++; if (t->openCount > t->maxOpenCount) t->maxOpenCount = t->openCount; } else close(t->working_dir_fd); /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; } return (r); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; int new_fd, r = 0, prev_dir_fd; te = t->stack; prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; else { new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(new_fd); } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; } else { /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; /* Current directory has been changed, we should * close an fd of previous working directory. */ close_and_restore_time(prev_dir_fd, t, &te->restore_time); if (te->flags & isDirLink) { t->openCount--; te->symlink_parent_fd = -1; } t->depth--; } return (r); } /* * Return to the initial directory where tree_open() was performed. */ static int tree_enter_initial_dir(struct tree *t) { int r = 0; if ((t->flags & onInitialDir) == 0) { r = fchdir(t->initial_dir_fd); if (r == 0) { t->flags &= ~onWorkingDir; t->flags |= onInitialDir; } } return (r); } /* * Restore working directory of directory traversals. */ static int tree_enter_working_dir(struct tree *t) { int r = 0; /* * Change the current directory if really needed. * Sometimes this is unneeded when we did not do * descent. */ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) { r = fchdir(t->working_dir_fd); if (r == 0) { t->flags &= ~onInitialDir; t->flags |= onWorkingDir; } } return (r); } static int tree_current_dir_fd(struct tree *t) { return (t->working_dir_fd); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; while (t->basename[0] == '/') t->basename++; archive_string_free(&te->name); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_DIR_HANDLE) { r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); /* t->dirname_length = t->path_length; */ /* tree_pop(t); */ t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_posix(struct tree *t) { int r; const char *name; size_t namelen; if (t->d == NULL) { #if defined(USE_READDIR_R) size_t dirent_size; #endif #if defined(HAVE_FDOPENDIR) t->d = fdopendir(tree_dup(t->working_dir_fd)); #else /* HAVE_FDOPENDIR */ if (tree_enter_working_dir(t) == 0) { t->d = opendir("."); #if HAVE_DIRFD || defined(dirfd) __archive_ensure_cloexec_flag(dirfd(t->d)); #endif } #endif /* HAVE_FDOPENDIR */ if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } #if defined(USE_READDIR_R) dirent_size = offsetof(struct dirent, d_name) + t->filesystem_table[t->current->filesystem_id].name_max + 1; if (t->dirent == NULL || t->dirent_allocated < dirent_size) { free(t->dirent); t->dirent = malloc(dirent_size); if (t->dirent == NULL) { closedir(t->d); t->d = INVALID_DIR_HANDLE; (void)tree_ascend(t); tree_pop(t); t->tree_errno = ENOMEM; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->dirent_allocated = dirent_size; } #endif /* USE_READDIR_R */ } for (;;) { errno = 0; #if defined(USE_READDIR_R) r = readdir_r(t->d, t->dirent, &t->de); #ifdef _AIX /* Note: According to the man page, return value 9 indicates * that the readdir_r was not successful and the error code * is set to the global errno variable. And then if the end * of directory entries was reached, the return value is 9 * and the third parameter is set to NULL and errno is * unchanged. */ if (r == 9) r = errno; #endif /* _AIX */ if (r != 0 || t->de == NULL) { #else t->de = readdir(t->d); if (t->de == NULL) { r = errno; #endif closedir(t->d); t->d = INVALID_DIR_HANDLE; if (r != 0) { t->tree_errno = r; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } else return (0); } name = t->de->d_name; namelen = D_NAMELEN(t->de); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == '.' && name[1] == '\0') continue; if (name[0] == '.' && name[1] == '.' && name[2] == '\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } /* * Get the stat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->st, 0) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->lst, AT_SYMLINK_NOFOLLOW) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (lstat(tree_current_access_path(t), &t->lst) != 0) #endif return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { const struct stat *st; /* * If we already have lstat() info, then try some * cheap tests to determine if this is a dir. */ if (t->flags & hasLstat) { /* If lstat() says it's a dir, it must be a dir. */ st = tree_current_lstat(t); if (st == NULL) return 0; if (S_ISDIR(st->st_mode)) return 1; /* Not a dir; might be a link to a dir. */ /* If it's not a link, then it's not a link to a dir. */ if (!S_ISLNK(st->st_mode)) return 0; /* * It's a link, but we don't know what it's a link to, * so we'll have to use stat(). */ } st = tree_current_stat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { const struct stat *st; /* * If stat() says it isn't a dir, then it's not a dir. * If stat() data is cached, this check is free, so do it first. */ if (t->flags & hasStat) { st = tree_current_stat(t); if (st == NULL) return (0); if (!S_ISDIR(st->st_mode)) return (0); } /* * Either stat() said it was a dir (in which case, we have * to determine whether it's really a link to a dir) or * stat() info wasn't available. So we use lstat(), which * hopefully is already cached. */ st = tree_current_lstat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const struct stat *st) { struct tree_entry *te; for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == (int64_t)st->st_dev && te->ino == (int64_t)st->st_ino) return (1); } return (0); } /* * Test whether the current file is symbolic link target and * on the other filesystem. */ static int tree_current_is_symblic_link_target(struct tree *t) { static const struct stat *lst, *st; lst = tree_current_lstat(t); st = tree_current_stat(t); return (st != NULL && lst != NULL && (int64_t)st->st_dev == t->current_filesystem->dev && st->st_dev != lst->st_dev); } /* * Return the access path for the entry just returned from tree_next(). */ static const char * tree_current_access_path(struct tree *t) { return (t->basename); } /* * Return the full path for the entry just returned from tree_next(). */ static const char * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } /* Close the handle of readdir(). */ if (t->d != INVALID_DIR_HANDLE) { closedir(t->d); t->d = INVALID_DIR_HANDLE; } /* Release anything remaining in the stack. */ while (t->stack != NULL) { if (t->stack->flags & isDirLink) close(t->stack->symlink_parent_fd); tree_pop(t); } if (t->working_dir_fd >= 0) { close(t->working_dir_fd); t->working_dir_fd = -1; } if (t->initial_dir_fd >= 0) { close(t->initial_dir_fd); t->initial_dir_fd = -1; } } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_string_free(&t->path); #if defined(USE_READDIR_R) free(t->dirent); #endif free(t->sparse_list); for (i = 0; i < t->max_filesystem_id; i++) free(t->filesystem_table[i].allocation_ptr); free(t->filesystem_table); free(t); } #endif Index: vendor/libarchive/dist/libarchive/archive_read_disk_windows.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_disk_windows.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read_disk_windows.c (revision 310622) @@ -1,2308 +1,2308 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #if defined(_WIN32) && !defined(__CYGWIN__) #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef IO_REPARSE_TAG_SYMLINK /* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */ #define IO_REPARSE_TAG_SYMLINK 0xA000000CL #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ struct restore_time { const wchar_t *full_path; FILETIME lastWriteTime; FILETIME lastAccessTime; mode_t filetype; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; size_t full_path_dir_length; struct archive_wstring name; struct archive_wstring full_path; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; DWORD bytesPerSector; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * On Windows, "first visit" is handled as a pattern to be handed to * _findfirst(). This is consistent with Windows conventions that * file patterns are handled within the application. On Posix, * "first visit" is just returned to the client. */ #define MAX_OVERLAPPED 8 #define BUFFER_SIZE (1024 * 8) #define DIRECT_IO 0/* Disabled */ #define ASYNC_IO 1/* Enabled */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; HANDLE d; WIN32_FIND_DATAW _findData; WIN32_FIND_DATAW *findData; int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* A full path with "\\?\" prefix. */ struct archive_wstring full_path; size_t full_path_dir_length; /* Dynamically-sized buffer for holding path */ struct archive_wstring path; /* Last path element */ const wchar_t *basename; /* Leading dir length */ size_t dirname_length; int depth; BY_HANDLE_FILE_INFORMATION lst; BY_HANDLE_FILE_INFORMATION st; int descend; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesystem; HANDLE entry_fh; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; int ol_idx_doing; int ol_idx_done; int ol_num_doing; int ol_num_done; int64_t ol_remaining_bytes; int64_t ol_total; struct la_overlapped { OVERLAPPED ol; struct archive * _a; unsigned char *buff; size_t buff_size; int64_t offset; size_t bytes_expected; size_t bytes_transferred; } ol[MAX_OVERLAPPED]; int direct_io; int async_io; }; #define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber) /* Treat FileIndex as i-node. We should remove a sequence number * which is high-16-bits of nFileIndexHigh. */ #define bhfi_ino(bhfi) \ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \ + (bhfi)->nFileIndexLow) /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define needsRestoreTimes 128 static int tree_dir_next_windows(struct tree *t, const wchar_t *pattern); /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const wchar_t *, int, int); static struct tree *tree_reopen(struct tree *, const wchar_t *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const wchar_t *, const wchar_t *, int, int64_t, int64_t, struct restore_time *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * */ static const wchar_t *tree_current_path(struct tree *); static const wchar_t *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const BY_HANDLE_FILE_INFORMATION *tree_current_stat(struct tree *); static const BY_HANDLE_FILE_INFORMATION *tree_current_lstat(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_link(struct tree *); /* Instead of archive_entry_copy_stat for BY_HANDLE_FILE_INFORMATION */ static void tree_archive_entry_copy_bhfi(struct archive_entry *, struct tree *, const BY_HANDLE_FILE_INFORMATION *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const BY_HANDLE_FILE_INFORMATION *); static int _archive_read_disk_open_w(struct archive *, const wchar_t *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(HANDLE, struct tree *, struct restore_time *); static int setup_sparse_from_disk(struct archive_read_disk *, struct archive_entry *, HANDLE); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->enable_copyfile = 1; a->traverse_mount_points = 1; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); a->restore_time = 1; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { a->restore_time = 0; if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } if (flags & ARCHIVE_READDISK_HONOR_NODUMP) a->honor_nodump = 1; else a->honor_nodump = 0; if (flags & ARCHIVE_READDISK_MAC_COPYFILE) a->enable_copyfile = 1; else a->enable_copyfile = 0; if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) a->traverse_mount_points = 0; else a->traverse_mount_points = 1; return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } static int64_t align_num_per_sector(struct tree *t, int64_t size) { int64_t surplus; size += t->current_filesystem->bytesPerSector -1; surplus = size % t->current_filesystem->bytesPerSector; size -= surplus; return (size); } static int start_next_async_read(struct archive_read_disk *a, struct tree *t) { struct la_overlapped *olp; DWORD buffbytes, rbytes; if (t->ol_remaining_bytes == 0) return (ARCHIVE_EOF); olp = &(t->ol[t->ol_idx_doing]); t->ol_idx_doing = (t->ol_idx_doing + 1) % MAX_OVERLAPPED; /* Allocate read buffer. */ if (olp->buff == NULL) { void *p; size_t s = (size_t)align_num_per_sector(t, BUFFER_SIZE); p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } olp->buff = p; olp->buff_size = s; olp->_a = &a->archive; olp->ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if (olp->ol.hEvent == NULL) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "CreateEvent failed"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } else ResetEvent(olp->ol.hEvent); buffbytes = (DWORD)olp->buff_size; if (buffbytes > t->current_sparse->length) buffbytes = (DWORD)t->current_sparse->length; /* Skip hole. */ if (t->current_sparse->offset > t->ol_total) { t->ol_remaining_bytes -= t->current_sparse->offset - t->ol_total; } olp->offset = t->current_sparse->offset; olp->ol.Offset = (DWORD)(olp->offset & 0xffffffff); olp->ol.OffsetHigh = (DWORD)(olp->offset >> 32); if (t->ol_remaining_bytes > buffbytes) { olp->bytes_expected = buffbytes; t->ol_remaining_bytes -= buffbytes; } else { olp->bytes_expected = (size_t)t->ol_remaining_bytes; t->ol_remaining_bytes = 0; } olp->bytes_transferred = 0; t->current_sparse->offset += buffbytes; t->current_sparse->length -= buffbytes; t->ol_total = t->current_sparse->offset; if (t->current_sparse->length == 0 && t->ol_remaining_bytes > 0) t->current_sparse++; if (!ReadFile(t->entry_fh, olp->buff, buffbytes, &rbytes, &(olp->ol))) { DWORD lasterr; lasterr = GetLastError(); if (lasterr == ERROR_HANDLE_EOF) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Reading file truncated"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } else if (lasterr != ERROR_IO_PENDING) { if (lasterr == ERROR_NO_DATA) errno = EAGAIN; else if (lasterr == ERROR_ACCESS_DENIED) errno = EBADF; else la_dosmaperr(lasterr); archive_set_error(&a->archive, errno, "Read error"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } else olp->bytes_transferred = rbytes; t->ol_num_doing++; return (t->ol_remaining_bytes == 0)? ARCHIVE_EOF: ARCHIVE_OK; } static void cancel_async(struct tree *t) { if (t->ol_num_doing != t->ol_num_done) { CancelIo(t->entry_fh); t->ol_num_doing = t->ol_num_done = 0; } } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; struct la_overlapped *olp; DWORD bytes_transferred; int r = ARCHIVE_FATAL; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Make a request to read the file in asynchronous. */ if (t->ol_num_doing == 0) { do { r = start_next_async_read(a, t); if (r == ARCHIVE_FATAL) goto abort_read_data; if (!t->async_io) break; } while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED); } else { if (start_next_async_read(a, t) == ARCHIVE_FATAL) goto abort_read_data; } olp = &(t->ol[t->ol_idx_done]); t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED; if (olp->bytes_transferred) bytes_transferred = (DWORD)olp->bytes_transferred; else if (!GetOverlappedResult(t->entry_fh, &(olp->ol), &bytes_transferred, TRUE)) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "GetOverlappedResult failed"); a->archive.state = ARCHIVE_STATE_FATAL; r = ARCHIVE_FATAL; goto abort_read_data; } t->ol_num_done++; if (bytes_transferred == 0 || olp->bytes_expected != bytes_transferred) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Reading file truncated"); a->archive.state = ARCHIVE_STATE_FATAL; r = ARCHIVE_FATAL; goto abort_read_data; } *buff = olp->buff; *size = bytes_transferred; *offset = olp->offset; if (olp->offset > t->entry_total) t->entry_remaining_bytes -= olp->offset - t->entry_total; t->entry_total = olp->offset + *size; t->entry_remaining_bytes -= *size; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; t->entry_eof = 1; } return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fh != INVALID_HANDLE_VALUE) { cancel_async(t); /* Close the current file descriptor */ close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const BY_HANDLE_FILE_INFORMATION *st; const BY_HANDLE_FILE_INFORMATION *lst; const char*name; int descend, r; st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%ls: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, t->tree_errno, "%ls: Couldn't visit directory", tree_current_path(t)); return (ARCHIVE_FAILED); case 0: return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { archive_set_error(&a->archive, t->tree_errno, "%ls: Cannot stat", tree_current_path(t)); return (ARCHIVE_FAILED); } break; } } while (lst == NULL); archive_entry_copy_pathname_w(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, bhfi_dev(st)) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; if (!a->traverse_mount_points) { if (t->initial_filesystem_id != t->current_filesystem_id) return (ARCHIVE_RETRY); } t->descend = descend; tree_archive_entry_copy_bhfi(entry, t, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, - * especially, invokng a callback. */ + * especially, invoking a callback. */ t->restore_time.lastWriteTime = st->ftLastWriteTime; t->restore_time.lastAccessTime = st->ftLastAccessTime; t->restore_time.filetype = archive_entry_filetype(entry); /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } archive_entry_copy_sourcepath_w(entry, tree_current_access_path(t)); r = ARCHIVE_OK; if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) > 0) { DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; if (t->async_io) flags |= FILE_FLAG_OVERLAPPED; if (t->direct_io) flags |= FILE_FLAG_NO_BUFFERING; else flags |= FILE_FLAG_SEQUENTIAL_SCAN; t->entry_fh = CreateFileW(tree_current_access_path(t), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL); if (t->entry_fh == INVALID_HANDLE_VALUE) { archive_set_error(&a->archive, errno, "Couldn't open %ls", tree_current_path(a->tree)); return (ARCHIVE_FAILED); } /* Find sparse data from the disk. */ if (archive_entry_hardlink(entry) == NULL && (st->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0) r = setup_sparse_from_disk(a, entry, t->entry_fh); } return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fh != INVALID_HANDLE_VALUE) { cancel_async(t); close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; } while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY) archive_entry_clear(entry); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } t->ol_idx_doing = t->ol_idx_done = 0; t->ol_num_doing = t->ol_num_done = 0; t->ol_remaining_bytes = t->entry_remaining_bytes; t->ol_total = 0; a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t aligned, length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } /* * Get sparse list and make sure those offsets and lengths are * aligned by a sector size. */ for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); aligned = align_num_per_sector(t, offset); if (aligned != offset) { aligned -= t->current_filesystem->bytesPerSector; length += offset - aligned; } t->sparse_list[i].offset = aligned; aligned = align_num_per_sector(t, length); t->sparse_list[i].length = aligned; } aligned = align_num_per_sector(t, archive_entry_size(entry)); if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = aligned; } else { int j, last = i; t->sparse_list[i].offset = aligned; t->sparse_list[i].length = 0; for (i = 0; i < last; i++) { if ((t->sparse_list[i].offset + t->sparse_list[i].length) <= t->sparse_list[i+1].offset) continue; /* * Now sparse_list[i+1] is overlapped by sparse_list[i]. * Merge those two. */ length = t->sparse_list[i+1].offset - t->sparse_list[i].offset; t->sparse_list[i+1].offset = t->sparse_list[i].offset; t->sparse_list[i+1].length += length; /* Remove sparse_list[i]. */ for (j = i; j < last; j++) { t->sparse_list[j].offset = t->sparse_list[j+1].offset; t->sparse_list[j].length = t->sparse_list[j+1].length; } last--; } } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->full_path.s, t->current_filesystem_id, bhfi_dev(&(t->lst)), bhfi_ino(&(t->lst)), &t->restore_time); t->stack->flags |= isDir; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->full_path.s, t->current_filesystem_id, bhfi_dev(&(t->st)), bhfi_ino(&(t->st)), &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_wstring wpath; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); /* Make a wchar_t string from a char string. */ archive_string_init(&wpath); if (archive_wstring_append_from_mbs(&wpath, pathname, strlen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a wchar_t string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open_w(_a, wpath.s); archive_wstring_free(&wpath); return (ret); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); return (_archive_read_disk_open_w(_a, pathname)); } static int _archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) a->tree = tree_reopen(a->tree, pathname, a->restore_time); else a->tree = tree_open(pathname, a->symlink_mode, a->restore_time); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate directory traversal data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * There is a new filesystem, we generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = (int)s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } /* * If symlink is broken, statfs or statvfs will fail. * Use its directory path instead. */ static wchar_t * safe_path_for_statfs(struct tree *t) { const wchar_t *path; wchar_t *cp, *p = NULL; path = tree_current_access_path(t); if (tree_current_stat(t) == NULL) { p = _wcsdup(path); cp = wcsrchr(p, '/'); if (cp != NULL && wcslen(cp) >= 2) { cp[1] = '.'; cp[2] = '\0'; path = p; } } else p = _wcsdup(path); return (p); } /* * Get conditions of synthetic and remote on Windows */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; wchar_t vol[256]; wchar_t *path; t->current_filesystem->synthetic = -1;/* Not supported */ path = safe_path_for_statfs(t); if (!GetVolumePathNameW(path, vol, sizeof(vol)/sizeof(vol[0]))) { free(path); t->current_filesystem->remote = -1; t->current_filesystem->bytesPerSector = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "GetVolumePathName failed: %d", (int)GetLastError()); return (ARCHIVE_FAILED); } free(path); switch (GetDriveTypeW(vol)) { case DRIVE_UNKNOWN: case DRIVE_NO_ROOT_DIR: t->current_filesystem->remote = -1; break; case DRIVE_REMOTE: t->current_filesystem->remote = 1; break; default: t->current_filesystem->remote = 0; break; } if (!GetDiskFreeSpaceW(vol, NULL, &(t->current_filesystem->bytesPerSector), NULL, NULL)) { t->current_filesystem->bytesPerSector = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "GetDiskFreeSpace failed: %d", (int)GetLastError()); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) { HANDLE handle; int r = 0; if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype) return (0); /* Close a file descritor. * It will not be used for SetFileTime() because it has been opened * by a read only mode. */ if (h != INVALID_HANDLE_VALUE) CloseHandle(h); if ((t->flags & needsRestoreTimes) == 0) return (r); handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { errno = EINVAL; return (-1); } if (SetFileTime(handle, NULL, &rt->lastAccessTime, &rt->lastWriteTime) == 0) { errno = EINVAL; r = -1; } else r = 0; CloseHandle(handle); return (r); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); archive_wstrcpy(&te->name, path); archive_string_init(&te->full_path); archive_wstrcpy(&te->full_path, full_path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->full_path_dir_length = t->full_path_dir_length; te->restore_time.full_path = te->full_path.s; if (rt != NULL) { te->restore_time.lastWriteTime = rt->lastWriteTime; te->restore_time.lastAccessTime = rt->lastAccessTime; te->restore_time.filetype = rt->filetype; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const wchar_t *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = L'\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == L'/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_wstring_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != L'/') archive_wstrappend_wchar(&t->path, L'/'); t->basename = t->path.s + archive_strlen(&t->path); archive_wstrncat(&t->path, name, name_length); t->restore_time.full_path = t->basename; if (t->full_path_dir_length > 0) { t->full_path.s[t->full_path_dir_length] = L'\0'; t->full_path.length = t->full_path_dir_length; size_needed = name_length + t->full_path_dir_length + 2; archive_wstring_ensure(&t->full_path, size_needed); /* Add a separating '\' if it's needed. */ if (t->full_path.s[archive_strlen(&t->full_path)-1] != L'\\') archive_wstrappend_wchar(&t->full_path, L'\\'); archive_wstrncat(&t->full_path, name, name_length); t->restore_time.full_path = t->full_path.s; } } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const wchar_t *path, int symlink_mode, int restore_time) { struct tree *t; t = calloc(1, sizeof(*t)); archive_string_init(&(t->full_path)); archive_string_init(&t->path); archive_wstring_ensure(&t->path, 15); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const wchar_t *path, int restore_time) { struct archive_wstring ws; wchar_t *pathname, *p, *base; t->flags = (restore_time)?needsRestoreTimes:0; t->visit_type = 0; t->tree_errno = 0; t->full_path_dir_length = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_HANDLE_VALUE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&(t->full_path)); archive_string_empty(&t->path); t->entry_fh = INVALID_HANDLE_VALUE; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* Get wchar_t strings from char strings. */ archive_string_init(&ws); archive_wstrcpy(&ws, path); pathname = ws.s; /* Get a full-path-name. */ p = __la_win_permissive_name_w(pathname); if (p == NULL) goto failed; archive_wstrcpy(&(t->full_path), p); free(p); /* Convert path separators from '\' to '/' */ for (p = pathname; *p != L'\0'; ++p) { if (*p == L'\\') *p = L'/'; } base = pathname; /* First item is set up a lot like a symlink traversal. */ /* printf("Looking for wildcard in %s\n", path); */ if ((base[0] == L'/' && base[1] == L'/' && base[2] == L'?' && base[3] == L'/' && (wcschr(base+4, L'*') || wcschr(base+4, L'?'))) || (!(base[0] == L'/' && base[1] == L'/' && base[2] == L'?' && base[3] == L'/') && (wcschr(base, L'*') || wcschr(base, L'?')))) { // It has a wildcard in it... // Separate the last element. p = wcsrchr(base, L'/'); if (p != NULL) { *p = L'\0'; tree_append(t, base, p - base); t->dirname_length = archive_strlen(&t->path); base = p + 1; } p = wcsrchr(t->full_path.s, L'\\'); if (p != NULL) { *p = L'\0'; t->full_path.length = wcslen(t->full_path.s); t->full_path_dir_length = archive_strlen(&t->full_path); } } tree_push(t, base, t->full_path.s, 0, 0, 0, NULL); archive_wstring_free(&ws); t->stack->flags = needsFirstVisit; /* * Debug flag for Direct IO(No buffering) or Async IO. * Those dependent on environment variable switches * will be removed until next release. */ { const char *e; if ((e = getenv("LIBARCHIVE_DIRECT_IO")) != NULL) { if (e[0] == '0') t->direct_io = 0; else t->direct_io = 1; fprintf(stderr, "LIBARCHIVE_DIRECT_IO=%s\n", (t->direct_io)?"Enabled":"Disabled"); } else t->direct_io = DIRECT_IO; if ((e = getenv("LIBARCHIVE_ASYNC_IO")) != NULL) { if (e[0] == '0') t->async_io = 0; else t->async_io = 1; fprintf(stderr, "LIBARCHIVE_ASYNC_IO=%s\n", (t->async_io)?"Enabled":"Disabled"); } else t->async_io = ASYNC_IO; } return (t); failed: archive_wstring_free(&ws); tree_free(t); return (NULL); } static int tree_descent(struct tree *t) { t->dirname_length = archive_strlen(&t->path); t->full_path_dir_length = archive_strlen(&t->full_path); t->depth++; return (0); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; te = t->stack; t->depth--; close_and_restore_time(INVALID_HANDLE_VALUE, t, &te->restore_time); return (0); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->full_path.s[t->full_path_dir_length] = L'\0'; t->full_path.length = t->full_path_dir_length; t->path.s[t->dirname_length] = L'\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; t->full_path_dir_length = te->full_path_dir_length; while (t->basename[0] == L'/') t->basename++; archive_wstring_free(&te->name); archive_wstring_free(&te->full_path); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_HANDLE_VALUE) { r = tree_dir_next_windows(t, NULL); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { wchar_t *d = t->stack->name.s; t->stack->flags &= ~needsFirstVisit; if (!(d[0] == L'/' && d[1] == L'/' && d[2] == L'?' && d[3] == L'/') && (wcschr(d, L'*') || wcschr(d, L'?'))) { r = tree_dir_next_windows(t, d); if (r == 0) continue; return (r); } else { HANDLE h = FindFirstFileW(d, &t->_findData); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->findData = &t->_findData; FindClose(h); } /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); //t->dirname_length = t->path_length; //tree_pop(t); t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_windows(t, L"*"); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_windows(struct tree *t, const wchar_t *pattern) { const wchar_t *name; size_t namelen; int r; for (;;) { if (pattern != NULL) { struct archive_wstring pt; archive_string_init(&pt); archive_wstring_ensure(&pt, archive_strlen(&(t->full_path)) + 2 + wcslen(pattern)); archive_wstring_copy(&pt, &(t->full_path)); archive_wstrappend_wchar(&pt, L'\\'); archive_wstrcat(&pt, pattern); t->d = FindFirstFileW(pt.s, &t->_findData); archive_wstring_free(&pt); if (t->d == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } t->findData = &t->_findData; pattern = NULL; } else if (!FindNextFileW(t->d, &t->_findData)) { FindClose(t->d); t->d = INVALID_HANDLE_VALUE; t->findData = NULL; return (0); } name = t->findData->cFileName; namelen = wcslen(name); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == L'.' && name[1] == L'\0') continue; if (name[0] == L'.' && name[1] == L'.' && name[2] == L'\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) static void fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns) { ULARGE_INTEGER utc; utc.HighPart = filetime->dwHighDateTime; utc.LowPart = filetime->dwLowDateTime; if (utc.QuadPart >= EPOC_TIME) { utc.QuadPart -= EPOC_TIME; /* milli seconds base */ *t = (time_t)(utc.QuadPart / 10000000); /* nano seconds base */ *ns = (long)(utc.QuadPart % 10000000) * 100; } else { *t = 0; *ns = 0; } } static void entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path, const WIN32_FIND_DATAW *findData, const BY_HANDLE_FILE_INFORMATION *bhfi) { time_t secs; long nsecs; mode_t mode; fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs); archive_entry_set_atime(entry, secs, nsecs); fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs); archive_entry_set_mtime(entry, secs, nsecs); fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs); archive_entry_set_birthtime(entry, secs, nsecs); archive_entry_set_ctime(entry, secs, nsecs); archive_entry_set_dev(entry, bhfi_dev(bhfi)); archive_entry_set_ino64(entry, bhfi_ino(bhfi)); if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) archive_entry_set_nlink(entry, bhfi->nNumberOfLinks + 1); else archive_entry_set_nlink(entry, bhfi->nNumberOfLinks); archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32) + bhfi->nFileSizeLow); archive_entry_set_uid(entry, 0); archive_entry_set_gid(entry, 0); archive_entry_set_rdev(entry, 0); mode = S_IRUSR | S_IRGRP | S_IROTH; if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) mode |= S_IWUSR | S_IWGRP | S_IWOTH; if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && findData != NULL && findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) mode |= S_IFLNK; else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else { const wchar_t *p; mode |= S_IFREG; p = wcsrchr(path, L'.'); if (p != NULL && wcslen(p) == 4) { switch (p[1]) { case L'B': case L'b': if ((p[2] == L'A' || p[2] == L'a' ) && (p[3] == L'T' || p[3] == L't' )) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; case L'C': case L'c': if (((p[2] == L'M' || p[2] == L'm' ) && (p[3] == L'D' || p[3] == L'd' ))) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; case L'E': case L'e': if ((p[2] == L'X' || p[2] == L'x' ) && (p[3] == L'E' || p[3] == L'e' )) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; default: break; } } } archive_entry_set_mode(entry, mode); } static void tree_archive_entry_copy_bhfi(struct archive_entry *entry, struct tree *t, const BY_HANDLE_FILE_INFORMATION *bhfi) { entry_copy_bhfi(entry, tree_current_path(t), t->findData, bhfi); } static int tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st, int sim_lstat) { HANDLE h; int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; if (sim_lstat && tree_current_is_physical_link(t)) flag |= FILE_FLAG_OPEN_REPARSE_POINT; h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; return (0); } r = GetFileInformationByHandle(h, st); CloseHandle(h); return (r); } /* * Get the stat() data for the entry just returned from tree_next(). */ static const BY_HANDLE_FILE_INFORMATION * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { if (!tree_current_file_information(t, &t->st, 0)) return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const BY_HANDLE_FILE_INFORMATION * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { if (!tree_current_file_information(t, &t->lst, 1)) return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { if (t->findData) return (t->findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); return (0); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { if (tree_current_is_physical_link(t)) return (0); return (tree_current_is_dir(t)); } /* * Test whether current entry is a symbolic link. */ static int tree_current_is_physical_link(struct tree *t) { if (t->findData) return ((t->findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (t->findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK)); return (0); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const BY_HANDLE_FILE_INFORMATION *st) { struct tree_entry *te; int64_t dev = bhfi_dev(st); int64_t ino = bhfi_ino(st); for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == dev && te->ino == ino) return (1); } return (0); } /* * Return the access path for the entry just returned from tree_next(). */ static const wchar_t * tree_current_access_path(struct tree *t) { return (t->full_path.s); } /* * Return the full path for the entry just returned from tree_next(). */ static const wchar_t * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fh != INVALID_HANDLE_VALUE) { cancel_async(t); close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; } /* Close the handle of FindFirstFileW */ if (t->d != INVALID_HANDLE_VALUE) { FindClose(t->d); t->d = INVALID_HANDLE_VALUE; t->findData = NULL; } /* Release anything remaining in the stack. */ while (t->stack != NULL) tree_pop(t); } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_wstring_free(&t->path); archive_wstring_free(&t->full_path); free(t->sparse_list); free(t->filesystem_table); for (i = 0; i < MAX_OVERLAPPED; i++) { if (t->ol[i].buff) VirtualFree(t->ol[i].buff, 0, MEM_RELEASE); CloseHandle(t->ol[i].ol.hEvent); } free(t); } /* * Populate the archive_entry with metadata from the disk. */ 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 wchar_t *path; const wchar_t *wname; const char *name; HANDLE h; BY_HANDLE_FILE_INFORMATION bhfi; DWORD fileAttributes = 0; int r; archive_clear_error(_a); wname = archive_entry_sourcepath_w(entry); if (wname == NULL) wname = archive_entry_pathname_w(entry); if (wname == NULL) { archive_set_error(&a->archive, EINVAL, "Can't get a wide character version of the path"); return (ARCHIVE_FAILED); } path = __la_win_permissive_name_w(wname); if (st == NULL) { /* * Get metadata through GetFileInformationByHandle(). */ if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); r = GetFileInformationByHandle(h, &bhfi); if (r == 0) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't GetFileInformationByHandle"); return (ARCHIVE_FAILED); } entry_copy_bhfi(entry, path, NULL, &bhfi); } else { WIN32_FIND_DATAW findData; DWORD flag, desiredAccess; h = FindFirstFileW(path, &findData); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't FindFirstFileW"); return (ARCHIVE_FAILED); } FindClose(h); flag = FILE_FLAG_BACKUP_SEMANTICS; if (!a->follow_symlinks && (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { flag |= FILE_FLAG_OPEN_REPARSE_POINT; desiredAccess = 0; } else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { desiredAccess = 0; } else desiredAccess = GENERIC_READ; h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't CreateFileW"); return (ARCHIVE_FAILED); } r = GetFileInformationByHandle(h, &bhfi); if (r == 0) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't GetFileInformationByHandle"); CloseHandle(h); return (ARCHIVE_FAILED); } entry_copy_bhfi(entry, path, &findData, &bhfi); } fileAttributes = bhfi.dwFileAttributes; } else { archive_entry_copy_stat(entry, st); h = INVALID_HANDLE_VALUE; } /* 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); /* * Can this file be sparse file ? */ if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) { if (h != INVALID_HANDLE_VALUE && fd < 0) CloseHandle(h); return (ARCHIVE_OK); } if (h == INVALID_HANDLE_VALUE) { if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); } else { h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't CreateFileW"); return (ARCHIVE_FAILED); } } r = GetFileInformationByHandle(h, &bhfi); if (r == 0) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't GetFileInformationByHandle"); if (h != INVALID_HANDLE_VALUE && fd < 0) CloseHandle(h); return (ARCHIVE_FAILED); } fileAttributes = bhfi.dwFileAttributes; } /* Sparse file must be set a mark, FILE_ATTRIBUTE_SPARSE_FILE */ if ((fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { if (fd < 0) CloseHandle(h); return (ARCHIVE_OK); } r = setup_sparse_from_disk(a, entry, h); if (fd < 0) CloseHandle(h); return (r); } /* * Windows sparse interface. */ #if defined(__MINGW32__) && !defined(FSCTL_QUERY_ALLOCATED_RANGES) #define FSCTL_QUERY_ALLOCATED_RANGES 0x940CF typedef struct { LARGE_INTEGER FileOffset; LARGE_INTEGER Length; } FILE_ALLOCATED_RANGE_BUFFER; #endif static int setup_sparse_from_disk(struct archive_read_disk *a, struct archive_entry *entry, HANDLE handle) { FILE_ALLOCATED_RANGE_BUFFER range, *outranges = NULL; size_t outranges_size; int64_t entry_size = archive_entry_size(entry); int exit_sts = ARCHIVE_OK; range.FileOffset.QuadPart = 0; range.Length.QuadPart = entry_size; outranges_size = 2048; outranges = (FILE_ALLOCATED_RANGE_BUFFER *)malloc(outranges_size); if (outranges == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); exit_sts = ARCHIVE_FATAL; goto exit_setup_sparse; } for (;;) { DWORD retbytes; BOOL ret; for (;;) { ret = DeviceIoControl(handle, FSCTL_QUERY_ALLOCATED_RANGES, &range, sizeof(range), outranges, (DWORD)outranges_size, &retbytes, NULL); if (ret == 0 && GetLastError() == ERROR_MORE_DATA) { free(outranges); outranges_size *= 2; outranges = (FILE_ALLOCATED_RANGE_BUFFER *) malloc(outranges_size); if (outranges == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); exit_sts = ARCHIVE_FATAL; goto exit_setup_sparse; } continue; } else break; } if (ret != 0) { if (retbytes > 0) { DWORD i, n; n = retbytes / sizeof(outranges[0]); if (n == 1 && outranges[0].FileOffset.QuadPart == 0 && outranges[0].Length.QuadPart == entry_size) break;/* This is not sparse. */ for (i = 0; i < n; i++) archive_entry_sparse_add_entry(entry, outranges[i].FileOffset.QuadPart, outranges[i].Length.QuadPart); range.FileOffset.QuadPart = outranges[n-1].FileOffset.QuadPart + outranges[n-1].Length.QuadPart; range.Length.QuadPart = entry_size - range.FileOffset.QuadPart; if (range.Length.QuadPart > 0) continue; } else { /* The remaining data is hole. */ archive_entry_sparse_add_entry(entry, range.FileOffset.QuadPart, range.Length.QuadPart); } break; } else { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "DeviceIoControl Failed: %lu", GetLastError()); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } } exit_setup_sparse: free(outranges); return (exit_sts); } #endif Index: vendor/libarchive/dist/libarchive/archive_read_support_filter_xz.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_filter_xz.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read_support_filter_xz.c (revision 310622) @@ -1,798 +1,796 @@ /*- * Copyright (c) 2009-2011 Michihiro NAKAJIMA * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna * 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_UNISTD_H #include #endif #if HAVE_LZMA_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_private.h" #include "archive_read_private.h" #if HAVE_LZMA_H && HAVE_LIBLZMA struct private_data { lzma_stream stream; unsigned char *out_block; size_t out_block_size; int64_t total_out; char eof; /* True = found end of compressed data. */ char in_stream; /* Following variables are used for lzip only. */ char lzip_ver; uint32_t crc32; int64_t member_in; int64_t member_out; }; #if LZMA_VERSION_MAJOR >= 5 /* Effectively disable the limiter. */ #define LZMA_MEMLIMIT UINT64_MAX #else /* NOTE: This needs to check memory size which running system has. */ #define LZMA_MEMLIMIT (1U << 30) #endif /* Combined lzip/lzma/xz filter */ static ssize_t xz_filter_read(struct archive_read_filter *, const void **); static int xz_filter_close(struct archive_read_filter *); static int xz_lzma_bidder_init(struct archive_read_filter *); #endif /* * Note that we can detect xz and lzma compressed files even if we * can't decompress them. (In fact, we like detecting them because we * can give better error messages.) So the bid framework here gets * compiled even if no lzma library is available. */ static int xz_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int xz_bidder_init(struct archive_read_filter *); static int lzma_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int lzma_bidder_init(struct archive_read_filter *); static int lzip_has_member(struct archive_read_filter *); static int lzip_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int lzip_bidder_init(struct archive_read_filter *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Deprecated; remove in libarchive 4.0 */ int archive_read_support_compression_xz(struct archive *a) { return archive_read_support_filter_xz(a); } #endif int archive_read_support_filter_xz(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_filter_xz"); if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->name = "xz"; bidder->bid = xz_bidder_bid; bidder->init = xz_bidder_init; bidder->options = NULL; bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external xz program for xz decompression"); return (ARCHIVE_WARN); #endif } #if ARCHIVE_VERSION_NUMBER < 4000000 int archive_read_support_compression_lzma(struct archive *a) { return archive_read_support_filter_lzma(a); } #endif int archive_read_support_filter_lzma(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_filter_lzma"); if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->name = "lzma"; bidder->bid = lzma_bidder_bid; bidder->init = lzma_bidder_init; bidder->options = NULL; bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external lzma program for lzma decompression"); return (ARCHIVE_WARN); #endif } #if ARCHIVE_VERSION_NUMBER < 4000000 int archive_read_support_compression_lzip(struct archive *a) { return archive_read_support_filter_lzip(a); } #endif int archive_read_support_filter_lzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_filter_lzip"); if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->name = "lzip"; bidder->bid = lzip_bidder_bid; bidder->init = lzip_bidder_init; bidder->options = NULL; bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external lzip program for lzip decompression"); return (ARCHIVE_WARN); #endif } /* * Test whether we can handle this data. */ static int xz_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; (void)self; /* UNUSED */ buffer = __archive_read_filter_ahead(filter, 6, &avail); if (buffer == NULL) return (0); /* * Verify Header Magic Bytes : FD 37 7A 58 5A 00 */ if (memcmp(buffer, "\xFD\x37\x7A\x58\x5A\x00", 6) != 0) return (0); return (48); } /* * Test whether we can handle this data. * * LZMA has a rather poor file signature. Zeros do not * make good signature bytes as a rule, and the only non-zero byte * here is an ASCII character. For example, an uncompressed tar * archive whose first file is ']' would satisfy this check. It may * be necessary to exclude LZMA from compression_all() because of * this. Clients of libarchive would then have to explicitly enable * LZMA checking instead of (or in addition to) compression_all() when * they have other evidence (file name, command-line option) to go on. */ static int lzma_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; uint32_t dicsize; uint64_t uncompressed_size; int bits_checked; (void)self; /* UNUSED */ buffer = __archive_read_filter_ahead(filter, 14, &avail); if (buffer == NULL) return (0); /* First byte of raw LZMA stream is commonly 0x5d. * The first byte is a special number, which consists of * three parameters of LZMA compression, a number of literal * context bits(which is from 0 to 8, default is 3), a number * of literal pos bits(which is from 0 to 4, default is 0), * a number of pos bits(which is from 0 to 4, default is 2). * The first byte is made by * (pos bits * 5 + literal pos bit) * 9 + * literal contest bit, * and so the default value in this field is * (2 * 5 + 0) * 9 + 3 = 0x5d. * lzma of LZMA SDK has options to change those parameters. * It means a range of this field is from 0 to 224. And lzma of * XZ Utils with option -e records 0x5e in this field. */ /* NOTE: If this checking of the first byte increases false * recognition, we should allow only 0x5d and 0x5e for the first * byte of LZMA stream. */ bits_checked = 0; if (buffer[0] > (4 * 5 + 4) * 9 + 8) return (0); /* Most likely value in the first byte of LZMA stream. */ if (buffer[0] == 0x5d || buffer[0] == 0x5e) bits_checked += 8; /* Sixth through fourteenth bytes are uncompressed size, * stored in little-endian order. `-1' means uncompressed * size is unknown and lzma of XZ Utils always records `-1' * in this field. */ uncompressed_size = archive_le64dec(buffer+5); if (uncompressed_size == (uint64_t)ARCHIVE_LITERAL_LL(-1)) bits_checked += 64; /* Second through fifth bytes are dictionary size, stored in * little-endian order. The minimum dictionary size is * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option - * -d12 and the maxinam dictionary size is 1 << 27(128MiB) + * -d12 and the maximum dictionary size is 1 << 27(128MiB) * which the one uses with option -d27. * NOTE: A comment of LZMA SDK source code says this dictionary * range is from 1 << 12 to 1 << 30. */ dicsize = archive_le32dec(buffer+1); switch (dicsize) { case 0x00001000:/* lzma of LZMA SDK option -d12. */ case 0x00002000:/* lzma of LZMA SDK option -d13. */ case 0x00004000:/* lzma of LZMA SDK option -d14. */ case 0x00008000:/* lzma of LZMA SDK option -d15. */ case 0x00010000:/* lzma of XZ Utils option -0 and -1. * lzma of LZMA SDK option -d16. */ case 0x00020000:/* lzma of LZMA SDK option -d17. */ case 0x00040000:/* lzma of LZMA SDK option -d18. */ case 0x00080000:/* lzma of XZ Utils option -2. * lzma of LZMA SDK option -d19. */ case 0x00100000:/* lzma of XZ Utils option -3. * lzma of LZMA SDK option -d20. */ case 0x00200000:/* lzma of XZ Utils option -4. * lzma of LZMA SDK option -d21. */ case 0x00400000:/* lzma of XZ Utils option -5. * lzma of LZMA SDK option -d22. */ case 0x00800000:/* lzma of XZ Utils option -6. * lzma of LZMA SDK option -d23. */ case 0x01000000:/* lzma of XZ Utils option -7. * lzma of LZMA SDK option -d24. */ case 0x02000000:/* lzma of XZ Utils option -8. * lzma of LZMA SDK option -d25. */ case 0x04000000:/* lzma of XZ Utils option -9. * lzma of LZMA SDK option -d26. */ case 0x08000000:/* lzma of LZMA SDK option -d27. */ bits_checked += 32; break; default: /* If a memory usage for encoding was not enough on * the platform where LZMA stream was made, lzma of * XZ Utils automatically decreased the dictionary * size to enough memory for encoding by 1Mi bytes * (1 << 20).*/ if (dicsize <= 0x03F00000 && dicsize >= 0x00300000 && (dicsize & ((1 << 20)-1)) == 0 && bits_checked == 8 + 64) { bits_checked += 32; break; } /* Otherwise dictionary size is unlikely. But it is * possible that someone makes lzma stream with * liblzma/LZMA SDK in one's dictionary size. */ return (0); } /* TODO: The above test is still very weak. It would be * good to do better. */ return (bits_checked); } static int lzip_has_member(struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; int bits_checked; int log2dic; buffer = __archive_read_filter_ahead(filter, 6, &avail); if (buffer == NULL) return (0); /* * Verify Header Magic Bytes : 4C 5A 49 50 (`LZIP') */ bits_checked = 0; if (memcmp(buffer, "LZIP", 4) != 0) return (0); bits_checked += 32; /* A version number must be 0 or 1 */ if (buffer[4] != 0 && buffer[4] != 1) return (0); bits_checked += 8; /* Dictionary size. */ log2dic = buffer[5] & 0x1f; if (log2dic < 12 || log2dic > 27) return (0); bits_checked += 8; return (bits_checked); } static int lzip_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { (void)self; /* UNUSED */ return (lzip_has_member(filter)); } #if HAVE_LZMA_H && HAVE_LIBLZMA /* * liblzma 4.999.7 and later support both lzma and xz streams. */ static int xz_bidder_init(struct archive_read_filter *self) { self->code = ARCHIVE_FILTER_XZ; self->name = "xz"; return (xz_lzma_bidder_init(self)); } static int lzma_bidder_init(struct archive_read_filter *self) { self->code = ARCHIVE_FILTER_LZMA; self->name = "lzma"; return (xz_lzma_bidder_init(self)); } static int lzip_bidder_init(struct archive_read_filter *self) { self->code = ARCHIVE_FILTER_LZIP; self->name = "lzip"; return (xz_lzma_bidder_init(self)); } /* * Set an error code and choose an error message */ static void set_error(struct archive_read_filter *self, int ret) { switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ case LZMA_OK: /* Decompressor made some progress. */ break; case LZMA_MEM_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Lzma library error: Cannot allocate memory"); break; case LZMA_MEMLIMIT_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Lzma library error: Out of memory"); break; case LZMA_FORMAT_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: format not recognized"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Invalid options"); break; case LZMA_DATA_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Corrupted input data"); break; case LZMA_BUF_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: No progress is possible"); break; default: /* Return an error. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed: Unknown error"); break; } } /* * Setup the callbacks. */ static int xz_lzma_bidder_init(struct archive_read_filter *self) { static const size_t out_block_size = 64 * 1024; void *out_block; struct private_data *state; int ret; state = (struct private_data *)calloc(sizeof(*state), 1); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for xz decompression"); free(out_block); free(state); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; self->read = xz_filter_read; self->skip = NULL; /* not supported */ self->close = xz_filter_close; state->stream.avail_in = 0; state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; state->crc32 = 0; if (self->code == ARCHIVE_FILTER_LZIP) { /* * We have to read a lzip header and use it to initialize * compression library, thus we cannot initialize the * library for lzip here. */ state->in_stream = 0; return (ARCHIVE_OK); } else state->in_stream = 1; /* Initialize compression library. */ if (self->code == ARCHIVE_FILTER_XZ) ret = lzma_stream_decoder(&(state->stream), LZMA_MEMLIMIT,/* memlimit */ LZMA_CONCATENATED); else ret = lzma_alone_decoder(&(state->stream), LZMA_MEMLIMIT);/* memlimit */ if (ret == LZMA_OK) return (ARCHIVE_OK); /* Library setup failed: Choose an error message and clean up. */ set_error(self, ret); free(state->out_block); free(state); self->data = NULL; return (ARCHIVE_FATAL); } static int lzip_init(struct archive_read_filter *self) { struct private_data *state; const unsigned char *h; lzma_filter filters[2]; unsigned char props[5]; ssize_t avail_in; uint32_t dicsize; int log2dic, ret; state = (struct private_data *)self->data; h = __archive_read_filter_ahead(self->upstream, 6, &avail_in); if (h == NULL) return (ARCHIVE_FATAL); /* Get a version number. */ state->lzip_ver = h[4]; /* * Setup lzma property. */ props[0] = 0x5d; /* Get dictionary size. */ log2dic = h[5] & 0x1f; if (log2dic < 12 || log2dic > 27) return (ARCHIVE_FATAL); dicsize = 1U << log2dic; if (log2dic > 12) dicsize -= (dicsize / 16) * (h[5] >> 5); archive_le32enc(props+1, dicsize); /* Consume lzip header. */ __archive_read_filter_consume(self->upstream, 6); state->member_in = 6; filters[0].id = LZMA_FILTER_LZMA1; filters[0].options = NULL; filters[1].id = LZMA_VLI_UNKNOWN; filters[1].options = NULL; ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props)); if (ret != LZMA_OK) { set_error(self, ret); return (ARCHIVE_FATAL); } ret = lzma_raw_decoder(&(state->stream), filters); -#if LZMA_VERSION < 50010000 free(filters[0].options); -#endif if (ret != LZMA_OK) { set_error(self, ret); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static int lzip_tail(struct archive_read_filter *self) { struct private_data *state; const unsigned char *f; ssize_t avail_in; int tail; state = (struct private_data *)self->data; if (state->lzip_ver == 0) tail = 12; else tail = 20; f = __archive_read_filter_ahead(self->upstream, tail, &avail_in); if (f == NULL && avail_in < 0) return (ARCHIVE_FATAL); if (f == NULL || avail_in < tail) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Remaining data is less bytes"); return (ARCHIVE_FAILED); } /* Check the crc32 value of the uncompressed data of the current * member */ if (state->crc32 != archive_le32dec(f)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: CRC32 error"); return (ARCHIVE_FAILED); } /* Check the uncompressed size of the current member */ if ((uint64_t)state->member_out != archive_le64dec(f + 4)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Uncompressed size error"); return (ARCHIVE_FAILED); } /* Check the total size of the current member */ if (state->lzip_ver == 1 && (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Member size error"); return (ARCHIVE_FAILED); } __archive_read_filter_consume(self->upstream, tail); /* If current lzip data consists of multi member, try decompressing * a next member. */ if (lzip_has_member(self->upstream) != 0) { state->in_stream = 0; state->crc32 = 0; state->member_out = 0; state->member_in = 0; state->eof = 0; } return (ARCHIVE_OK); } /* * Return the next block of decompressed data. */ static ssize_t xz_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; ssize_t avail_in; int ret; state = (struct private_data *)self->data; /* Empty our output buffer. */ state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Try to fill the output buffer. */ while (state->stream.avail_out > 0 && !state->eof) { if (!state->in_stream) { /* * Initialize liblzma for lzip */ ret = lzip_init(self); if (ret != ARCHIVE_OK) return (ret); state->in_stream = 1; } state->stream.next_in = __archive_read_filter_ahead(self->upstream, 1, &avail_in); if (state->stream.next_in == NULL && avail_in < 0) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "truncated input"); return (ARCHIVE_FATAL); } state->stream.avail_in = avail_in; /* Decompress as much as we can in one pass. */ ret = lzma_code(&(state->stream), (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN); switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ state->eof = 1; /* FALL THROUGH */ case LZMA_OK: /* Decompressor made some progress. */ __archive_read_filter_consume(self->upstream, avail_in - state->stream.avail_in); state->member_in += avail_in - state->stream.avail_in; break; default: set_error(self, ret); return (ARCHIVE_FATAL); } } decompressed = state->stream.next_out - state->out_block; state->total_out += decompressed; state->member_out += decompressed; if (decompressed == 0) *p = NULL; else { *p = state->out_block; if (self->code == ARCHIVE_FILTER_LZIP) { state->crc32 = lzma_crc32(state->out_block, decompressed, state->crc32); if (state->eof) { ret = lzip_tail(self); if (ret != ARCHIVE_OK) return (ret); } } } return (decompressed); } /* * Clean up the decompressor. */ static int xz_filter_close(struct archive_read_filter *self) { struct private_data *state; state = (struct private_data *)self->data; lzma_end(&(state->stream)); free(state->out_block); free(state); return (ARCHIVE_OK); } #else /* * * If we have no suitable library on this system, we can't actually do * the decompression. We can, however, still detect compressed * archives and emit a useful message. * */ static int lzma_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "lzma -d -qq"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_FILTER_LZMA; self->name = "lzma"; return (r); } static int xz_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "xz -d -qq"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_FILTER_XZ; self->name = "xz"; return (r); } static int lzip_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "lzip -d -q"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_FILTER_LZIP; self->name = "lzip"; return (r); } #endif /* HAVE_LZMA_H */ Index: vendor/libarchive/dist/libarchive/archive_read_support_format_7zip.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_7zip.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_7zip.c (revision 310622) @@ -1,3893 +1,3886 @@ /*- * Copyright (c) 2011 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #define _7ZIP_SIGNATURE "7z\xBC\xAF\x27\x1C" #define SFX_MIN_ADDR 0x27000 #define SFX_MAX_ADDR 0x60000 /* * Codec ID */ #define _7Z_COPY 0 #define _7Z_LZMA 0x030101 #define _7Z_LZMA2 0x21 #define _7Z_DEFLATE 0x040108 #define _7Z_BZ2 0x040202 #define _7Z_PPMD 0x030401 #define _7Z_DELTA 0x03 #define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */ #define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */ #define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */ #define _7Z_X86 0x03030103 #define _7Z_X86_BCJ2 0x0303011B #define _7Z_POWERPC 0x03030205 #define _7Z_IA64 0x03030401 #define _7Z_ARM 0x03030501 #define _7Z_ARMTHUMB 0x03030701 #define _7Z_SPARC 0x03030805 /* * 7-Zip header property IDs. */ #define kEnd 0x00 #define kHeader 0x01 #define kArchiveProperties 0x02 #define kAdditionalStreamsInfo 0x03 #define kMainStreamsInfo 0x04 #define kFilesInfo 0x05 #define kPackInfo 0x06 #define kUnPackInfo 0x07 #define kSubStreamsInfo 0x08 #define kSize 0x09 #define kCRC 0x0A #define kFolder 0x0B #define kCodersUnPackSize 0x0C #define kNumUnPackStream 0x0D #define kEmptyStream 0x0E #define kEmptyFile 0x0F #define kAnti 0x10 #define kName 0x11 #define kCTime 0x12 #define kATime 0x13 #define kMTime 0x14 #define kAttributes 0x15 #define kEncodedHeader 0x17 #define kDummy 0x19 struct _7z_digests { unsigned char *defineds; uint32_t *digests; }; struct _7z_folder { uint64_t numCoders; struct _7z_coder { unsigned long codec; uint64_t numInStreams; uint64_t numOutStreams; uint64_t propertiesSize; unsigned char *properties; } *coders; uint64_t numBindPairs; struct { uint64_t inIndex; uint64_t outIndex; } *bindPairs; uint64_t numPackedStreams; uint64_t *packedStreams; uint64_t numInStreams; uint64_t numOutStreams; uint64_t *unPackSize; unsigned char digest_defined; uint32_t digest; uint64_t numUnpackStreams; uint32_t packIndex; /* Unoperated bytes. */ uint64_t skipped_bytes; }; struct _7z_coders_info { uint64_t numFolders; struct _7z_folder *folders; uint64_t dataStreamIndex; }; struct _7z_pack_info { uint64_t pos; uint64_t numPackStreams; uint64_t *sizes; struct _7z_digests digest; /* Calculated from pos and numPackStreams. */ uint64_t *positions; }; struct _7z_substream_info { size_t unpack_streams; uint64_t *unpackSizes; unsigned char *digestsDefined; uint32_t *digests; }; struct _7z_stream_info { struct _7z_pack_info pi; struct _7z_coders_info ci; struct _7z_substream_info ss; }; struct _7z_header_info { uint64_t dataIndex; unsigned char *emptyStreamBools; unsigned char *emptyFileBools; unsigned char *antiBools; unsigned char *attrBools; }; struct _7zip_entry { size_t name_len; unsigned char *utf16name; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) const wchar_t *wname; #endif uint32_t folderIndex; uint32_t ssIndex; unsigned flg; #define MTIME_IS_SET (1<<0) #define ATIME_IS_SET (1<<1) #define CTIME_IS_SET (1<<2) #define CRC32_IS_SET (1<<3) #define HAS_STREAM (1<<4) time_t mtime; time_t atime; time_t ctime; long mtime_ns; long atime_ns; long ctime_ns; uint32_t mode; uint32_t attr; }; struct _7zip { /* Structural information about the archive. */ struct _7z_stream_info si; int header_is_being_read; int header_is_encoded; uint64_t header_bytes_remaining; unsigned long header_crc32; /* Header offset to check that reading points of the file contents * will not exceed the header. */ uint64_t header_offset; /* Base offset of the archive file for a seek in case reading SFX. */ uint64_t seek_base; /* List of entries */ size_t entries_remaining; uint64_t numFiles; struct _7zip_entry *entries; struct _7zip_entry *entry; unsigned char *entry_names; /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; uint64_t entry_bytes_remaining; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; /* Flags to mark progress of decompression. */ char end_of_entry; /* Uncompressed buffer control. */ #define UBUFF_SIZE (64 * 1024) unsigned char *uncompressed_buffer; unsigned char *uncompressed_buffer_pointer; size_t uncompressed_buffer_size; size_t uncompressed_buffer_bytes_remaining; /* Offset of the compressed data. */ int64_t stream_offset; /* * Decompressing control data. */ unsigned folder_index; uint64_t folder_outbytes_remaining; unsigned pack_stream_index; unsigned pack_stream_remaining; uint64_t pack_stream_inbytes_remaining; size_t pack_stream_bytes_unconsumed; /* The codec information of a folder. */ unsigned long codec; unsigned long codec2; /* * Decompressor controllers. */ - /* Decording LZMA1 and LZMA2 data. */ + /* Decoding LZMA1 and LZMA2 data. */ #ifdef HAVE_LZMA_H lzma_stream lzstream; int lzstream_valid; #endif - /* Decording bzip2 data. */ + /* Decoding bzip2 data. */ #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream bzstream; int bzstream_valid; #endif - /* Decording deflate data. */ + /* Decoding deflate data. */ #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; #endif - /* Decording PPMd data. */ + /* Decoding PPMd data. */ int ppmd7_stat; CPpmd7 ppmd7_context; CPpmd7z_RangeDec range_dec; IByteIn bytein; struct { const unsigned char *next_in; int64_t avail_in; int64_t total_in; unsigned char *next_out; int64_t avail_out; int64_t total_out; int overconsumed; } ppstream; int ppmd7_valid; /* Decoding BCJ and BCJ2 data. */ uint32_t bcj_state; size_t odd_bcj_size; unsigned char odd_bcj[4]; /* Decoding BCJ data. */ size_t bcj_prevPosT; uint32_t bcj_prevMask; uint32_t bcj_ip; /* Decoding BCJ2 data. */ size_t main_stream_bytes_remaining; unsigned char *sub_stream_buff[3]; size_t sub_stream_size[3]; size_t sub_stream_bytes_remaining[3]; unsigned char *tmp_stream_buff; size_t tmp_stream_buff_size; size_t tmp_stream_bytes_avail; size_t tmp_stream_bytes_remaining; #ifdef _LZMA_PROB32 #define CProb uint32_t #else #define CProb uint16_t #endif CProb bcj2_p[256 + 2]; uint8_t bcj2_prevByte; uint32_t bcj2_range; uint32_t bcj2_code; uint64_t bcj2_outPos; /* Filename character-set conversion data. */ struct archive_string_conv *sconv; char format_name[64]; /* Custom value that is non-zero if this archive contains encrypted entries. */ int has_encrypted_entries; }; /* Maximum entry size. This limitation prevents reading intentional * corrupted 7-zip files on assuming there are not so many entries in * the files. */ #define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000) static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *); static int archive_read_support_format_7zip_capabilities(struct archive_read *a); static int archive_read_format_7zip_bid(struct archive_read *, int); static int archive_read_format_7zip_cleanup(struct archive_read *); static int archive_read_format_7zip_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_7zip_read_data_skip(struct archive_read *); static int archive_read_format_7zip_read_header(struct archive_read *, struct archive_entry *); static int check_7zip_header_in_sfx(const char *); static unsigned long decode_codec_id(const unsigned char *, size_t); static int decode_encoded_header_info(struct archive_read *, struct _7z_stream_info *); static int decompress(struct archive_read *, struct _7zip *, void *, size_t *, const void *, size_t *); static ssize_t extract_pack_stream(struct archive_read *, size_t); static void fileTimeToUtc(uint64_t, time_t *, long *); static uint64_t folder_uncompressed_size(struct _7z_folder *); static void free_CodersInfo(struct _7z_coders_info *); static void free_Digest(struct _7z_digests *); static void free_Folder(struct _7z_folder *); static void free_Header(struct _7z_header_info *); static void free_PackInfo(struct _7z_pack_info *); static void free_StreamsInfo(struct _7z_stream_info *); static void free_SubStreamsInfo(struct _7z_substream_info *); static int free_decompression(struct archive_read *, struct _7zip *); static ssize_t get_uncompressed_data(struct archive_read *, const void **, size_t, size_t); static const unsigned char * header_bytes(struct archive_read *, size_t); static int init_decompression(struct archive_read *, struct _7zip *, const struct _7z_coder *, const struct _7z_coder *); static int parse_7zip_uint64(struct archive_read *, uint64_t *); static int read_Bools(struct archive_read *, unsigned char *, size_t); static int read_CodersInfo(struct archive_read *, struct _7z_coders_info *); static int read_Digests(struct archive_read *, struct _7z_digests *, size_t); static int read_Folder(struct archive_read *, struct _7z_folder *); static int read_Header(struct archive_read *, struct _7z_header_info *, int); static int read_PackInfo(struct archive_read *, struct _7z_pack_info *); static int read_StreamsInfo(struct archive_read *, struct _7z_stream_info *); static int read_SubStreamsInfo(struct archive_read *, struct _7z_substream_info *, struct _7z_folder *, size_t); static int read_Times(struct archive_read *, struct _7z_header_info *, int); static void read_consume(struct archive_read *); static ssize_t read_stream(struct archive_read *, const void **, size_t, size_t); static int seek_pack(struct archive_read *); static int64_t skip_stream(struct archive_read *, size_t); static int skip_sfx(struct archive_read *, ssize_t); static int slurp_central_directory(struct archive_read *, struct _7zip *, struct _7z_header_info *); static int setup_decode_folder(struct archive_read *, struct _7z_folder *, int); static void x86_Init(struct _7zip *); static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); int archive_read_support_format_7zip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct _7zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_7zip"); zip = calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate 7zip data"); return (ARCHIVE_FATAL); } /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; r = __archive_read_register_format(a, zip, "7zip", archive_read_format_7zip_bid, NULL, archive_read_format_7zip_read_header, archive_read_format_7zip_read_data, archive_read_format_7zip_read_data_skip, NULL, archive_read_format_7zip_cleanup, archive_read_support_format_7zip_capabilities, archive_read_format_7zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } static int archive_read_support_format_7zip_capabilities(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct _7zip * zip = (struct _7zip *)_a->format->data; if (zip) { return zip->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { const char *p; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (0); /* If first six bytes are the 7-Zip signature, * return the bid right now. */ if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) return (48); /* * It may a 7-Zip SFX archive file. If first two bytes are * 'M' and 'Z' available on Windows or first four bytes are * "\x7F\x45LF" available on posix like system, seek the 7-Zip * signature. Although we will perform a seek when reading * a header, what we do not use __archive_read_seek() here is * due to a bidding performance. */ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { ssize_t offset = SFX_MIN_ADDR; ssize_t window = 4096; ssize_t bytes_avail; while (offset + window <= (SFX_MAX_ADDR)) { const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) return (0); continue; } p = buff + offset; while (p + 32 < buff + bytes_avail) { int step = check_7zip_header_in_sfx(p); if (step == 0) return (48); p += step; } offset = p - buff; } } return (0); } static int check_7zip_header_in_sfx(const char *p) { switch ((unsigned char)p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) return (6); /* * Test the CRC because its extraction code has 7-Zip * Magic Code, so we should do this in order not to * make a mis-detection. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) return (6); /* Hit the header! */ return (0); case 0x37: return (5); case 0x7A: return (4); case 0xBC: return (3); case 0xAF: return (2); case 0x27: return (1); default: return (6); } } static int skip_sfx(struct archive_read *a, ssize_t bytes_avail) { const void *h; const char *p, *q; size_t skip, offset; ssize_t bytes, window; /* * If bytes_avail > SFX_MIN_ADDR we do not have to call * __archive_read_seek() at this time since we have * alredy had enough data. */ if (bytes_avail > SFX_MIN_ADDR) __archive_read_consume(a, SFX_MIN_ADDR); else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0) return (ARCHIVE_FATAL); offset = 0; window = 1; while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) goto fatal; continue; } if (bytes < 6) { /* This case might happen when window == 1. */ window = 4096; continue; } p = (const char *)h; q = p + bytes; /* * Scan ahead until we find something that looks * like the 7-Zip header. */ while (p + 32 < q) { int step = check_7zip_header_in_sfx(p); if (step == 0) { struct _7zip *zip = (struct _7zip *)a->format->data; skip = p - (const char *)h; __archive_read_consume(a, skip); zip->seek_base = SFX_MIN_ADDR + offset + skip; return (ARCHIVE_OK); } p += step; } skip = p - (const char *)h; __archive_read_consume(a, skip); offset += skip; if (window == 1) window = 4096; } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out 7-Zip header"); return (ARCHIVE_FATAL); } static int archive_read_format_7zip_read_header(struct archive_read *a, struct archive_entry *entry) { struct _7zip *zip = (struct _7zip *)a->format->data; struct _7zip_entry *zip_entry; int r, ret = ARCHIVE_OK; struct _7z_folder *folder = 0; uint64_t fidx = 0; /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } a->archive.archive_format = ARCHIVE_FORMAT_7ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "7-Zip"; if (zip->entries == NULL) { struct _7z_header_info header; memset(&header, 0, sizeof(header)); r = slurp_central_directory(a, zip, &header); free_Header(&header); if (r != ARCHIVE_OK) return (r); zip->entries_remaining = (size_t)zip->numFiles; zip->entry = zip->entries; } else { ++zip->entry; } zip_entry = zip->entry; if (zip->entries_remaining <= 0 || zip_entry == NULL) return ARCHIVE_EOF; --zip->entries_remaining; zip->entry_offset = 0; zip->end_of_entry = 0; zip->entry_crc32 = crc32(0, NULL, 0); /* Setup a string conversion for a filename. */ if (zip->sconv == NULL) { zip->sconv = archive_string_conversion_from_charset( &a->archive, "UTF-16LE", 1); if (zip->sconv == NULL) return (ARCHIVE_FATAL); } /* Figure out if the entry is encrypted by looking at the folder that is associated to the current 7zip entry. If the folder has a coder with a _7Z_CRYPTO codec then the folder is encrypted. Hence the entry must also be encrypted. */ if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) { folder = &(zip->si.ci.folders[zip_entry->folderIndex]); for (fidx=0; folder && fidxnumCoders; fidx++) { switch(folder->coders[fidx].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: { archive_entry_set_is_data_encrypted(entry, 1); zip->has_encrypted_entries = 1; break; } } } } /* Now that we've checked for encryption, if there were still no * encrypted entries found we can say for sure that there are none. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if (archive_entry_copy_pathname_l(entry, (const char *)zip_entry->utf16name, zip_entry->name_len, zip->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(zip->sconv)); ret = ARCHIVE_WARN; } /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); if (zip_entry->flg & MTIME_IS_SET) archive_entry_set_mtime(entry, zip_entry->mtime, zip_entry->mtime_ns); if (zip_entry->flg & CTIME_IS_SET) archive_entry_set_ctime(entry, zip_entry->ctime, zip_entry->ctime_ns); if (zip_entry->flg & ATIME_IS_SET) archive_entry_set_atime(entry, zip_entry->atime, zip_entry->atime_ns); if (zip_entry->ssIndex != (uint32_t)-1) { zip->entry_bytes_remaining = zip->si.ss.unpackSizes[zip_entry->ssIndex]; archive_entry_set_size(entry, zip->entry_bytes_remaining); } else { zip->entry_bytes_remaining = 0; archive_entry_set_size(entry, 0); } /* If there's no body, force read_data() to return EOF immediately. */ if (zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) { unsigned char *symname = NULL; size_t symsize = 0; /* * Symbolic-name is recorded as its contents. We have to * read the contents at this time. */ while (zip->entry_bytes_remaining > 0) { const void *buff; unsigned char *mem; size_t size; int64_t offset; r = archive_read_format_7zip_read_data(a, &buff, &size, &offset); if (r < ARCHIVE_WARN) { free(symname); return (r); } mem = realloc(symname, symsize + size + 1); if (mem == NULL) { free(symname); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Symname"); return (ARCHIVE_FATAL); } symname = mem; memcpy(symname+symsize, buff, size); symsize += size; } if (symsize == 0) { /* If there is no synname, handle it as a regular * file. */ zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFREG; archive_entry_set_mode(entry, zip_entry->mode); } else { symname[symsize] = '\0'; archive_entry_copy_symlink(entry, (const char *)symname); } free(symname); archive_entry_set_size(entry, 0); } /* Set up a more descriptive format name. */ sprintf(zip->format_name, "7-Zip"); a->archive.archive_format_name = zip->format_name; return (ret); } static int archive_read_format_7zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct _7zip *zip; ssize_t bytes; int ret = ARCHIVE_OK; zip = (struct _7zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if (zip->pack_stream_bytes_unconsumed) read_consume(a); *offset = zip->entry_offset; *size = 0; *buff = NULL; /* * If we hit end-of-entry last time, clean up and return * ARCHIVE_EOF this time. */ if (zip->end_of_entry) return (ARCHIVE_EOF); bytes = read_stream(a, buff, (size_t)zip->entry_bytes_remaining, 0); if (bytes < 0) return ((int)bytes); if (bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } zip->entry_bytes_remaining -= bytes; if (zip->entry_bytes_remaining == 0) zip->end_of_entry = 1; /* Update checksum */ if ((zip->entry->flg & CRC32_IS_SET) && bytes) zip->entry_crc32 = crc32(zip->entry_crc32, *buff, (unsigned)bytes); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { /* Check computed CRC against file contents. */ if ((zip->entry->flg & CRC32_IS_SET) && zip->si.ss.digests[zip->entry->ssIndex] != zip->entry_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "7-Zip bad CRC: 0x%lx should be 0x%lx", (unsigned long)zip->entry_crc32, (unsigned long)zip->si.ss.digests[ zip->entry->ssIndex]); ret = ARCHIVE_WARN; } } *size = bytes; *offset = zip->entry_offset; zip->entry_offset += bytes; return (ret); } static int archive_read_format_7zip_read_data_skip(struct archive_read *a) { struct _7zip *zip; int64_t bytes_skipped; zip = (struct _7zip *)(a->format->data); if (zip->pack_stream_bytes_unconsumed) read_consume(a); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); zip->entry_bytes_remaining = 0; /* This entry is finished and done. */ zip->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_7zip_cleanup(struct archive_read *a) { struct _7zip *zip; zip = (struct _7zip *)(a->format->data); free_StreamsInfo(&(zip->si)); free(zip->entries); free(zip->entry_names); free_decompression(a, zip); free(zip->uncompressed_buffer); free(zip->sub_stream_buff[0]); free(zip->sub_stream_buff[1]); free(zip->sub_stream_buff[2]); free(zip->tmp_stream_buff); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } static void read_consume(struct archive_read *a) { struct _7zip *zip = (struct _7zip *)a->format->data; if (zip->pack_stream_bytes_unconsumed) { __archive_read_consume(a, zip->pack_stream_bytes_unconsumed); zip->stream_offset += zip->pack_stream_bytes_unconsumed; zip->pack_stream_bytes_unconsumed = 0; } } #ifdef HAVE_LZMA_H /* * Set an error code and choose an error message for liblzma. */ static void set_error(struct archive_read *a, int ret) { switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ case LZMA_OK: /* Decompressor made some progress. */ break; case LZMA_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Lzma library error: Cannot allocate memory"); break; case LZMA_MEMLIMIT_ERROR: archive_set_error(&a->archive, ENOMEM, "Lzma library error: Out of memory"); break; case LZMA_FORMAT_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: format not recognized"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Invalid options"); break; case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Corrupted input data"); break; case LZMA_BUF_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: No progress is possible"); break; default: /* Return an error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed: Unknown error"); break; } } #endif static unsigned long decode_codec_id(const unsigned char *codecId, size_t id_size) { unsigned i; unsigned long id = 0; for (i = 0; i < id_size; i++) { id <<= 8; id += codecId[i]; } return (id); } static void * ppmd_alloc(void *p, size_t size) { (void)p; return malloc(size); } static void ppmd_free(void *p, void *address) { (void)p; free(address); } static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; struct _7zip *zip = (struct _7zip *)(a->format->data); Byte b; if (zip->ppstream.avail_in == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); zip->ppstream.overconsumed = 1; return (0); } b = *zip->ppstream.next_in++; zip->ppstream.avail_in--; zip->ppstream.total_in++; return (b); } static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free }; static int init_decompression(struct archive_read *a, struct _7zip *zip, const struct _7z_coder *coder1, const struct _7z_coder *coder2) { int r; zip->codec = coder1->codec; zip->codec2 = -1; switch (zip->codec) { case _7Z_COPY: case _7Z_BZ2: case _7Z_DEFLATE: case _7Z_PPMD: if (coder2 != NULL) { if (coder2->codec != _7Z_X86 && coder2->codec != _7Z_X86_BCJ2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported filter %lx for %lx", coder2->codec, coder1->codec); return (ARCHIVE_FAILED); } zip->codec2 = coder2->codec; zip->bcj_state = 0; if (coder2->codec == _7Z_X86) x86_Init(zip); } break; default: break; } switch (zip->codec) { case _7Z_COPY: break; case _7Z_LZMA: case _7Z_LZMA2: #ifdef HAVE_LZMA_H #if LZMA_VERSION_MAJOR >= 5 /* Effectively disable the limiter. */ #define LZMA_MEMLIMIT UINT64_MAX #else /* NOTE: This needs to check memory size which running system has. */ #define LZMA_MEMLIMIT (1U << 30) #endif { lzma_options_delta delta_opt; - lzma_filter filters[LZMA_FILTERS_MAX]; -#if LZMA_VERSION < 50010000 - lzma_filter *ff; -#endif + lzma_filter filters[LZMA_FILTERS_MAX], *ff; int fi = 0; if (zip->lzstream_valid) { lzma_end(&(zip->lzstream)); zip->lzstream_valid = 0; } /* * NOTE: liblzma incompletely handle the BCJ+LZMA compressed * data made by 7-Zip because 7-Zip does not add End-Of- * Payload Marker(EOPM) at the end of LZMA compressed data, * and so liblzma cannot know the end of the compressed data * without EOPM. So consequently liblzma will not return last * three or four bytes of uncompressed data because * LZMA_FILTER_X86 filter does not handle input data if its * data size is less than five bytes. If liblzma detect EOPM * or know the uncompressed data size, liblzma will flush out * the remaining that three or four bytes of uncompressed * data. That is why we have to use our converting program * for BCJ+LZMA. If we were able to tell the uncompressed * size to liblzma when using lzma_raw_decoder() liblzma * could correctly deal with BCJ+LZMA. But unfortunately * there is no way to do that. * Discussion about this can be found at XZ Utils forum. */ if (coder2 != NULL) { zip->codec2 = coder2->codec; filters[fi].options = NULL; switch (zip->codec2) { case _7Z_X86: if (zip->codec == _7Z_LZMA2) { filters[fi].id = LZMA_FILTER_X86; fi++; } else /* Use our filter. */ x86_Init(zip); break; case _7Z_X86_BCJ2: /* Use our filter. */ zip->bcj_state = 0; break; case _7Z_DELTA: filters[fi].id = LZMA_FILTER_DELTA; memset(&delta_opt, 0, sizeof(delta_opt)); delta_opt.type = LZMA_DELTA_TYPE_BYTE; delta_opt.dist = 1; filters[fi].options = &delta_opt; fi++; break; /* Following filters have not been tested yet. */ case _7Z_POWERPC: filters[fi].id = LZMA_FILTER_POWERPC; fi++; break; case _7Z_IA64: filters[fi].id = LZMA_FILTER_IA64; fi++; break; case _7Z_ARM: filters[fi].id = LZMA_FILTER_ARM; fi++; break; case _7Z_ARMTHUMB: filters[fi].id = LZMA_FILTER_ARMTHUMB; fi++; break; case _7Z_SPARC: filters[fi].id = LZMA_FILTER_SPARC; fi++; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec2); return (ARCHIVE_FAILED); } } if (zip->codec == _7Z_LZMA2) filters[fi].id = LZMA_FILTER_LZMA2; else filters[fi].id = LZMA_FILTER_LZMA1; filters[fi].options = NULL; -#if LZMA_VERSION < 50010000 ff = &filters[fi]; -#endif r = lzma_properties_decode(&filters[fi], NULL, coder1->properties, (size_t)coder1->propertiesSize); if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); } fi++; filters[fi].id = LZMA_VLI_UNKNOWN; filters[fi].options = NULL; r = lzma_raw_decoder(&(zip->lzstream), filters); -#if LZMA_VERSION < 50010000 free(ff->options); -#endif if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); } zip->lzstream_valid = 1; zip->lzstream.total_in = 0; zip->lzstream.total_out = 0; break; } #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LZMA codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_BZ2: #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (zip->bzstream_valid) { BZ2_bzDecompressEnd(&(zip->bzstream)); zip->bzstream_valid = 0; } r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0); if (r == BZ_MEM_ERROR) r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1); if (r != BZ_OK) { int err = ARCHIVE_ERRNO_MISC; const char *detail = NULL; switch (r) { case BZ_PARAM_ERROR: detail = "invalid setup parameter"; break; case BZ_MEM_ERROR: err = ENOMEM; detail = "out of memory"; break; case BZ_CONFIG_ERROR: detail = "mis-compiled library"; break; } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", detail != NULL ? detail : "??"); zip->bzstream_valid = 0; return (ARCHIVE_FAILED); } zip->bzstream_valid = 1; zip->bzstream.total_in_lo32 = 0; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_out_lo32 = 0; zip->bzstream.total_out_hi32 = 0; break; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "BZ2 codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_DEFLATE: #ifdef HAVE_ZLIB_H if (zip->stream_valid) r = inflateReset(&(zip->stream)); else r = inflateInit2(&(zip->stream), -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't initialize zlib stream."); return (ARCHIVE_FAILED); } zip->stream_valid = 1; zip->stream.total_in = 0; zip->stream.total_out = 0; break; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "DEFLATE codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_PPMD: { unsigned order; uint32_t msize; if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( &zip->ppmd7_context, &g_szalloc); zip->ppmd7_valid = 0; } if (coder1->propertiesSize < 5) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed PPMd parameter"); return (ARCHIVE_FAILED); } order = coder1->properties[0]; msize = archive_le32dec(&(coder1->properties[1])); if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER || msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed PPMd parameter"); return (ARCHIVE_FAILED); } __archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context); r = __archive_ppmd7_functions.Ppmd7_Alloc( &zip->ppmd7_context, msize, &g_szalloc); if (r == 0) { archive_set_error(&a->archive, ENOMEM, "Coludn't allocate memory for PPMd"); return (ARCHIVE_FATAL); } __archive_ppmd7_functions.Ppmd7_Init( &zip->ppmd7_context, order); __archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable( &zip->range_dec); zip->ppmd7_valid = 1; zip->ppmd7_stat = 0; zip->ppstream.overconsumed = 0; zip->ppstream.total_in = 0; zip->ppstream.total_out = 0; break; } case _7Z_X86: case _7Z_X86_BCJ2: case _7Z_POWERPC: case _7Z_IA64: case _7Z_ARM: case _7Z_ARMTHUMB: case _7Z_SPARC: case _7Z_DELTA: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: if (a->entry) { archive_entry_set_is_metadata_encrypted(a->entry, 1); archive_entry_set_is_data_encrypted(a->entry, 1); zip->has_encrypted_entries = 1; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Crypto codec not supported yet (ID: 0x%lX)", zip->codec); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int decompress(struct archive_read *a, struct _7zip *zip, void *buff, size_t *outbytes, const void *b, size_t *used) { const uint8_t *t_next_in; uint8_t *t_next_out; size_t o_avail_in, o_avail_out; size_t t_avail_in, t_avail_out; uint8_t *bcj2_next_out; size_t bcj2_avail_out; int r, ret = ARCHIVE_OK; t_avail_in = o_avail_in = *used; t_avail_out = o_avail_out = *outbytes; t_next_in = b; t_next_out = buff; if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { int i; /* Do not copy out the BCJ remaining bytes when the output * buffer size is less than five bytes. */ if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) { *used = 0; *outbytes = 0; return (ret); } for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) { *t_next_out++ = zip->odd_bcj[i]; t_avail_out--; zip->odd_bcj_size--; } if (o_avail_in == 0 || t_avail_out == 0) { *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0) ret = ARCHIVE_EOF; return (ret); } } bcj2_next_out = t_next_out; bcj2_avail_out = t_avail_out; if (zip->codec2 == _7Z_X86_BCJ2) { /* * Decord a remaining decompressed main stream for BCJ2. */ if (zip->tmp_stream_bytes_remaining) { ssize_t bytes; size_t remaining = zip->tmp_stream_bytes_remaining; bytes = Bcj2_Decode(zip, t_next_out, t_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= remaining - zip->tmp_stream_bytes_remaining; t_avail_out -= bytes; if (o_avail_in == 0 || t_avail_out == 0) { *used = 0; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0 && zip->tmp_stream_bytes_remaining) ret = ARCHIVE_EOF; return (ret); } t_next_out += bytes; bcj2_next_out = t_next_out; bcj2_avail_out = t_avail_out; } t_next_out = zip->tmp_stream_buff; t_avail_out = zip->tmp_stream_buff_size; } switch (zip->codec) { case _7Z_COPY: { size_t bytes = (t_avail_in > t_avail_out)?t_avail_out:t_avail_in; memcpy(t_next_out, t_next_in, bytes); t_avail_in -= bytes; t_avail_out -= bytes; if (o_avail_in == 0) ret = ARCHIVE_EOF; break; } #ifdef HAVE_LZMA_H case _7Z_LZMA: case _7Z_LZMA2: zip->lzstream.next_in = t_next_in; zip->lzstream.avail_in = t_avail_in; zip->lzstream.next_out = t_next_out; zip->lzstream.avail_out = t_avail_out; r = lzma_code(&(zip->lzstream), LZMA_RUN); switch (r) { case LZMA_STREAM_END: /* Found end of stream. */ lzma_end(&(zip->lzstream)); zip->lzstream_valid = 0; ret = ARCHIVE_EOF; break; case LZMA_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression failed(%d)", r); return (ARCHIVE_FAILED); } t_avail_in = zip->lzstream.avail_in; t_avail_out = zip->lzstream.avail_out; break; #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case _7Z_BZ2: zip->bzstream.next_in = (char *)(uintptr_t)t_next_in; zip->bzstream.avail_in = t_avail_in; zip->bzstream.next_out = (char *)(uintptr_t)t_next_out; zip->bzstream.avail_out = t_avail_out; r = BZ2_bzDecompress(&(zip->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ switch (BZ2_bzDecompressEnd(&(zip->bzstream))) { case BZ_OK: break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FAILED); } zip->bzstream_valid = 0; ret = ARCHIVE_EOF; break; case BZ_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FAILED); } t_avail_in = zip->bzstream.avail_in; t_avail_out = zip->bzstream.avail_out; break; #endif #ifdef HAVE_ZLIB_H case _7Z_DEFLATE: zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in; zip->stream.avail_in = (uInt)t_avail_in; zip->stream.next_out = t_next_out; zip->stream.avail_out = (uInt)t_avail_out; r = inflate(&(zip->stream), 0); switch (r) { case Z_STREAM_END: /* Found end of stream. */ ret = ARCHIVE_EOF; break; case Z_OK: /* Decompressor made some progress.*/ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File decompression failed (%d)", r); return (ARCHIVE_FAILED); } t_avail_in = zip->stream.avail_in; t_avail_out = zip->stream.avail_out; break; #endif case _7Z_PPMD: { uint64_t flush_bytes; if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 || t_avail_out <= 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression internal error"); return (ARCHIVE_FAILED); } zip->ppstream.next_in = t_next_in; zip->ppstream.avail_in = t_avail_in; zip->ppstream.next_out = t_next_out; zip->ppstream.avail_out = t_avail_out; if (zip->ppmd7_stat == 0) { zip->bytein.a = a; zip->bytein.Read = &ppmd_read; zip->range_dec.Stream = &zip->bytein; r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init( &(zip->range_dec)); if (r == 0) { zip->ppmd7_stat = -1; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize PPMd range decorder"); return (ARCHIVE_FAILED); } if (zip->ppstream.overconsumed) { zip->ppmd7_stat = -1; return (ARCHIVE_FAILED); } zip->ppmd7_stat = 1; } if (t_avail_in == 0) /* XXX Flush out remaining decoded data XXX */ flush_bytes = zip->folder_outbytes_remaining; else flush_bytes = 0; do { int sym; sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &(zip->ppmd7_context), &(zip->range_dec.p)); if (sym < 0) { zip->ppmd7_stat = -1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to decode PPMd"); return (ARCHIVE_FAILED); } if (zip->ppstream.overconsumed) { zip->ppmd7_stat = -1; return (ARCHIVE_FAILED); } *zip->ppstream.next_out++ = (unsigned char)sym; zip->ppstream.avail_out--; zip->ppstream.total_out++; if (flush_bytes) flush_bytes--; } while (zip->ppstream.avail_out && (zip->ppstream.avail_in || flush_bytes)); t_avail_in = (size_t)zip->ppstream.avail_in; t_avail_out = (size_t)zip->ppstream.avail_out; break; } default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression internal error"); return (ARCHIVE_FAILED); } if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF) return (ret); *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; /* * Decord BCJ. */ if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { size_t l = x86_Convert(zip, buff, *outbytes); zip->odd_bcj_size = *outbytes - l; if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && o_avail_in && ret != ARCHIVE_EOF) { memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, zip->odd_bcj_size); *outbytes = l; } else zip->odd_bcj_size = 0; } /* * Decord BCJ2 with a decompressed main stream. */ if (zip->codec2 == _7Z_X86_BCJ2) { ssize_t bytes; zip->tmp_stream_bytes_avail = zip->tmp_stream_buff_size - t_avail_out; if (zip->tmp_stream_bytes_avail > zip->main_stream_bytes_remaining) zip->tmp_stream_bytes_avail = zip->main_stream_bytes_remaining; zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail; bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= zip->tmp_stream_bytes_avail - zip->tmp_stream_bytes_remaining; bcj2_avail_out -= bytes; *outbytes = o_avail_out - bcj2_avail_out; } return (ret); } static int free_decompression(struct archive_read *a, struct _7zip *zip) { int r = ARCHIVE_OK; #if !defined(HAVE_ZLIB_H) &&\ !(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)) (void)a;/* UNUSED */ #endif #ifdef HAVE_LZMA_H if (zip->lzstream_valid) lzma_end(&(zip->lzstream)); #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (zip->bzstream_valid) { if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up bzip2 decompressor"); r = ARCHIVE_FATAL; } zip->bzstream_valid = 0; } #endif #ifdef HAVE_ZLIB_H if (zip->stream_valid) { if (inflateEnd(&(zip->stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } zip->stream_valid = 0; } #endif if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( &zip->ppmd7_context, &g_szalloc); zip->ppmd7_valid = 0; } return (r); } static int parse_7zip_uint64(struct archive_read *a, uint64_t *val) { const unsigned char *p; unsigned char avail, mask; int i; if ((p = header_bytes(a, 1)) == NULL) return (-1); avail = *p; mask = 0x80; *val = 0; for (i = 0; i < 8; i++) { if (avail & mask) { if ((p = header_bytes(a, 1)) == NULL) return (-1); *val |= ((uint64_t)*p) << (8 * i); mask >>= 1; continue; } *val += ((uint64_t)(avail & (mask -1))) << (8 * i); break; } return (0); } static int read_Bools(struct archive_read *a, unsigned char *data, size_t num) { const unsigned char *p; unsigned i, mask = 0, avail = 0; for (i = 0; i < num; i++) { if (mask == 0) { if ((p = header_bytes(a, 1)) == NULL) return (-1); avail = *p; mask = 0x80; } data[i] = (avail & mask)?1:0; mask >>= 1; } return (0); } static void free_Digest(struct _7z_digests *d) { free(d->defineds); free(d->digests); } static int read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num) { const unsigned char *p; unsigned i; if (num == 0) return (-1); memset(d, 0, sizeof(*d)); d->defineds = malloc(num); if (d->defineds == NULL) return (-1); /* * Read Bools. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == 0) { if (read_Bools(a, d->defineds, num) < 0) return (-1); } else /* All are defined */ memset(d->defineds, 1, num); d->digests = calloc(num, sizeof(*d->digests)); if (d->digests == NULL) return (-1); for (i = 0; i < num; i++) { if (d->defineds[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); d->digests[i] = archive_le32dec(p); } } return (0); } static void free_PackInfo(struct _7z_pack_info *pi) { free(pi->sizes); free(pi->positions); free_Digest(&(pi->digest)); } static int read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) { const unsigned char *p; unsigned i; memset(pi, 0, sizeof(*pi)); /* * Read PackPos. */ if (parse_7zip_uint64(a, &(pi->pos)) < 0) return (-1); /* * Read NumPackStreams. */ if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0) return (-1); if (pi->numPackStreams == 0) return (-1); if (UMAX_ENTRY < pi->numPackStreams) return (-1); /* * Read PackSizes[num] */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kEnd) /* PackSizes[num] are not present. */ return (0); if (*p != kSize) return (-1); pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); if (pi->sizes == NULL || pi->positions == NULL) return (-1); for (i = 0; i < pi->numPackStreams; i++) { if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0) return (-1); } /* * Read PackStreamDigests[num] */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kEnd) { /* PackStreamDigests[num] are not present. */ pi->digest.defineds = calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds)); pi->digest.digests = calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests)); if (pi->digest.defineds == NULL || pi->digest.digests == NULL) return (-1); return (0); } if (*p != kSize) return (-1); if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0) return (-1); /* * Must be marked by kEnd. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p != kEnd) return (-1); return (0); } static void free_Folder(struct _7z_folder *f) { unsigned i; if (f->coders) { for (i = 0; i< f->numCoders; i++) { free(f->coders[i].properties); } free(f->coders); } free(f->bindPairs); free(f->packedStreams); free(f->unPackSize); } static int read_Folder(struct archive_read *a, struct _7z_folder *f) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; uint64_t numInStreamsTotal = 0; uint64_t numOutStreamsTotal = 0; unsigned i; memset(f, 0, sizeof(*f)); /* * Read NumCoders. */ if (parse_7zip_uint64(a, &(f->numCoders)) < 0) return (-1); if (f->numCoders > 4) /* Too many coders. */ return (-1); f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders)); if (f->coders == NULL) return (-1); for (i = 0; i< f->numCoders; i++) { size_t codec_size; int simple, attr; if ((p = header_bytes(a, 1)) == NULL) return (-1); /* * 0:3 CodecIdSize * 4: 0 - IsSimple * 1 - Is not Simple * 5: 0 - No Attributes * 1 - There are Attributes; * 7: Must be zero. */ codec_size = *p & 0xf; simple = (*p & 0x10)?0:1; attr = *p & 0x20; if (*p & 0x80) return (-1);/* Not supported. */ /* * Read Decompression Method IDs. */ if ((p = header_bytes(a, codec_size)) == NULL) return (-1); f->coders[i].codec = decode_codec_id(p, codec_size); if (simple) { f->coders[i].numInStreams = 1; f->coders[i].numOutStreams = 1; } else { if (parse_7zip_uint64( a, &(f->coders[i].numInStreams)) < 0) return (-1); if (UMAX_ENTRY < f->coders[i].numInStreams) return (-1); if (parse_7zip_uint64( a, &(f->coders[i].numOutStreams)) < 0) return (-1); if (UMAX_ENTRY < f->coders[i].numOutStreams) return (-1); } if (attr) { if (parse_7zip_uint64( a, &(f->coders[i].propertiesSize)) < 0) return (-1); if ((p = header_bytes( a, (size_t)f->coders[i].propertiesSize)) == NULL) return (-1); f->coders[i].properties = malloc((size_t)f->coders[i].propertiesSize); if (f->coders[i].properties == NULL) return (-1); memcpy(f->coders[i].properties, p, (size_t)f->coders[i].propertiesSize); } numInStreamsTotal += f->coders[i].numInStreams; numOutStreamsTotal += f->coders[i].numOutStreams; } if (numOutStreamsTotal == 0 || numInStreamsTotal < numOutStreamsTotal-1) return (-1); f->numBindPairs = numOutStreamsTotal - 1; if (zip->header_bytes_remaining < f->numBindPairs) return (-1); if (f->numBindPairs > 0) { f->bindPairs = calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs)); if (f->bindPairs == NULL) return (-1); } else f->bindPairs = NULL; for (i = 0; i < f->numBindPairs; i++) { if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0) return (-1); if (UMAX_ENTRY < f->bindPairs[i].inIndex) return (-1); if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0) return (-1); if (UMAX_ENTRY < f->bindPairs[i].outIndex) return (-1); } f->numPackedStreams = numInStreamsTotal - f->numBindPairs; f->packedStreams = calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams)); if (f->packedStreams == NULL) return (-1); if (f->numPackedStreams == 1) { for (i = 0; i < numInStreamsTotal; i++) { unsigned j; for (j = 0; j < f->numBindPairs; j++) { if (f->bindPairs[j].inIndex == i) break; } if (j == f->numBindPairs) break; } if (i == numInStreamsTotal) return (-1); f->packedStreams[0] = i; } else { for (i = 0; i < f->numPackedStreams; i++) { if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0) return (-1); if (UMAX_ENTRY < f->packedStreams[i]) return (-1); } } f->numInStreams = numInStreamsTotal; f->numOutStreams = numOutStreamsTotal; return (0); } static void free_CodersInfo(struct _7z_coders_info *ci) { unsigned i; if (ci->folders) { for (i = 0; i < ci->numFolders; i++) free_Folder(&(ci->folders[i])); free(ci->folders); } } static int read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) { const unsigned char *p; struct _7z_digests digest; unsigned i; memset(ci, 0, sizeof(*ci)); memset(&digest, 0, sizeof(digest)); if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kFolder) goto failed; /* * Read NumFolders. */ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0) goto failed; if (UMAX_ENTRY < ci->numFolders) return (-1); /* * Read External. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; switch (*p) { case 0: ci->folders = calloc((size_t)ci->numFolders, sizeof(*ci->folders)); if (ci->folders == NULL) return (-1); for (i = 0; i < ci->numFolders; i++) { if (read_Folder(a, &(ci->folders[i])) < 0) goto failed; } break; case 1: if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0) return (-1); if (UMAX_ENTRY < ci->dataStreamIndex) return (-1); if (ci->numFolders > 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); goto failed; } break; default: archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); goto failed; } if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kCodersUnPackSize) goto failed; for (i = 0; i < ci->numFolders; i++) { struct _7z_folder *folder = &(ci->folders[i]); unsigned j; folder->unPackSize = calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize)); if (folder->unPackSize == NULL) goto failed; for (j = 0; j < folder->numOutStreams; j++) { if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0) goto failed; } } /* * Read CRCs. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p == kEnd) return (0); if (*p != kCRC) goto failed; if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0) goto failed; for (i = 0; i < ci->numFolders; i++) { ci->folders[i].digest_defined = digest.defineds[i]; ci->folders[i].digest = digest.digests[i]; } /* * Must be kEnd. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kEnd) goto failed; free_Digest(&digest); return (0); failed: free_Digest(&digest); return (-1); } static uint64_t folder_uncompressed_size(struct _7z_folder *f) { int n = (int)f->numOutStreams; unsigned pairs = (unsigned)f->numBindPairs; while (--n >= 0) { unsigned i; for (i = 0; i < pairs; i++) { if (f->bindPairs[i].outIndex == (uint64_t)n) break; } if (i >= pairs) return (f->unPackSize[n]); } return (0); } static void free_SubStreamsInfo(struct _7z_substream_info *ss) { free(ss->unpackSizes); free(ss->digestsDefined); free(ss->digests); } static int read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, struct _7z_folder *f, size_t numFolders) { const unsigned char *p; uint64_t *usizes; size_t unpack_streams; int type; unsigned i; uint32_t numDigests; memset(ss, 0, sizeof(*ss)); for (i = 0; i < numFolders; i++) f[i].numUnpackStreams = 1; if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; if (type == kNumUnPackStream) { unpack_streams = 0; for (i = 0; i < numFolders; i++) { if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) return (-1); if (UMAX_ENTRY < f[i].numUnpackStreams) return (-1); if (unpack_streams > SIZE_MAX - UMAX_ENTRY) { return (-1); } unpack_streams += (size_t)f[i].numUnpackStreams; } if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } else unpack_streams = numFolders; ss->unpack_streams = unpack_streams; if (unpack_streams) { ss->unpackSizes = calloc(unpack_streams, sizeof(*ss->unpackSizes)); ss->digestsDefined = calloc(unpack_streams, sizeof(*ss->digestsDefined)); ss->digests = calloc(unpack_streams, sizeof(*ss->digests)); if (ss->unpackSizes == NULL || ss->digestsDefined == NULL || ss->digests == NULL) return (-1); } usizes = ss->unpackSizes; for (i = 0; i < numFolders; i++) { unsigned pack; uint64_t sum; if (f[i].numUnpackStreams == 0) continue; sum = 0; if (type == kSize) { for (pack = 1; pack < f[i].numUnpackStreams; pack++) { if (parse_7zip_uint64(a, usizes) < 0) return (-1); sum += *usizes++; } } *usizes++ = folder_uncompressed_size(&f[i]) - sum; } if (type == kSize) { if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } for (i = 0; i < unpack_streams; i++) { ss->digestsDefined[i] = 0; ss->digests[i] = 0; } numDigests = 0; for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams != 1 || !f[i].digest_defined) numDigests += (uint32_t)f[i].numUnpackStreams; } if (type == kCRC) { struct _7z_digests tmpDigests; unsigned char *digestsDefined = ss->digestsDefined; uint32_t * digests = ss->digests; int di = 0; memset(&tmpDigests, 0, sizeof(tmpDigests)); if (read_Digests(a, &(tmpDigests), numDigests) < 0) { free_Digest(&tmpDigests); return (-1); } for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams == 1 && f[i].digest_defined) { *digestsDefined++ = 1; *digests++ = f[i].digest; } else { unsigned j; for (j = 0; j < f[i].numUnpackStreams; j++, di++) { *digestsDefined++ = tmpDigests.defineds[di]; *digests++ = tmpDigests.digests[di]; } } } free_Digest(&tmpDigests); if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } /* * Must be kEnd. */ if (type != kEnd) return (-1); return (0); } static void free_StreamsInfo(struct _7z_stream_info *si) { free_PackInfo(&(si->pi)); free_CodersInfo(&(si->ci)); free_SubStreamsInfo(&(si->ss)); } static int read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; unsigned i; memset(si, 0, sizeof(*si)); if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kPackInfo) { uint64_t packPos; if (read_PackInfo(a, &(si->pi)) < 0) return (-1); if (si->pi.positions == NULL || si->pi.sizes == NULL) return (-1); /* * Calculate packed stream positions. */ packPos = si->pi.pos; for (i = 0; i < si->pi.numPackStreams; i++) { si->pi.positions[i] = packPos; packPos += si->pi.sizes[i]; if (packPos > zip->header_offset) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kUnPackInfo) { uint32_t packIndex; struct _7z_folder *f; if (read_CodersInfo(a, &(si->ci)) < 0) return (-1); /* * Calculate packed stream indexes. */ packIndex = 0; f = si->ci.folders; for (i = 0; i < si->ci.numFolders; i++) { f[i].packIndex = packIndex; packIndex += (uint32_t)f[i].numPackedStreams; if (packIndex > si->pi.numPackStreams) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kSubStreamsInfo) { if (read_SubStreamsInfo(a, &(si->ss), si->ci.folders, (size_t)si->ci.numFolders) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); } /* * Must be kEnd. */ if (*p != kEnd) return (-1); return (0); } static void free_Header(struct _7z_header_info *h) { free(h->emptyStreamBools); free(h->emptyFileBools); free(h->antiBools); free(h->attrBools); } static int read_Header(struct archive_read *a, struct _7z_header_info *h, int check_header_id) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; struct _7z_folder *folders; struct _7z_stream_info *si = &(zip->si); struct _7zip_entry *entries; uint32_t folderIndex, indexInFolder; unsigned i; int eindex, empty_streams, sindex; if (check_header_id) { /* * Read Header. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p != kHeader) return (-1); } /* * Read ArchiveProperties. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kArchiveProperties) { for (;;) { uint64_t size; if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == 0) break; if (parse_7zip_uint64(a, &size) < 0) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } /* * Read MainStreamsInfo. */ if (*p == kMainStreamsInfo) { if (read_StreamsInfo(a, &(zip->si)) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kEnd) return (0); /* * Read FilesInfo. */ if (*p != kFilesInfo) return (-1); if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) return (-1); if (UMAX_ENTRY < zip->numFiles) return (-1); zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); if (zip->entries == NULL) return (-1); entries = zip->entries; empty_streams = 0; for (;;) { int type; uint64_t size; size_t ll; if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; if (type == kEnd) break; if (parse_7zip_uint64(a, &size) < 0) return (-1); if (zip->header_bytes_remaining < size) return (-1); ll = (size_t)size; switch (type) { case kEmptyStream: if (h->emptyStreamBools != NULL) return (-1); h->emptyStreamBools = calloc((size_t)zip->numFiles, sizeof(*h->emptyStreamBools)); if (h->emptyStreamBools == NULL) return (-1); if (read_Bools( a, h->emptyStreamBools, (size_t)zip->numFiles) < 0) return (-1); empty_streams = 0; for (i = 0; i < zip->numFiles; i++) { if (h->emptyStreamBools[i]) empty_streams++; } break; case kEmptyFile: if (empty_streams <= 0) { /* Unexcepted sequence. Skip this. */ if (header_bytes(a, ll) == NULL) return (-1); break; } if (h->emptyFileBools != NULL) return (-1); h->emptyFileBools = calloc(empty_streams, sizeof(*h->emptyFileBools)); if (h->emptyFileBools == NULL) return (-1); if (read_Bools(a, h->emptyFileBools, empty_streams) < 0) return (-1); break; case kAnti: if (empty_streams <= 0) { /* Unexcepted sequence. Skip this. */ if (header_bytes(a, ll) == NULL) return (-1); break; } if (h->antiBools != NULL) return (-1); h->antiBools = calloc(empty_streams, sizeof(*h->antiBools)); if (h->antiBools == NULL) return (-1); if (read_Bools(a, h->antiBools, empty_streams) < 0) return (-1); break; case kCTime: case kATime: case kMTime: if (read_Times(a, h, type) < 0) return (-1); break; case kName: { unsigned char *np; size_t nl, nb; /* Skip one byte. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); ll--; if ((ll & 1) || ll < zip->numFiles * 4) return (-1); if (zip->entry_names != NULL) return (-1); zip->entry_names = malloc(ll); if (zip->entry_names == NULL) return (-1); np = zip->entry_names; nb = ll; /* * Copy whole file names. * NOTE: This loop prevents from expanding * the uncompressed buffer in order not to * use extra memory resource. */ while (nb) { size_t b; if (nb > UBUFF_SIZE) b = UBUFF_SIZE; else b = nb; if ((p = header_bytes(a, b)) == NULL) return (-1); memcpy(np, p, b); np += b; nb -= b; } np = zip->entry_names; nl = ll; for (i = 0; i < zip->numFiles; i++) { entries[i].utf16name = np; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) entries[i].wname = (wchar_t *)np; #endif /* Find a terminator. */ while (nl >= 2 && (np[0] || np[1])) { np += 2; nl -= 2; } if (nl < 2) return (-1);/* Terminator not found */ entries[i].name_len = np - entries[i].utf16name; np += 2; nl -= 2; } break; } case kAttributes: { int allAreDefined; if ((p = header_bytes(a, 2)) == NULL) return (-1); allAreDefined = *p; if (h->attrBools != NULL) return (-1); h->attrBools = calloc((size_t)zip->numFiles, sizeof(*h->attrBools)); if (h->attrBools == NULL) return (-1); if (allAreDefined) memset(h->attrBools, 1, (size_t)zip->numFiles); else { if (read_Bools(a, h->attrBools, (size_t)zip->numFiles) < 0) return (-1); } for (i = 0; i < zip->numFiles; i++) { if (h->attrBools[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); entries[i].attr = archive_le32dec(p); } } break; } case kDummy: if (ll == 0) break; default: if (header_bytes(a, ll) == NULL) return (-1); break; } } /* * Set up entry's attributes. */ folders = si->ci.folders; eindex = sindex = 0; folderIndex = indexInFolder = 0; for (i = 0; i < zip->numFiles; i++) { if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0) entries[i].flg |= HAS_STREAM; /* The high 16 bits of attributes is a posix file mode. */ entries[i].mode = entries[i].attr >> 16; if (entries[i].flg & HAS_STREAM) { if ((size_t)sindex >= si->ss.unpack_streams) return (-1); if (entries[i].mode == 0) entries[i].mode = AE_IFREG | 0666; if (si->ss.digestsDefined[sindex]) entries[i].flg |= CRC32_IS_SET; entries[i].ssIndex = sindex; sindex++; } else { int dir; if (h->emptyFileBools == NULL) dir = 1; else { if (h->emptyFileBools[eindex]) dir = 0; else dir = 1; eindex++; } if (entries[i].mode == 0) { if (dir) entries[i].mode = AE_IFDIR | 0777; else entries[i].mode = AE_IFREG | 0666; } else if (dir && (entries[i].mode & AE_IFMT) != AE_IFDIR) { entries[i].mode &= ~AE_IFMT; entries[i].mode |= AE_IFDIR; } if ((entries[i].mode & AE_IFMT) == AE_IFDIR && entries[i].name_len >= 2 && (entries[i].utf16name[entries[i].name_len-2] != '/' || entries[i].utf16name[entries[i].name_len-1] != 0)) { entries[i].utf16name[entries[i].name_len] = '/'; entries[i].utf16name[entries[i].name_len+1] = 0; entries[i].name_len += 2; } entries[i].ssIndex = -1; } if (entries[i].attr & 0x01) entries[i].mode &= ~0222;/* Read only. */ if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) { /* * The entry is an empty file or a directory file, * those both have no contents. */ entries[i].folderIndex = -1; continue; } if (indexInFolder == 0) { for (;;) { if (folderIndex >= si->ci.numFolders) return (-1); if (folders[folderIndex].numUnpackStreams) break; folderIndex++; } } entries[i].folderIndex = folderIndex; if ((entries[i].flg & HAS_STREAM) == 0) continue; indexInFolder++; if (indexInFolder >= folders[folderIndex].numUnpackStreams) { folderIndex++; indexInFolder = 0; } } return (0); } #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) static void fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns) { if (fileTime >= EPOC_TIME) { fileTime -= EPOC_TIME; /* milli seconds base */ *timep = (time_t)(fileTime / 10000000); /* nano seconds base */ *ns = (long)(fileTime % 10000000) * 100; } else { *timep = 0; *ns = 0; } } static int read_Times(struct archive_read *a, struct _7z_header_info *h, int type) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; struct _7zip_entry *entries = zip->entries; unsigned char *timeBools; int allAreDefined; unsigned i; timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools)); if (timeBools == NULL) return (-1); /* Read allAreDefined. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; allAreDefined = *p; if (allAreDefined) memset(timeBools, 1, (size_t)zip->numFiles); else { if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0) goto failed; } /* Read external. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p) { if (parse_7zip_uint64(a, &(h->dataIndex)) < 0) goto failed; if (UMAX_ENTRY < h->dataIndex) goto failed; } for (i = 0; i < zip->numFiles; i++) { if (!timeBools[i]) continue; if ((p = header_bytes(a, 8)) == NULL) goto failed; switch (type) { case kCTime: fileTimeToUtc(archive_le64dec(p), &(entries[i].ctime), &(entries[i].ctime_ns)); entries[i].flg |= CTIME_IS_SET; break; case kATime: fileTimeToUtc(archive_le64dec(p), &(entries[i].atime), &(entries[i].atime_ns)); entries[i].flg |= ATIME_IS_SET; break; case kMTime: fileTimeToUtc(archive_le64dec(p), &(entries[i].mtime), &(entries[i].mtime_ns)); entries[i].flg |= MTIME_IS_SET; break; } } free(timeBools); return (0); failed: free(timeBools); return (-1); } static int decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si) { struct _7zip *zip = (struct _7zip *)a->format->data; errno = 0; if (read_StreamsInfo(a, si) < 0) { if (errno == ENOMEM) archive_set_error(&a->archive, -1, "Couldn't allocate memory"); else archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } if (zip->header_offset < si->pi.pos + si->pi.sizes[0] || (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 || si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) { archive_set_error(&a->archive, -1, "Malformed Header offset"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static const unsigned char * header_bytes(struct archive_read *a, size_t rbytes) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; if (zip->header_bytes_remaining < rbytes) return (NULL); if (zip->pack_stream_bytes_unconsumed) read_consume(a); if (zip->header_is_encoded == 0) { p = __archive_read_ahead(a, rbytes, NULL); if (p == NULL) return (NULL); zip->header_bytes_remaining -= rbytes; zip->pack_stream_bytes_unconsumed = rbytes; } else { const void *buff; ssize_t bytes; bytes = read_stream(a, &buff, rbytes, rbytes); if (bytes <= 0) return (NULL); zip->header_bytes_remaining -= bytes; p = buff; } /* Update checksum */ zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes); return (p); } static int slurp_central_directory(struct archive_read *a, struct _7zip *zip, struct _7z_header_info *header) { const unsigned char *p; uint64_t next_header_offset; uint64_t next_header_size; uint32_t next_header_crc; ssize_t bytes_avail; int check_header_crc, r; if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) return (ARCHIVE_FATAL); if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { /* This is an executable ? Must be self-extracting... */ r = skip_sfx(a, bytes_avail); if (r < ARCHIVE_WARN) return (r); if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) return (ARCHIVE_FATAL); } zip->seek_base += 32; if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) { archive_set_error(&a->archive, -1, "Not 7-Zip archive file"); return (ARCHIVE_FATAL); } /* CRC check. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) { archive_set_error(&a->archive, -1, "Header CRC error"); return (ARCHIVE_FATAL); } next_header_offset = archive_le64dec(p + 12); next_header_size = archive_le64dec(p + 20); next_header_crc = archive_le32dec(p + 28); if (next_header_size == 0) /* There is no entry in an archive file. */ return (ARCHIVE_EOF); if (((int64_t)next_header_offset) < 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 32); if (next_header_offset != 0) { if (bytes_avail >= (ssize_t)next_header_offset) __archive_read_consume(a, next_header_offset); else if (__archive_read_seek(a, next_header_offset + zip->seek_base, SEEK_SET) < 0) return (ARCHIVE_FATAL); } zip->stream_offset = next_header_offset; zip->header_offset = next_header_offset; zip->header_bytes_remaining = next_header_size; zip->header_crc32 = 0; zip->header_is_encoded = 0; zip->header_is_being_read = 1; zip->has_encrypted_entries = 0; check_header_crc = 1; if ((p = header_bytes(a, 1)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } /* Parse ArchiveProperties. */ switch (p[0]) { case kEncodedHeader: /* * The archive has an encoded header and we have to decode it * in order to parse the header correctly. */ r = decode_encoded_header_info(a, &(zip->si)); /* Check the EncodedHeader CRC.*/ if (r == 0 && zip->header_crc32 != next_header_crc) { archive_set_error(&a->archive, -1, "Damaged 7-Zip archive"); r = -1; } if (r == 0) { if (zip->si.ci.folders[0].digest_defined) next_header_crc = zip->si.ci.folders[0].digest; else check_header_crc = 0; if (zip->pack_stream_bytes_unconsumed) read_consume(a); r = setup_decode_folder(a, zip->si.ci.folders, 1); if (r == 0) { zip->header_bytes_remaining = zip->folder_outbytes_remaining; r = seek_pack(a); } } /* Clean up StreamsInfo. */ free_StreamsInfo(&(zip->si)); memset(&(zip->si), 0, sizeof(zip->si)); if (r < 0) return (ARCHIVE_FATAL); zip->header_is_encoded = 1; zip->header_crc32 = 0; /* FALL THROUGH */ case kHeader: /* * Parse the header. */ errno = 0; r = read_Header(a, header, zip->header_is_encoded); if (r < 0) { if (errno == ENOMEM) archive_set_error(&a->archive, -1, "Couldn't allocate memory"); else archive_set_error(&a->archive, -1, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } /* * Must be kEnd. */ if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } /* Check the Header CRC.*/ if (check_header_crc && zip->header_crc32 != next_header_crc) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } break; default: archive_set_error(&a->archive, -1, "Unexpected Property ID = %X", p[0]); return (ARCHIVE_FATAL); } /* Clean up variables be used for decoding the archive header */ zip->pack_stream_remaining = 0; zip->pack_stream_index = 0; zip->folder_outbytes_remaining = 0; zip->uncompressed_buffer_bytes_remaining = 0; zip->pack_stream_bytes_unconsumed = 0; zip->header_is_being_read = 0; return (ARCHIVE_OK); } static ssize_t get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; ssize_t bytes_avail; if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { /* Copy mode. */ /* * 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 7-Zip file data"); return (ARCHIVE_FATAL); } if ((size_t)bytes_avail > zip->uncompressed_buffer_bytes_remaining) bytes_avail = (ssize_t) zip->uncompressed_buffer_bytes_remaining; if ((size_t)bytes_avail > size) bytes_avail = (ssize_t)size; zip->pack_stream_bytes_unconsumed = bytes_avail; } else if (zip->uncompressed_buffer_pointer == NULL) { /* Decompression has failed. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } else { /* Packed mode. */ if (minimum > zip->uncompressed_buffer_bytes_remaining) { /* * If remaining uncompressed data size is less than * the minimum size, fill the buffer up to the * minimum size. */ if (extract_pack_stream(a, minimum) < 0) return (ARCHIVE_FATAL); } if (size > zip->uncompressed_buffer_bytes_remaining) bytes_avail = (ssize_t) zip->uncompressed_buffer_bytes_remaining; else bytes_avail = (ssize_t)size; *buff = zip->uncompressed_buffer_pointer; zip->uncompressed_buffer_pointer += bytes_avail; } zip->uncompressed_buffer_bytes_remaining -= bytes_avail; return (bytes_avail); } static ssize_t extract_pack_stream(struct archive_read *a, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; ssize_t bytes_avail; int r; if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { if (minimum == 0) minimum = 1; if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL || bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } if (bytes_avail > (ssize_t)zip->pack_stream_inbytes_remaining) bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining; zip->pack_stream_inbytes_remaining -= bytes_avail; if (bytes_avail > (ssize_t)zip->folder_outbytes_remaining) bytes_avail = (ssize_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_avail; zip->uncompressed_buffer_bytes_remaining = bytes_avail; return (ARCHIVE_OK); } /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = UBUFF_SIZE; if (zip->uncompressed_buffer_size < minimum) { zip->uncompressed_buffer_size = minimum + 1023; zip->uncompressed_buffer_size &= ~0x3ff; } zip->uncompressed_buffer = malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer_bytes_remaining = 0; } else if (zip->uncompressed_buffer_size < minimum || zip->uncompressed_buffer_bytes_remaining < minimum) { /* * Make sure the uncompressed buffer can have bytes * at least `minimum' bytes. * NOTE: This case happen when reading the header. */ size_t used; if (zip->uncompressed_buffer_pointer != 0) used = zip->uncompressed_buffer_pointer - zip->uncompressed_buffer; else used = 0; if (zip->uncompressed_buffer_size < minimum) { /* * Expand the uncompressed buffer up to * the minimum size. */ void *p; size_t new_size; new_size = minimum + 1023; new_size &= ~0x3ff; p = realloc(zip->uncompressed_buffer, new_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer = (unsigned char *)p; zip->uncompressed_buffer_size = new_size; } /* * Move unconsumed bytes to the head. */ if (used) { memmove(zip->uncompressed_buffer, zip->uncompressed_buffer + used, zip->uncompressed_buffer_bytes_remaining); } } else zip->uncompressed_buffer_bytes_remaining = 0; zip->uncompressed_buffer_pointer = NULL; for (;;) { size_t bytes_in, bytes_out; const void *buff_in; unsigned char *buff_out; int end_of_data; /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ buff_in = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } buff_out = zip->uncompressed_buffer + zip->uncompressed_buffer_bytes_remaining; bytes_out = zip->uncompressed_buffer_size - zip->uncompressed_buffer_bytes_remaining; bytes_in = bytes_avail; if (bytes_in > zip->pack_stream_inbytes_remaining) bytes_in = (size_t)zip->pack_stream_inbytes_remaining; /* Drive decompression. */ r = decompress(a, zip, buff_out, &bytes_out, buff_in, &bytes_in); switch (r) { case ARCHIVE_OK: end_of_data = 0; break; case ARCHIVE_EOF: end_of_data = 1; break; default: return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining -= bytes_in; if (bytes_out > zip->folder_outbytes_remaining) bytes_out = (size_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_out; zip->uncompressed_buffer_bytes_remaining += bytes_out; zip->pack_stream_bytes_unconsumed = bytes_in; /* * Continue decompression until uncompressed_buffer is full. */ if (zip->uncompressed_buffer_bytes_remaining == zip->uncompressed_buffer_size) break; if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size && zip->uncompressed_buffer_bytes_remaining + 5 > zip->uncompressed_buffer_size) break; if (zip->pack_stream_inbytes_remaining == 0 && zip->folder_outbytes_remaining == 0) break; if (end_of_data || (bytes_in == 0 && bytes_out == 0)) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } read_consume(a); } if (zip->uncompressed_buffer_bytes_remaining < minimum) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer_pointer = zip->uncompressed_buffer; return (ARCHIVE_OK); } static int seek_pack(struct archive_read *a) { struct _7zip *zip = (struct _7zip *)a->format->data; int64_t pack_offset; if (zip->pack_stream_remaining <= 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining = zip->si.pi.sizes[zip->pack_stream_index]; pack_offset = zip->si.pi.positions[zip->pack_stream_index]; if (zip->stream_offset != pack_offset) { if (0 > __archive_read_seek(a, pack_offset + zip->seek_base, SEEK_SET)) return (ARCHIVE_FATAL); zip->stream_offset = pack_offset; } zip->pack_stream_index++; zip->pack_stream_remaining--; return (ARCHIVE_OK); } static ssize_t read_stream(struct archive_read *a, const void **buff, size_t size, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; uint64_t skip_bytes = 0; ssize_t r; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { r = extract_pack_stream(a, 0); if (r < 0) return (r); return (get_uncompressed_data(a, buff, size, minimum)); } else if (zip->folder_outbytes_remaining > 0) { /* Extract a remaining pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); return (get_uncompressed_data(a, buff, size, minimum)); } } else return (get_uncompressed_data(a, buff, size, minimum)); /* * Current pack stream has been consumed. */ if (zip->pack_stream_remaining == 0) { if (zip->header_is_being_read) { /* Invalid sequence. This might happen when * reading a malformed archive. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } /* * All current folder's pack streams have been * consumed. Switch to next folder. */ if (zip->folder_index == 0 && (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes || zip->folder_index != zip->entry->folderIndex)) { zip->folder_index = zip->entry->folderIndex; skip_bytes = zip->si.ci.folders[zip->folder_index].skipped_bytes; } if (zip->folder_index >= zip->si.ci.numFolders) { /* * We have consumed all folders and its pack streams. */ *buff = NULL; return (0); } r = setup_decode_folder(a, &(zip->si.ci.folders[zip->folder_index]), 0); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->folder_index++; } /* * Switch to next pack stream. */ r = seek_pack(a); if (r < 0) return (r); /* Extract a new pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); /* * Skip the bytes we alrady has skipped in skip_stream(). */ while (skip_bytes) { ssize_t skipped; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { r = extract_pack_stream(a, 0); if (r < 0) return (r); } else if (zip->folder_outbytes_remaining > 0) { /* Extract a remaining pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } } skipped = get_uncompressed_data( a, buff, (size_t)skip_bytes, 0); if (skipped < 0) return (skipped); skip_bytes -= skipped; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } return (get_uncompressed_data(a, buff, size, minimum)); } static int setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, int header) { struct _7zip *zip = (struct _7zip *)a->format->data; const struct _7z_coder *coder1, *coder2; const char *cname = (header)?"archive header":"file content"; unsigned i; int r, found_bcj2 = 0; /* * Release the memory which the previous folder used for BCJ2. */ for (i = 0; i < 3; i++) { if (zip->sub_stream_buff[i] != NULL) free(zip->sub_stream_buff[i]); zip->sub_stream_buff[i] = NULL; } /* * Initialize a stream reader. */ zip->pack_stream_remaining = (unsigned)folder->numPackedStreams; zip->pack_stream_index = (unsigned)folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; /* * Check coder types. */ for (i = 0; i < folder->numCoders; i++) { switch(folder->coders[i].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: { /* For entry that is associated with this folder, mark it as encrypted (data+metadata). */ zip->has_encrypted_entries = 1; if (a->entry) { archive_entry_set_is_data_encrypted(a->entry, 1); archive_entry_set_is_metadata_encrypted(a->entry, 1); } archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "The %s is encrypted, " "but currently not supported", cname); return (ARCHIVE_FATAL); } case _7Z_X86_BCJ2: { found_bcj2++; break; } } } /* Now that we've checked for encryption, if there were still no * encrypted entries found we can say for sure that there are none. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "The %s is encoded with many filters, " "but currently not supported", cname); return (ARCHIVE_FATAL); } coder1 = &(folder->coders[0]); if (folder->numCoders == 2) coder2 = &(folder->coders[1]); else coder2 = NULL; if (found_bcj2) { /* * Preparation to decode BCJ2. * Decoding BCJ2 requires four sources. Those are at least, * as far as I know, two types of the storage form. */ const struct _7z_coder *fc = folder->coders; static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL}; const struct _7z_coder *scoder[3] = {&coder_copy, &coder_copy, &coder_copy}; const void *buff; ssize_t bytes; unsigned char *b[3] = {NULL, NULL, NULL}; uint64_t sunpack[3] ={-1, -1, -1}; size_t s[3] = {0, 0, 0}; int idx[3] = {0, 1, 2}; if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 && folder->numInStreams == 7 && folder->numOutStreams == 4 && zip->pack_stream_remaining == 4) { /* Source type 1 made by 7zr or 7z with -m options. */ if (folder->bindPairs[0].inIndex == 5) { /* The form made by 7zr */ idx[0] = 1; idx[1] = 2; idx[2] = 0; scoder[1] = &(fc[1]); scoder[2] = &(fc[0]); sunpack[1] = folder->unPackSize[1]; sunpack[2] = folder->unPackSize[0]; coder1 = &(fc[2]); } else { /* * NOTE: Some patterns do not work. * work: * 7z a -m0=BCJ2 -m1=COPY -m2=COPY * -m3=(any) * 7z a -m0=BCJ2 -m1=COPY -m2=(any) * -m3=COPY * 7z a -m0=BCJ2 -m1=(any) -m2=COPY * -m3=COPY * not work: * other patterns. * * We have to handle this like `pipe' or * our libarchive7s filter frame work, * decoding the BCJ2 main stream sequentially, * m3 -> m2 -> m1 -> BCJ2. * */ if (fc[0].codec == _7Z_COPY && fc[1].codec == _7Z_COPY) coder1 = &(folder->coders[2]); else if (fc[0].codec == _7Z_COPY && fc[2].codec == _7Z_COPY) coder1 = &(folder->coders[1]); else if (fc[1].codec == _7Z_COPY && fc[2].codec == _7Z_COPY) coder1 = &(folder->coders[0]); else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unsupported form of " "BCJ2 streams"); return (ARCHIVE_FATAL); } } coder2 = &(fc[3]); zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[2]; } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 && zip->pack_stream_remaining == 4 && folder->numInStreams == 5 && folder->numOutStreams == 2) { /* Source type 0 made by 7z */ zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[0]; } else { /* We got an unexpected form. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unsupported form of BCJ2 streams"); return (ARCHIVE_FATAL); } /* Skip the main stream at this time. */ if ((r = seek_pack(a)) < 0) return (r); zip->pack_stream_bytes_unconsumed = (size_t)zip->pack_stream_inbytes_remaining; read_consume(a); /* Read following three sub streams. */ for (i = 0; i < 3; i++) { const struct _7z_coder *coder = scoder[i]; if ((r = seek_pack(a)) < 0) { free(b[0]); free(b[1]); free(b[2]); return (r); } if (sunpack[i] == (uint64_t)-1) zip->folder_outbytes_remaining = zip->pack_stream_inbytes_remaining; else zip->folder_outbytes_remaining = sunpack[i]; r = init_decompression(a, zip, coder, NULL); if (r != ARCHIVE_OK) { free(b[0]); free(b[1]); free(b[2]); return (ARCHIVE_FATAL); } /* Allocate memory for the decorded data of a sub * stream. */ b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { free(b[0]); free(b[1]); free(b[2]); archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } /* Extract a sub stream. */ while (zip->pack_stream_inbytes_remaining > 0) { r = (int)extract_pack_stream(a, 0); if (r < 0) { free(b[0]); free(b[1]); free(b[2]); return (r); } bytes = get_uncompressed_data(a, &buff, zip->uncompressed_buffer_bytes_remaining, 0); if (bytes < 0) { free(b[0]); free(b[1]); free(b[2]); return ((int)bytes); } memcpy(b[i]+s[i], buff, bytes); s[i] += bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } } /* Set the sub streams to the right place. */ for (i = 0; i < 3; i++) { zip->sub_stream_buff[i] = b[idx[i]]; zip->sub_stream_size[i] = s[idx[i]]; zip->sub_stream_bytes_remaining[i] = s[idx[i]]; } /* Allocate memory used for decoded main stream bytes. */ if (zip->tmp_stream_buff == NULL) { zip->tmp_stream_buff_size = 32 * 1024; zip->tmp_stream_buff = malloc(zip->tmp_stream_buff_size); if (zip->tmp_stream_buff == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } } zip->tmp_stream_bytes_avail = 0; zip->tmp_stream_bytes_remaining = 0; zip->odd_bcj_size = 0; zip->bcj2_outPos = 0; /* * Reset a stream reader in order to read the main stream * of BCJ2. */ zip->pack_stream_remaining = 1; zip->pack_stream_index = (unsigned)folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; } /* * Initialize the decompressor for the new folder's pack streams. */ r = init_decompression(a, zip, coder1, coder2); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static int64_t skip_stream(struct archive_read *a, size_t skip_bytes) { struct _7zip *zip = (struct _7zip *)a->format->data; const void *p; int64_t skipped_bytes; size_t bytes = skip_bytes; if (zip->folder_index == 0) { /* * Optimization for a list mode. * Avoid unncecessary decoding operations. */ zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes += skip_bytes; return (skip_bytes); } while (bytes) { skipped_bytes = read_stream(a, &p, bytes, 0); if (skipped_bytes < 0) return (skipped_bytes); if (skipped_bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } bytes -= (size_t)skipped_bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } return (skip_bytes); } /* * Brought from LZMA SDK. * * Bra86.c -- Converter for x86 code (BCJ) * 2008-10-04 : Igor Pavlov : Public domain * */ #define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) static void x86_Init(struct _7zip *zip) { zip->bcj_state = 0; zip->bcj_prevPosT = (size_t)0 - 1; zip->bcj_prevMask = 0; zip->bcj_ip = 5; } static size_t x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) { static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; size_t bufferPos, prevPosT; uint32_t ip, prevMask; if (size < 5) return 0; bufferPos = 0; prevPosT = zip->bcj_prevPosT; prevMask = zip->bcj_prevMask; ip = zip->bcj_ip; for (;;) { uint8_t *p = data + bufferPos; uint8_t *limit = data + size - 4; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; bufferPos = (size_t)(p - data); if (p >= limit) break; prevPosT = bufferPos - prevPosT; if (prevPosT > 3) prevMask = 0; else { prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; if (prevMask != 0) { unsigned char b = p[4 - kMaskToBitNumber[prevMask]]; if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) { prevPosT = bufferPos; prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; continue; } } } prevPosT = bufferPos; if (Test86MSByte(p[4])) { uint32_t src = ((uint32_t)p[4] << 24) | ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) | ((uint32_t)p[1]); uint32_t dest; for (;;) { uint8_t b; int b_index; dest = src - (ip + (uint32_t)bufferPos); if (prevMask == 0) break; b_index = kMaskToBitNumber[prevMask] * 8; b = (uint8_t)(dest >> (24 - b_index)); if (!Test86MSByte(b)) break; src = dest ^ ((1 << (32 - b_index)) - 1); } p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1)); p[3] = (uint8_t)(dest >> 16); p[2] = (uint8_t)(dest >> 8); p[1] = (uint8_t)dest; bufferPos += 5; } else { prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; } } zip->bcj_prevPosT = prevPosT; zip->bcj_prevMask = prevMask; zip->bcj_ip += (uint32_t)bufferPos; return (bufferPos); } /* * Brought from LZMA SDK. * * Bcj2.c -- Converter for x86 code (BCJ2) * 2008-10-04 : Igor Pavlov : Public domain * */ #define SZ_ERROR_DATA ARCHIVE_FAILED #define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) #define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) #define kNumTopBits 24 #define kTopValue ((uint32_t)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_READ_BYTE (*buffer++) #define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } #define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \ { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }} #define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; } #define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound) #define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; #define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; static ssize_t Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) { size_t inPos = 0, outPos = 0; const uint8_t *buf0, *buf1, *buf2, *buf3; size_t size0, size1, size2, size3; const uint8_t *buffer, *bufferLim; unsigned int i, j; size0 = zip->tmp_stream_bytes_remaining; buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0; size1 = zip->sub_stream_bytes_remaining[0]; buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1; size2 = zip->sub_stream_bytes_remaining[1]; buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2; size3 = zip->sub_stream_bytes_remaining[2]; buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3; buffer = buf3; bufferLim = buffer + size3; if (zip->bcj_state == 0) { /* * Initialize. */ zip->bcj2_prevByte = 0; for (i = 0; i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++) zip->bcj2_p[i] = kBitModelTotal >> 1; RC_INIT2; zip->bcj_state = 1; } /* * Gather the odd bytes of a previous call. */ for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) { outBuf[outPos++] = zip->odd_bcj[i]; zip->odd_bcj_size--; } if (outSize == 0) { zip->bcj2_outPos += outPos; return (outPos); } for (;;) { uint8_t b; CProb *prob; uint32_t bound; uint32_t ttt; size_t limit = size0 - inPos; if (outSize - outPos < limit) limit = outSize - outPos; if (zip->bcj_state == 1) { while (limit != 0) { uint8_t bb = buf0[inPos]; outBuf[outPos++] = bb; if (IsJ(zip->bcj2_prevByte, bb)) { zip->bcj_state = 2; break; } inPos++; zip->bcj2_prevByte = bb; limit--; } } if (limit == 0 || outPos == outSize) break; zip->bcj_state = 1; b = buf0[inPos++]; if (b == 0xE8) prob = zip->bcj2_p + zip->bcj2_prevByte; else if (b == 0xE9) prob = zip->bcj2_p + 256; else prob = zip->bcj2_p + 257; IF_BIT_0(prob) { UPDATE_0(prob) zip->bcj2_prevByte = b; } else { uint32_t dest; const uint8_t *v; uint8_t out[4]; UPDATE_1(prob) if (b == 0xE8) { v = buf1; if (size1 < 4) return SZ_ERROR_DATA; buf1 += 4; size1 -= 4; } else { v = buf2; if (size2 < 4) return SZ_ERROR_DATA; buf2 += 4; size2 -= 4; } dest = (((uint32_t)v[0] << 24) | ((uint32_t)v[1] << 16) | ((uint32_t)v[2] << 8) | ((uint32_t)v[3])) - ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4); out[0] = (uint8_t)dest; out[1] = (uint8_t)(dest >> 8); out[2] = (uint8_t)(dest >> 16); out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24); for (i = 0; i < 4 && outPos < outSize; i++) outBuf[outPos++] = out[i]; if (i < 4) { /* * Save odd bytes which we could not add into * the output buffer because of out of space. */ zip->odd_bcj_size = 4 -i; for (; i < 4; i++) { j = i - 4 + (unsigned)zip->odd_bcj_size; zip->odd_bcj[j] = out[i]; } break; } } } zip->tmp_stream_bytes_remaining -= inPos; zip->sub_stream_bytes_remaining[0] = size1; zip->sub_stream_bytes_remaining[1] = size2; zip->sub_stream_bytes_remaining[2] = bufferLim - buffer; zip->bcj2_outPos += outPos; return ((ssize_t)outPos); } Index: vendor/libarchive/dist/libarchive/archive_read_support_format_cpio.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_cpio.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_cpio.c (revision 310622) @@ -1,1079 +1,1079 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $"); #ifdef HAVE_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #define bin_magic_offset 0 #define bin_magic_size 2 #define bin_dev_offset 2 #define bin_dev_size 2 #define bin_ino_offset 4 #define bin_ino_size 2 #define bin_mode_offset 6 #define bin_mode_size 2 #define bin_uid_offset 8 #define bin_uid_size 2 #define bin_gid_offset 10 #define bin_gid_size 2 #define bin_nlink_offset 12 #define bin_nlink_size 2 #define bin_rdev_offset 14 #define bin_rdev_size 2 #define bin_mtime_offset 16 #define bin_mtime_size 4 #define bin_namesize_offset 20 #define bin_namesize_size 2 #define bin_filesize_offset 22 #define bin_filesize_size 4 #define bin_header_size 26 #define odc_magic_offset 0 #define odc_magic_size 6 #define odc_dev_offset 6 #define odc_dev_size 6 #define odc_ino_offset 12 #define odc_ino_size 6 #define odc_mode_offset 18 #define odc_mode_size 6 #define odc_uid_offset 24 #define odc_uid_size 6 #define odc_gid_offset 30 #define odc_gid_size 6 #define odc_nlink_offset 36 #define odc_nlink_size 6 #define odc_rdev_offset 42 #define odc_rdev_size 6 #define odc_mtime_offset 48 #define odc_mtime_size 11 #define odc_namesize_offset 59 #define odc_namesize_size 6 #define odc_filesize_offset 65 #define odc_filesize_size 11 #define odc_header_size 76 #define newc_magic_offset 0 #define newc_magic_size 6 #define newc_ino_offset 6 #define newc_ino_size 8 #define newc_mode_offset 14 #define newc_mode_size 8 #define newc_uid_offset 22 #define newc_uid_size 8 #define newc_gid_offset 30 #define newc_gid_size 8 #define newc_nlink_offset 38 #define newc_nlink_size 8 #define newc_mtime_offset 46 #define newc_mtime_size 8 #define newc_filesize_offset 54 #define newc_filesize_size 8 #define newc_devmajor_offset 62 #define newc_devmajor_size 8 #define newc_devminor_offset 70 #define newc_devminor_size 8 #define newc_rdevmajor_offset 78 #define newc_rdevmajor_size 8 #define newc_rdevminor_offset 86 #define newc_rdevminor_size 8 #define newc_namesize_offset 94 #define newc_namesize_size 8 #define newc_checksum_offset 102 #define newc_checksum_size 8 #define newc_header_size 110 /* * An afio large ASCII header, which they named itself. * afio utility uses this header, if a file size is larger than 2G bytes * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than * 0x7fffffff, which we cannot record to odc header because of its limit. * If not, uses odc header. */ #define afiol_magic_offset 0 #define afiol_magic_size 6 #define afiol_dev_offset 6 #define afiol_dev_size 8 /* hex */ #define afiol_ino_offset 14 #define afiol_ino_size 16 /* hex */ #define afiol_ino_m_offset 30 /* 'm' */ #define afiol_mode_offset 31 #define afiol_mode_size 6 /* oct */ #define afiol_uid_offset 37 #define afiol_uid_size 8 /* hex */ #define afiol_gid_offset 45 #define afiol_gid_size 8 /* hex */ #define afiol_nlink_offset 53 #define afiol_nlink_size 8 /* hex */ #define afiol_rdev_offset 61 #define afiol_rdev_size 8 /* hex */ #define afiol_mtime_offset 69 #define afiol_mtime_size 16 /* hex */ #define afiol_mtime_n_offset 85 /* 'n' */ #define afiol_namesize_offset 86 #define afiol_namesize_size 4 /* hex */ #define afiol_flag_offset 90 #define afiol_flag_size 4 /* hex */ #define afiol_xsize_offset 94 #define afiol_xsize_size 4 /* hex */ #define afiol_xsize_s_offset 98 /* 's' */ #define afiol_filesize_offset 99 #define afiol_filesize_size 16 /* hex */ #define afiol_filesize_c_offset 115 /* ':' */ #define afiol_header_size 116 struct links_entry { struct links_entry *next; struct links_entry *previous; int links; dev_t dev; int64_t ino; char *name; }; #define CPIO_MAGIC 0x13141516 struct cpio { int magic; int (*read_header)(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); struct links_entry *links_head; int64_t entry_bytes_remaining; int64_t entry_bytes_unconsumed; int64_t entry_offset; int64_t entry_padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; static int64_t atol16(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive_read *, int); static int archive_read_format_cpio_options(struct archive_read *, const char *, const char *); static int archive_read_format_cpio_cleanup(struct archive_read *); static int archive_read_format_cpio_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cpio_skip(struct archive_read *); static int64_t be4(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_bin_le(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_newc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_odc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_afiol(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); static int64_t le4(const unsigned char *); static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry); int archive_read_support_format_cpio(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cpio *cpio; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cpio"); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } cpio->magic = CPIO_MAGIC; r = __archive_read_register_format(a, cpio, "cpio", archive_read_format_cpio_bid, archive_read_format_cpio_options, archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, archive_read_format_cpio_skip, NULL, archive_read_format_cpio_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cpio); return (ARCHIVE_OK); } static int archive_read_format_cpio_bid(struct archive_read *a, int best_bid) { const unsigned char *p; struct cpio *cpio; int bid; (void)best_bid; /* UNUSED */ cpio = (struct cpio *)(a->format->data); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); bid = 0; if (memcmp(p, "070707", 6) == 0) { /* ASCII cpio archive (odc, POSIX.1) */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070727", 6) == 0) { /* afio large ASCII cpio archive */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that almost hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070702", 6) == 0) { /* ASCII cpio archive (SVR4 with CRC) */ /* XXX TODO: Flag that we should check the CRC. XXX */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (p[0] * 256 + p[1] == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ } else if (p[0] + p[1] * 256 == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; /* Is more verification possible here? */ } else return (ARCHIVE_WARN); return (bid); } static int archive_read_format_cpio_options(struct archive_read *a, const char *key, const char *val) { struct cpio *cpio; int ret = ARCHIVE_FAILED; cpio = (struct cpio *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { - /* Handle filnames as libarchive 2.x */ + /* Handle filenames as libarchive 2.x */ cpio->init_default_conversion = (val != NULL)?1:0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "cpio: hdrcharset option needs a character-set name"); else { cpio->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; const void *h; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; int r; cpio = (struct cpio *)(a->format->data); sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); if (r < ARCHIVE_WARN) return (r); /* Read name from buffer. */ h = __archive_read_ahead(a, namelength + name_pad, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, (const char *)h, namelength, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname can't be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } cpio->entry_offset = 0; __archive_read_consume(a, namelength + name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { if (cpio->entry_bytes_remaining > 1024 * 1024) { archive_set_error(&a->archive, ENOMEM, "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); return (ARCHIVE_FATAL); } h = __archive_read_ahead(a, (size_t)cpio->entry_bytes_remaining, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_symlink_l(entry, (const char *)h, (size_t)cpio->entry_bytes_remaining, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname can't be converted from %s to " "current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } __archive_read_consume(a, cpio->entry_bytes_remaining); cpio->entry_bytes_remaining = 0; } /* XXX TODO: If the full mode is 0160200, then this is a Solaris * ACL description for the following entry. Read this body * and parse it as a Solaris-style ACL, then read the next * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* Detect and record hardlinks to previously-extracted entries. */ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } return (r); } static int archive_read_format_cpio_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct cpio *cpio; cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_unconsumed) { __archive_read_consume(a, cpio->entry_bytes_unconsumed); cpio->entry_bytes_unconsumed = 0; } if (cpio->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) bytes_read = (ssize_t)cpio->entry_bytes_remaining; *size = bytes_read; cpio->entry_bytes_unconsumed = bytes_read; *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; return (ARCHIVE_OK); } else { if (cpio->entry_padding != __archive_read_consume(a, cpio->entry_padding)) { return (ARCHIVE_FATAL); } cpio->entry_padding = 0; *buff = NULL; *size = 0; *offset = cpio->entry_offset; return (ARCHIVE_EOF); } } static int archive_read_format_cpio_skip(struct archive_read *a) { struct cpio *cpio = (struct cpio *)(a->format->data); int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding + cpio->entry_bytes_unconsumed; if (to_skip != __archive_read_consume(a, to_skip)) { return (ARCHIVE_FATAL); } cpio->entry_bytes_remaining = 0; cpio->entry_padding = 0; cpio->entry_bytes_unconsumed = 0; return (ARCHIVE_OK); } /* * Skip forward to the next cpio newc header by searching for the * 07070[12] string. This should be generalized and merged with * find_odc_header below. */ static int is_hex(const char *p, size_t len) { while (len-- > 0) { if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')) ++p; else return (0); } return (1); } static int find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, newc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("07070", p, 5) == 0 && (p[5] == '1' || p[5] == '2') && is_hex(p, newc_header_size)) return (ARCHIVE_OK); /* * Scan ahead until we find something that looks * like a newc header. */ while (p + newc_header_size <= q) { switch (p[5]) { case '1': case '2': if (memcmp("07070", p, 5) == 0 && is_hex(p, newc_header_size)) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_newc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; int r; r = find_newc_header(a); if (r < ARCHIVE_WARN) return (r); /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, newc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out hex fields. */ header = (const char *)h; if (memcmp(header + newc_magic_offset, "070701", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)"; } else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)"; } else { /* TODO: Abort here? */ } archive_entry_set_devmajor(entry, (dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size)); archive_entry_set_devminor(entry, (dev_t)atol16(header + newc_devminor_offset, newc_devminor_size)); archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol16(header + newc_mode_offset, newc_mode_size)); archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size)); archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size)); archive_entry_set_rdevmajor(entry, (dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size)); archive_entry_set_rdevminor(entry, (dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size)); archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0); *namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size); /* Pad name to 2 more than a multiple of 4. */ *name_pad = (2 - *namelength) & 3; /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol16(header + newc_filesize_offset, newc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); /* Pad file contents to a multiple of 4. */ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; __archive_read_consume(a, newc_header_size); return (r); } /* * Skip forward to the next cpio odc header by searching for the * 070707 string. This is a hand-optimized search that could * probably be easily generalized to handle all character-based * cpio variants. */ static int is_octal(const char *p, size_t len) { while (len-- > 0) { if (*p < '0' || *p > '7') return (0); ++p; } return (1); } static int is_afio_large(const char *h, size_t len) { if (len < afiol_header_size) return (0); if (h[afiol_ino_m_offset] != 'm' || h[afiol_mtime_n_offset] != 'n' || h[afiol_xsize_s_offset] != 's' || h[afiol_filesize_c_offset] != ':') return (0); if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset)) return (0); if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset)) return (0); if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset)) return (0); if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size)) return (0); return (1); } static int find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, odc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) return (ARCHIVE_OK); if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; return (ARCHIVE_OK); } /* * Scan ahead until we find something that looks * like an odc header. */ while (p + odc_header_size <= q) { switch (p[5]) { case '7': if ((memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) || (memcmp("070727", p, 6) == 0 && is_afio_large(p, q - p))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (p[4] == '2') a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_odc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; int r; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; /* Find the start of the next header. */ r = find_odc_header(a); if (r < ARCHIVE_WARN) return (r); if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) { int r2 = (header_afiol(a, cpio, entry, namelength, name_pad)); if (r2 == ARCHIVE_OK) return (r); else return (r2); } /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, odc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol8(header + odc_dev_offset, odc_dev_size)); archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + odc_mode_offset, odc_mode_size)); archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size)); archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol8(header + odc_rdev_offset, odc_rdev_size)); archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0); *namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size); *name_pad = 0; /* No padding of filename. */ /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol8(header + odc_filesize_offset, odc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, odc_header_size); return (r); } /* * NOTE: if a filename suffix is ".z", it is the file gziped by afio. * it would be nice that we can show uncompressed file size and we can * uncompressed file contents automatically, unfortunately we have nothing * to get a uncompressed file size while reading each header. It means * we also cannot uncompress file contents under our framework. */ static int header_afiol(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; a->archive.archive_format_name = "afio large ASCII"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, afiol_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size)); archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size)); archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size)); archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size)); archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0); *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size); *name_pad = 0; /* No padding of filename. */ cpio->entry_bytes_remaining = atol16(header + afiol_filesize_offset, afiol_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, afiol_header_size); return (ARCHIVE_OK); } static int header_bin_le(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256); archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256); archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256); archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256); archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256); archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256); archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256); archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = le4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int header_bin_be(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]); archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]); archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]); archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]); archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]); archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]); archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]); archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1]; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = be4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int archive_read_format_cpio_cleanup(struct archive_read *a) { struct cpio *cpio; cpio = (struct cpio *)(a->format->data); /* Free inode->name map */ while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; if (cpio->links_head->name) free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } free(cpio); (a->format->data) = NULL; return (ARCHIVE_OK); } static int64_t le4(const unsigned char *p) { return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8)); } static int64_t be4(const unsigned char *p) { return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3])); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t atol8(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else return (l); p++; l <<= 3; l |= digit; } return (l); } static int64_t atol16(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= 'a' && *p <= 'f') digit = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') digit = *p - 'A' + 10; else if (*p >= '0' && *p <= '9') digit = *p - '0'; else return (l); p++; l <<= 4; l |= digit; } return (l); } static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry) { struct links_entry *le; dev_t dev; int64_t ino; if (archive_entry_nlink(entry) <= 1) return (ARCHIVE_OK); dev = archive_entry_dev(entry); ino = archive_entry_ino64(entry); /* * First look in the list of multiply-linked files. If we've * already dumped it, convert this entry to a hard link entry. */ for (le = cpio->links_head; le; le = le->next) { if (le->dev == dev && le->ino == ino) { archive_entry_copy_hardlink(entry, le->name); if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (cpio->links_head == le) cpio->links_head = le->next; free(le->name); free(le); } return (ARCHIVE_OK); } } le = (struct links_entry *)malloc(sizeof(struct links_entry)); if (le == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; le->name = strdup(archive_entry_pathname(entry)); if (le->name == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } Index: vendor/libarchive/dist/libarchive/archive_read_support_format_zip.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_zip.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_zip.c (revision 310622) @@ -1,3078 +1,3082 @@ /*- * 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: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $"); /* * 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 decyption. */ /* 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_decypt_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_decypt_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 { 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) { if (datasize < 8) break; zip_entry->uncompressed_size = archive_le64dec(p + offset); offset += 8; datasize -= 8; } if (zip_entry->compressed_size == 0xffffffff) { if (datasize < 8) break; zip_entry->compressed_size = archive_le64dec(p + offset); offset += 8; datasize -= 8; } if (zip_entry->local_header_offset == 0xffffffff) { if (datasize < 8) break; zip_entry->local_header_offset = archive_le64dec(p + offset); 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 ((zip_entry->mode & AE_IFMT) == 0) { - /* Especially in streaming mode, we can end up - here without having seen proper mode information. - Guess from the filename. */ + /* 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); - if (len > 0 && wp[len - 1] == L'/') - zip_entry->mode |= AE_IFDIR; - else - zip_entry->mode |= AE_IFREG; + has_slash = len > 0 && wp[len - 1] == L'/'; } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; - if (len > 0 && cp[len - 1] == '/') - zip_entry->mode |= AE_IFDIR; - else - zip_entry->mode |= AE_IFREG; + has_slash = len > 0 && cp[len - 1] == '/'; } - if (zip_entry->mode == AE_IFDIR) { - zip_entry->mode |= 0775; - } else if (zip_entry->mode == AE_IFREG) { - zip_entry->mode |= 0664; + /* 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); } } 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); } } } 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) { zip->entry->crc32 = archive_le32dec(p + 4); zip->entry->compressed_size = archive_le64dec(p + 8); zip->entry->uncompressed_size = archive_le64dec(p + 16); 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) { zip->entry->crc32 = archive_le32dec(p); zip->entry->compressed_size = archive_le64dec(p + 4); zip->entry->uncompressed_size = archive_le64dec(p + 12); 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 */ /* Suuported 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 implemnted 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 Decyption. */ 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 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; /* Must be only a single volume. */ if (archive_le32dec(p + 16) != 1) return; /* Find the Zip64 EOCD record. */ eocd64_offset = archive_le64dec(p + 8); if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0) return; if ((p = __archive_read_ahead(a, 56, NULL)) == NULL) return; /* Make sure we can read all of it. */ eocd64_size = archive_le64dec(p + 4) + 12; if (eocd64_size < 56 || eocd64_size > 16384) return; if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL) return; /* Sanity-check the EOCD64 */ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */ return; if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */ return; /* CD can't be split. */ if (archive_le64dec(p + 24) != archive_le64dec(p + 32)) return; /* Save the central directory offset for later use. */ zip->central_directory_offset = archive_le64dec(p + 48); } 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); } } 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: vendor/libarchive/dist/libarchive/archive_string.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_string.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_string.c (revision 310622) @@ -1,4198 +1,4200 @@ /*- * Copyright (c) 2003-2011 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33:22Z kientzle $"); /* * Basic resizable string support, to simplify manipulating arbitrary-sized * strings while minimizing heap activity. * * In particular, the buffer used by a string object is only grown, it * never shrinks, so you can clear and reuse the same string object * without incurring additional memory allocations. */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_ICONV_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_LOCALCHARSET_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #endif #include "archive_endian.h" #include "archive_private.h" #include "archive_string.h" #include "archive_string_composition.h" #if !defined(HAVE_WMEMCPY) && !defined(wmemcpy) #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif #if !defined(HAVE_WMEMMOVE) && !defined(wmemmove) #define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) #endif struct archive_string_conv { struct archive_string_conv *next; char *from_charset; char *to_charset; unsigned from_cp; unsigned to_cp; /* Set 1 if from_charset and to_charset are the same. */ int same; int flag; #define SCONV_TO_CHARSET 1 /* MBS is being converted to specified * charset. */ #define SCONV_FROM_CHARSET (1<<1) /* MBS is being converted from * specified charset. */ #define SCONV_BEST_EFFORT (1<<2) /* Copy at least ASCII code. */ #define SCONV_WIN_CP (1<<3) /* Use Windows API for converting * MBS. */ #define SCONV_UTF8_LIBARCHIVE_2 (1<<4) /* Incorrect UTF-8 made by libarchive * 2.x in the wrong assumption. */ #define SCONV_NORMALIZATION_C (1<<6) /* Need normalization to be Form C. * Before UTF-8 characters are actually * processed. */ #define SCONV_NORMALIZATION_D (1<<7) /* Need normalization to be Form D. * Before UTF-8 characters are actually * processed. * Currently this only for MAC OS X. */ #define SCONV_TO_UTF8 (1<<8) /* "to charset" side is UTF-8. */ #define SCONV_FROM_UTF8 (1<<9) /* "from charset" side is UTF-8. */ #define SCONV_TO_UTF16BE (1<<10) /* "to charset" side is UTF-16BE. */ #define SCONV_FROM_UTF16BE (1<<11) /* "from charset" side is UTF-16BE. */ #define SCONV_TO_UTF16LE (1<<12) /* "to charset" side is UTF-16LE. */ #define SCONV_FROM_UTF16LE (1<<13) /* "from charset" side is UTF-16LE. */ #define SCONV_TO_UTF16 (SCONV_TO_UTF16BE | SCONV_TO_UTF16LE) #define SCONV_FROM_UTF16 (SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE) #if HAVE_ICONV iconv_t cd; iconv_t cd_w;/* Use at archive_mstring on * Windows. */ #endif /* A temporary buffer for normalization. */ struct archive_string utftmp; int (*converter[2])(struct archive_string *, const void *, size_t, struct archive_string_conv *); int nconverter; }; #define CP_C_LOCALE 0 /* "C" locale only for this file. */ #define CP_UTF16LE 1200 #define CP_UTF16BE 1201 #define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF) #define IS_LOW_SURROGATE_LA(uc) ((uc) >= 0xDC00 && (uc) <= 0xDFFF) #define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF) #define UNICODE_MAX 0x10FFFF #define UNICODE_R_CHAR 0xFFFD /* Replacement character. */ /* Set U+FFFD(Replacement character) in UTF-8. */ static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd}; static struct archive_string_conv *find_sconv_object(struct archive *, const char *, const char *); static void add_sconv_object(struct archive *, struct archive_string_conv *); static struct archive_string_conv *create_sconv_object(const char *, const char *, unsigned, int); static void free_sconv_object(struct archive_string_conv *); static struct archive_string_conv *get_sconv_object(struct archive *, const char *, const char *, int); static unsigned make_codepage_from_charset(const char *); static unsigned get_current_codepage(void); static unsigned get_current_oemcp(void); static size_t mbsnbytes(const void *, size_t); static size_t utf16nbytes(const void *, size_t); #if defined(_WIN32) && !defined(__CYGWIN__) static int archive_wstring_append_from_mbs_in_codepage( struct archive_wstring *, const char *, size_t, struct archive_string_conv *); static int archive_string_append_from_wcs_in_codepage(struct archive_string *, const wchar_t *, size_t, struct archive_string_conv *); static int is_big_endian(void); static int strncat_in_codepage(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_from_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_from_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_to_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_to_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); #endif static int best_effort_strncat_from_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int best_effort_strncat_from_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int best_effort_strncat_to_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int best_effort_strncat_to_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); #if defined(HAVE_ICONV) static int iconv_strncat_in_locale(struct archive_string *, const void *, size_t, struct archive_string_conv *); #endif static int best_effort_strncat_in_locale(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int _utf8_to_unicode(uint32_t *, const char *, size_t); static int utf8_to_unicode(uint32_t *, const char *, size_t); static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t); static int cesu8_to_unicode(uint32_t *, const char *, size_t); static size_t unicode_to_utf8(char *, size_t, uint32_t); static int utf16_to_unicode(uint32_t *, const char *, size_t, int); static size_t unicode_to_utf16be(char *, size_t, uint32_t); static size_t unicode_to_utf16le(char *, size_t, uint32_t); static int strncat_from_utf8_libarchive2(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int strncat_from_utf8_to_utf8(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int archive_string_normalize_C(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int archive_string_normalize_D(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int archive_string_append_unicode(struct archive_string *, const void *, size_t, struct archive_string_conv *); static struct archive_string * archive_string_append(struct archive_string *as, const char *p, size_t s) { if (archive_string_ensure(as, as->length + s + 1) == NULL) return (NULL); memmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); } static struct archive_wstring * archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) { if (archive_wstring_ensure(as, as->length + s + 1) == NULL) return (NULL); wmemmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); } void archive_string_concat(struct archive_string *dest, struct archive_string *src) { if (archive_string_append(dest, src->s, src->length) == NULL) __archive_errx(1, "Out of memory"); } void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src) { if (archive_wstring_append(dest, src->s, src->length) == NULL) __archive_errx(1, "Out of memory"); } void archive_string_free(struct archive_string *as) { as->length = 0; as->buffer_length = 0; free(as->s); as->s = NULL; } void archive_wstring_free(struct archive_wstring *as) { as->length = 0; as->buffer_length = 0; free(as->s); as->s = NULL; } struct archive_wstring * archive_wstring_ensure(struct archive_wstring *as, size_t s) { return (struct archive_wstring *) archive_string_ensure((struct archive_string *)as, s * sizeof(wchar_t)); } /* Returns NULL on any allocation failure. */ struct archive_string * archive_string_ensure(struct archive_string *as, size_t s) { char *p; size_t new_length; /* If buffer is already big enough, don't reallocate. */ if (as->s && (s <= as->buffer_length)) return (as); /* * Growing the buffer at least exponentially ensures that * append operations are always linear in the number of * characters appended. Using a smaller growth rate for * larger buffers reduces memory waste somewhat at the cost of * a larger constant factor. */ if (as->buffer_length < 32) /* Start with a minimum 32-character buffer. */ new_length = 32; else if (as->buffer_length < 8192) /* Buffers under 8k are doubled for speed. */ new_length = as->buffer_length + as->buffer_length; else { /* Buffers 8k and over grow by at least 25% each time. */ new_length = as->buffer_length + as->buffer_length / 4; /* Be safe: If size wraps, fail. */ if (new_length < as->buffer_length) { /* On failure, wipe the string and return NULL. */ archive_string_free(as); errno = ENOMEM;/* Make sure errno has ENOMEM. */ return (NULL); } } /* * The computation above is a lower limit to how much we'll * grow the buffer. In any case, we have to grow it enough to * hold the request. */ if (new_length < s) new_length = s; /* Now we can reallocate the buffer. */ p = (char *)realloc(as->s, new_length); if (p == NULL) { /* On failure, wipe the string and return NULL. */ archive_string_free(as); errno = ENOMEM;/* Make sure errno has ENOMEM. */ return (NULL); } as->s = p; as->buffer_length = new_length; return (as); } /* * TODO: See if there's a way to avoid scanning * the source string twice. Then test to see * if it actually helps (remember that we're almost * always called with pretty short arguments, so * such an optimization might not help). */ struct archive_string * archive_strncat(struct archive_string *as, const void *_p, size_t n) { size_t s; const char *p, *pp; p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (s < n && *pp) { pp++; s++; } if ((as = archive_string_append(as, p, s)) == NULL) __archive_errx(1, "Out of memory"); return (as); } struct archive_wstring * archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n) { size_t s; const wchar_t *pp; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (s < n && *pp) { pp++; s++; } if ((as = archive_wstring_append(as, p, s)) == NULL) __archive_errx(1, "Out of memory"); return (as); } struct archive_string * archive_strcat(struct archive_string *as, const void *p) { /* strcat is just strncat without an effective limit. * Assert that we'll never get called with a source * string over 16MB. * TODO: Review all uses of strcat in the source * and try to replace them with strncat(). */ return archive_strncat(as, p, 0x1000000); } struct archive_wstring * archive_wstrcat(struct archive_wstring *as, const wchar_t *p) { /* Ditto. */ return archive_wstrncat(as, p, 0x1000000); } struct archive_string * archive_strappend_char(struct archive_string *as, char c) { if ((as = archive_string_append(as, &c, 1)) == NULL) __archive_errx(1, "Out of memory"); return (as); } struct archive_wstring * archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c) { if ((as = archive_wstring_append(as, &c, 1)) == NULL) __archive_errx(1, "Out of memory"); return (as); } /* * Get the "current character set" name to use with iconv. * On FreeBSD, the empty character set name "" chooses * the correct character encoding for the current locale, * so this isn't necessary. * But iconv on Mac OS 10.6 doesn't seem to handle this correctly; * on that system, we have to explicitly call nl_langinfo() * to get the right name. Not sure about other platforms. * * NOTE: GNU libiconv does not recognize the character-set name * which some platform nl_langinfo(CODESET) returns, so we should * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv. */ static const char * default_iconv_charset(const char *charset) { if (charset != NULL && charset[0] != '\0') return charset; #if HAVE_LOCALE_CHARSET && !defined(__APPLE__) /* locale_charset() is broken on Mac OS */ return locale_charset(); #elif HAVE_NL_LANGINFO return nl_langinfo(CODESET); #else return ""; #endif } #if defined(_WIN32) && !defined(__CYGWIN__) /* * Convert MBS to WCS. * Note: returns -1 if conversion fails. */ int archive_wstring_append_from_mbs(struct archive_wstring *dest, const char *p, size_t len) { return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL); } static int archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, const char *s, size_t length, struct archive_string_conv *sc) { int count, ret = 0; UINT from_cp; if (sc != NULL) from_cp = sc->from_cp; else from_cp = get_current_codepage(); if (from_cp == CP_C_LOCALE) { /* * "C" locale special process. */ wchar_t *ws; const unsigned char *mp; if (NULL == archive_wstring_ensure(dest, dest->length + length + 1)) return (-1); ws = dest->s + dest->length; mp = (const unsigned char *)s; count = 0; while (count < (int)length && *mp) { *ws++ = (wchar_t)*mp++; count++; } } else if (sc != NULL && (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) { /* * Normalize UTF-8 and UTF-16BE and convert it directly * to UTF-16 as wchar_t. */ struct archive_string u16; int saved_flag = sc->flag;/* save current flag. */ if (is_big_endian()) sc->flag |= SCONV_TO_UTF16BE; else sc->flag |= SCONV_TO_UTF16LE; if (sc->flag & SCONV_FROM_UTF16) { /* * UTF-16BE/LE NFD ===> UTF-16 NFC * UTF-16BE/LE NFC ===> UTF-16 NFD */ count = (int)utf16nbytes(s, length); } else { /* * UTF-8 NFD ===> UTF-16 NFC * UTF-8 NFC ===> UTF-16 NFD */ count = (int)mbsnbytes(s, length); } u16.s = (char *)dest->s; u16.length = dest->length << 1;; u16.buffer_length = dest->buffer_length; if (sc->flag & SCONV_NORMALIZATION_C) ret = archive_string_normalize_C(&u16, s, count, sc); else ret = archive_string_normalize_D(&u16, s, count, sc); dest->s = (wchar_t *)u16.s; dest->length = u16.length >> 1; dest->buffer_length = u16.buffer_length; sc->flag = saved_flag;/* restore the saved flag. */ return (ret); } else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) { count = (int)utf16nbytes(s, length); count >>= 1; /* to be WCS length */ /* Allocate memory for WCS. */ if (NULL == archive_wstring_ensure(dest, dest->length + count + 1)) return (-1); wmemcpy(dest->s + dest->length, (const wchar_t *)s, count); if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) { uint16_t *u16 = (uint16_t *)(dest->s + dest->length); int b; for (b = 0; b < count; b++) { uint16_t val = archive_le16dec(u16+b); archive_be16enc(u16+b, val); } } else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) { uint16_t *u16 = (uint16_t *)(dest->s + dest->length); int b; for (b = 0; b < count; b++) { uint16_t val = archive_be16dec(u16+b); archive_le16enc(u16+b, val); } } } else { DWORD mbflag; size_t buffsize; if (sc == NULL) mbflag = 0; else if (sc->flag & SCONV_FROM_CHARSET) { /* Do not trust the length which comes from * an archive file. */ length = mbsnbytes(s, length); mbflag = 0; } else mbflag = MB_PRECOMPOSED; buffsize = dest->length + length + 1; do { /* Allocate memory for WCS. */ if (NULL == archive_wstring_ensure(dest, buffsize)) return (-1); /* Convert MBS to WCS. */ count = MultiByteToWideChar(from_cp, mbflag, s, (int)length, dest->s + dest->length, (int)(dest->buffer_length >> 1) -1); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the WCS buffer. */ buffsize = dest->buffer_length << 1; continue; } if (count == 0 && length != 0) ret = -1; break; } while (1); } dest->length += count; dest->s[dest->length] = L'\0'; return (ret); } #else /* * Convert MBS to WCS. * Note: returns -1 if conversion fails. */ int archive_wstring_append_from_mbs(struct archive_wstring *dest, const char *p, size_t len) { size_t r; int ret_val = 0; /* * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ size_t wcs_length = len; size_t mbs_length = len; const char *mbs = p; wchar_t *wcs; #if HAVE_MBRTOWC mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #endif if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1)) return (-1); wcs = dest->s + dest->length; /* * We cannot use mbsrtowcs/mbstowcs here because those may convert * extra MBS when strlen(p) > len and one wide character consis of * multi bytes. */ while (*mbs && mbs_length > 0) { if (wcs_length == 0) { dest->length = wcs - dest->s; dest->s[dest->length] = L'\0'; wcs_length = mbs_length; if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1)) return (-1); wcs = dest->s + dest->length; } #if HAVE_MBRTOWC r = mbrtowc(wcs, mbs, wcs_length, &shift_state); #else r = mbtowc(wcs, mbs, wcs_length); #endif if (r == (size_t)-1 || r == (size_t)-2) { ret_val = -1; if (errno == EILSEQ) { ++mbs; --mbs_length; continue; } else break; } if (r == 0 || r > mbs_length) break; wcs++; wcs_length--; mbs += r; mbs_length -= r; } dest->length = wcs - dest->s; dest->s[dest->length] = L'\0'; return (ret_val); } #endif #if defined(_WIN32) && !defined(__CYGWIN__) /* * WCS ==> MBS. * Note: returns -1 if conversion fails. * * Win32 builds use WideCharToMultiByte from the Windows API. * (Maybe Cygwin should too? WideCharToMultiByte will know a * lot more about local character encodings than the wcrtomb() * wrapper is going to know.) */ int archive_string_append_from_wcs(struct archive_string *as, const wchar_t *w, size_t len) { return archive_string_append_from_wcs_in_codepage(as, w, len, NULL); } static int archive_string_append_from_wcs_in_codepage(struct archive_string *as, const wchar_t *ws, size_t len, struct archive_string_conv *sc) { BOOL defchar_used, *dp; int count, ret = 0; UINT to_cp; int wslen = (int)len; if (sc != NULL) to_cp = sc->to_cp; else to_cp = get_current_codepage(); if (to_cp == CP_C_LOCALE) { /* * "C" locale special process. */ const wchar_t *wp = ws; char *p; if (NULL == archive_string_ensure(as, as->length + wslen +1)) return (-1); p = as->s + as->length; count = 0; defchar_used = 0; while (count < wslen && *wp) { if (*wp > 255) { *p++ = '?'; wp++; defchar_used = 1; } else *p++ = (char)*wp++; count++; } } else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) { uint16_t *u16; if (NULL == archive_string_ensure(as, as->length + len * 2 + 2)) return (-1); u16 = (uint16_t *)(as->s + as->length); count = 0; defchar_used = 0; if (sc->flag & SCONV_TO_UTF16BE) { while (count < (int)len && *ws) { archive_be16enc(u16+count, *ws); ws++; count++; } } else { while (count < (int)len && *ws) { archive_le16enc(u16+count, *ws); ws++; count++; } } count <<= 1; /* to be byte size */ } else { /* Make sure the MBS buffer has plenty to set. */ if (NULL == archive_string_ensure(as, as->length + len * 2 + 1)) return (-1); do { defchar_used = 0; if (to_cp == CP_UTF8 || sc == NULL) dp = NULL; else dp = &defchar_used; count = WideCharToMultiByte(to_cp, 0, ws, wslen, as->s + as->length, (int)as->buffer_length-1, NULL, dp); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the MBS buffer and retry. */ if (NULL == archive_string_ensure(as, as->buffer_length + len)) return (-1); continue; } if (count == 0) ret = -1; break; } while (1); } as->length += count; as->s[as->length] = '\0'; return (defchar_used?-1:ret); } #elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB) /* * Translates a wide character string into current locale character set * and appends to the archive_string. Note: returns -1 if conversion * fails. */ int archive_string_append_from_wcs(struct archive_string *as, const wchar_t *w, size_t len) { /* We cannot use the standard wcstombs() here because it * cannot tell us how big the output buffer should be. So * I've built a loop around wcrtomb() or wctomb() that * converts a character at a time and resizes the string as * needed. We prefer wcrtomb() when it's available because * it's thread-safe. */ int n, ret_val = 0; char *p; char *end; #if HAVE_WCRTOMB mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ wctomb(NULL, L'\0'); #endif /* * Allocate buffer for MBS. * We need this allocation here since it is possible that * as->s is still NULL. */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; while (*w != L'\0' && len > 0) { if (p >= end) { as->length = p - as->s; as->s[as->length] = '\0'; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, as->length + len * 2 + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; } #if HAVE_WCRTOMB n = wcrtomb(p, *w++, &shift_state); #else n = wctomb(p, *w++); #endif if (n == -1) { if (errno == EILSEQ) { /* Skip an illegal wide char. */ *p++ = '?'; ret_val = -1; } else { ret_val = -1; break; } } else p += n; len--; } as->length = p - as->s; as->s[as->length] = '\0'; return (ret_val); } #else /* HAVE_WCTOMB || HAVE_WCRTOMB */ /* * TODO: Test if __STDC_ISO_10646__ is defined. * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion * one character at a time. If a non-Windows platform doesn't have * either of these, fall back to the built-in UTF8 conversion. */ int archive_string_append_from_wcs(struct archive_string *as, const wchar_t *w, size_t len) { (void)as;/* UNUSED */ (void)w;/* UNUSED */ (void)len;/* UNUSED */ errno = ENOSYS; return (-1); } #endif /* HAVE_WCTOMB || HAVE_WCRTOMB */ /* * Find a string conversion object by a pair of 'from' charset name * and 'to' charset name from an archive object. * Return NULL if not found. */ static struct archive_string_conv * find_sconv_object(struct archive *a, const char *fc, const char *tc) { struct archive_string_conv *sc; if (a == NULL) return (NULL); for (sc = a->sconv; sc != NULL; sc = sc->next) { if (strcmp(sc->from_charset, fc) == 0 && strcmp(sc->to_charset, tc) == 0) break; } return (sc); } /* * Register a string object to an archive object. */ static void add_sconv_object(struct archive *a, struct archive_string_conv *sc) { struct archive_string_conv **psc; /* Add a new sconv to sconv list. */ psc = &(a->sconv); while (*psc != NULL) psc = &((*psc)->next); *psc = sc; } static void add_converter(struct archive_string_conv *sc, int (*converter) (struct archive_string *, const void *, size_t, struct archive_string_conv *)) { if (sc == NULL || sc->nconverter >= 2) __archive_errx(1, "Programing error"); sc->converter[sc->nconverter++] = converter; } static void setup_converter(struct archive_string_conv *sc) { /* Reset. */ sc->nconverter = 0; /* * Perform special sequence for the incorrect UTF-8 filenames * made by libarchive2.x. */ if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) { add_converter(sc, strncat_from_utf8_libarchive2); return; } /* * Convert a string to UTF-16BE/LE. */ if (sc->flag & SCONV_TO_UTF16) { /* * If the current locale is UTF-8, we can translate * a UTF-8 string into a UTF-16BE string. */ if (sc->flag & SCONV_FROM_UTF8) { add_converter(sc, archive_string_append_unicode); return; } #if defined(_WIN32) && !defined(__CYGWIN__) if (sc->flag & SCONV_WIN_CP) { if (sc->flag & SCONV_TO_UTF16BE) add_converter(sc, win_strncat_to_utf16be); else add_converter(sc, win_strncat_to_utf16le); return; } #endif #if defined(HAVE_ICONV) if (sc->cd != (iconv_t)-1) { add_converter(sc, iconv_strncat_in_locale); return; } #endif if (sc->flag & SCONV_BEST_EFFORT) { if (sc->flag & SCONV_TO_UTF16BE) add_converter(sc, best_effort_strncat_to_utf16be); else add_converter(sc, best_effort_strncat_to_utf16le); } else /* Make sure we have no converter. */ sc->nconverter = 0; return; } /* * Convert a string from UTF-16BE/LE. */ if (sc->flag & SCONV_FROM_UTF16) { /* * At least we should normalize a UTF-16BE string. */ if (sc->flag & SCONV_NORMALIZATION_D) add_converter(sc,archive_string_normalize_D); else if (sc->flag & SCONV_NORMALIZATION_C) add_converter(sc, archive_string_normalize_C); if (sc->flag & SCONV_TO_UTF8) { /* * If the current locale is UTF-8, we can translate * a UTF-16BE/LE string into a UTF-8 string directly. */ if (!(sc->flag & (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C))) add_converter(sc, archive_string_append_unicode); return; } #if defined(_WIN32) && !defined(__CYGWIN__) if (sc->flag & SCONV_WIN_CP) { if (sc->flag & SCONV_FROM_UTF16BE) add_converter(sc, win_strncat_from_utf16be); else add_converter(sc, win_strncat_from_utf16le); return; } #endif #if defined(HAVE_ICONV) if (sc->cd != (iconv_t)-1) { add_converter(sc, iconv_strncat_in_locale); return; } #endif if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE)) == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE)) add_converter(sc, best_effort_strncat_from_utf16be); else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE)) == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE)) add_converter(sc, best_effort_strncat_from_utf16le); else /* Make sure we have no converter. */ sc->nconverter = 0; return; } if (sc->flag & SCONV_FROM_UTF8) { /* * At least we should normalize a UTF-8 string. */ if (sc->flag & SCONV_NORMALIZATION_D) add_converter(sc,archive_string_normalize_D); else if (sc->flag & SCONV_NORMALIZATION_C) add_converter(sc, archive_string_normalize_C); /* * Copy UTF-8 string with a check of CESU-8. * Apparently, iconv does not check surrogate pairs in UTF-8 * when both from-charset and to-charset are UTF-8, and then * we use our UTF-8 copy code. */ if (sc->flag & SCONV_TO_UTF8) { /* * If the current locale is UTF-8, we can translate * a UTF-16BE string into a UTF-8 string directly. */ if (!(sc->flag & (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C))) add_converter(sc, strncat_from_utf8_to_utf8); return; } } #if defined(_WIN32) && !defined(__CYGWIN__) /* * On Windows we can use Windows API for a string conversion. */ if (sc->flag & SCONV_WIN_CP) { add_converter(sc, strncat_in_codepage); return; } #endif #if HAVE_ICONV if (sc->cd != (iconv_t)-1) { add_converter(sc, iconv_strncat_in_locale); /* * iconv generally does not support UTF-8-MAC and so * we have to the output of iconv from NFC to NFD if * need. */ if ((sc->flag & SCONV_FROM_CHARSET) && (sc->flag & SCONV_TO_UTF8)) { if (sc->flag & SCONV_NORMALIZATION_D) add_converter(sc, archive_string_normalize_D); } return; } #endif /* * Try conversion in the best effort or no conversion. */ if ((sc->flag & SCONV_BEST_EFFORT) || sc->same) add_converter(sc, best_effort_strncat_in_locale); else /* Make sure we have no converter. */ sc->nconverter = 0; } /* * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE * and CP932 which are referenced in create_sconv_object(). */ static const char * canonical_charset_name(const char *charset) { char cs[16]; char *p; const char *s; if (charset == NULL || charset[0] == '\0' || strlen(charset) > 15) return (charset); /* Copy name to uppercase. */ p = cs; s = charset; while (*s) { char c = *s++; if (c >= 'a' && c <= 'z') c -= 'a' - 'A'; *p++ = c; } *p++ = '\0'; if (strcmp(cs, "UTF-8") == 0 || strcmp(cs, "UTF8") == 0) return ("UTF-8"); if (strcmp(cs, "UTF-16BE") == 0 || strcmp(cs, "UTF16BE") == 0) return ("UTF-16BE"); if (strcmp(cs, "UTF-16LE") == 0 || strcmp(cs, "UTF16LE") == 0) return ("UTF-16LE"); if (strcmp(cs, "CP932") == 0) return ("CP932"); return (charset); } /* * Create a string conversion object. */ static struct archive_string_conv * create_sconv_object(const char *fc, const char *tc, unsigned current_codepage, int flag) { struct archive_string_conv *sc; sc = calloc(1, sizeof(*sc)); if (sc == NULL) return (NULL); sc->next = NULL; sc->from_charset = strdup(fc); if (sc->from_charset == NULL) { free(sc); return (NULL); } sc->to_charset = strdup(tc); if (sc->to_charset == NULL) { free(sc->from_charset); free(sc); return (NULL); } archive_string_init(&sc->utftmp); if (flag & SCONV_TO_CHARSET) { /* * Convert characters from the current locale charset to * a specified charset. */ sc->from_cp = current_codepage; sc->to_cp = make_codepage_from_charset(tc); #if defined(_WIN32) && !defined(__CYGWIN__) if (IsValidCodePage(sc->to_cp)) flag |= SCONV_WIN_CP; #endif } else if (flag & SCONV_FROM_CHARSET) { /* * Convert characters from a specified charset to * the current locale charset. */ sc->to_cp = current_codepage; sc->from_cp = make_codepage_from_charset(fc); #if defined(_WIN32) && !defined(__CYGWIN__) if (IsValidCodePage(sc->from_cp)) flag |= SCONV_WIN_CP; #endif } /* * Check if "from charset" and "to charset" are the same. */ if (strcmp(fc, tc) == 0 || (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp)) sc->same = 1; else sc->same = 0; /* * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE. */ if (strcmp(tc, "UTF-8") == 0) flag |= SCONV_TO_UTF8; else if (strcmp(tc, "UTF-16BE") == 0) flag |= SCONV_TO_UTF16BE; else if (strcmp(tc, "UTF-16LE") == 0) flag |= SCONV_TO_UTF16LE; if (strcmp(fc, "UTF-8") == 0) flag |= SCONV_FROM_UTF8; else if (strcmp(fc, "UTF-16BE") == 0) flag |= SCONV_FROM_UTF16BE; else if (strcmp(fc, "UTF-16LE") == 0) flag |= SCONV_FROM_UTF16LE; #if defined(_WIN32) && !defined(__CYGWIN__) if (sc->to_cp == CP_UTF8) flag |= SCONV_TO_UTF8; else if (sc->to_cp == CP_UTF16BE) flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP; else if (sc->to_cp == CP_UTF16LE) flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP; if (sc->from_cp == CP_UTF8) flag |= SCONV_FROM_UTF8; else if (sc->from_cp == CP_UTF16BE) flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP; else if (sc->from_cp == CP_UTF16LE) flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP; #endif /* * Set a flag for Unicode NFD. Usually iconv cannot correctly * handle it. So we have to translate NFD characters to NFC ones * ourselves before iconv handles. Another reason is to prevent * that the same sight of two filenames, one is NFC and other * is NFD, would be in its directory. * On Mac OS X, although its filesystem layer automatically * convert filenames to NFD, it would be useful for filename * comparing to find out the same filenames that we normalize * that to be NFD ourselves. */ if ((flag & SCONV_FROM_CHARSET) && (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) { #if defined(__APPLE__) if (flag & SCONV_TO_UTF8) flag |= SCONV_NORMALIZATION_D; else #endif flag |= SCONV_NORMALIZATION_C; } #if defined(__APPLE__) /* * In case writing an archive file, make sure that a filename * going to be passed to iconv is a Unicode NFC string since * a filename in HFS Plus filesystem is a Unicode NFD one and * iconv cannot handle it with "UTF-8" charset. It is simpler * than a use of "UTF-8-MAC" charset. */ if ((flag & SCONV_TO_CHARSET) && (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8))) flag |= SCONV_NORMALIZATION_C; /* * In case reading an archive file. make sure that a filename * will be passed to users is a Unicode NFD string in order to * correctly compare the filename with other one which comes * from HFS Plus filesystem. */ if ((flag & SCONV_FROM_CHARSET) && !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && (flag & SCONV_TO_UTF8)) flag |= SCONV_NORMALIZATION_D; #endif #if defined(HAVE_ICONV) sc->cd_w = (iconv_t)-1; /* * Create an iconv object. */ if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) && (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) || (flag & SCONV_WIN_CP)) { /* This case we won't use iconv. */ sc->cd = (iconv_t)-1; } else { sc->cd = iconv_open(tc, fc); if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) { /* * Unfortunaly, all of iconv implements do support * "CP932" character-set, so we should use "SJIS" * instead if iconv_open failed. */ if (strcmp(tc, "CP932") == 0) sc->cd = iconv_open("SJIS", fc); else if (strcmp(fc, "CP932") == 0) sc->cd = iconv_open(tc, "SJIS"); } #if defined(_WIN32) && !defined(__CYGWIN__) /* * archive_mstring on Windows directly convert multi-bytes * into archive_wstring in order not to depend on locale * so that you can do a I18N programing. This will be * used only in archive_mstring_copy_mbs_len_l so far. */ if (flag & SCONV_FROM_CHARSET) { sc->cd_w = iconv_open("UTF-8", fc); if (sc->cd_w == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) { if (strcmp(fc, "CP932") == 0) sc->cd_w = iconv_open("UTF-8", "SJIS"); } } #endif /* _WIN32 && !__CYGWIN__ */ } #endif /* HAVE_ICONV */ sc->flag = flag; /* * Set up converters. */ setup_converter(sc); return (sc); } /* * Free a string conversion object. */ static void free_sconv_object(struct archive_string_conv *sc) { free(sc->from_charset); free(sc->to_charset); archive_string_free(&sc->utftmp); #if HAVE_ICONV if (sc->cd != (iconv_t)-1) iconv_close(sc->cd); if (sc->cd_w != (iconv_t)-1) iconv_close(sc->cd_w); #endif free(sc); } #if defined(_WIN32) && !defined(__CYGWIN__) static unsigned my_atoi(const char *p) { unsigned cp; cp = 0; while (*p) { if (*p >= '0' && *p <= '9') cp = cp * 10 + (*p - '0'); else return (-1); p++; } return (cp); } /* * Translate Charset name (as used by iconv) into CodePage (as used by Windows) * Return -1 if failed. * * Note: This translation code may be insufficient. */ static struct charset { const char *name; unsigned cp; } charsets[] = { /* MUST BE SORTED! */ {"ASCII", 1252}, {"ASMO-708", 708}, {"BIG5", 950}, {"CHINESE", 936}, {"CP367", 1252}, {"CP819", 1252}, {"CP1025", 21025}, {"DOS-720", 720}, {"DOS-862", 862}, {"EUC-CN", 51936}, {"EUC-JP", 51932}, {"EUC-KR", 949}, {"EUCCN", 51936}, {"EUCJP", 51932}, {"EUCKR", 949}, {"GB18030", 54936}, {"GB2312", 936}, {"HEBREW", 1255}, {"HZ-GB-2312", 52936}, {"IBM273", 20273}, {"IBM277", 20277}, {"IBM278", 20278}, {"IBM280", 20280}, {"IBM284", 20284}, {"IBM285", 20285}, {"IBM290", 20290}, {"IBM297", 20297}, {"IBM367", 1252}, {"IBM420", 20420}, {"IBM423", 20423}, {"IBM424", 20424}, {"IBM819", 1252}, {"IBM871", 20871}, {"IBM880", 20880}, {"IBM905", 20905}, {"IBM924", 20924}, {"ISO-8859-1", 28591}, {"ISO-8859-13", 28603}, {"ISO-8859-15", 28605}, {"ISO-8859-2", 28592}, {"ISO-8859-3", 28593}, {"ISO-8859-4", 28594}, {"ISO-8859-5", 28595}, {"ISO-8859-6", 28596}, {"ISO-8859-7", 28597}, {"ISO-8859-8", 28598}, {"ISO-8859-9", 28599}, {"ISO8859-1", 28591}, {"ISO8859-13", 28603}, {"ISO8859-15", 28605}, {"ISO8859-2", 28592}, {"ISO8859-3", 28593}, {"ISO8859-4", 28594}, {"ISO8859-5", 28595}, {"ISO8859-6", 28596}, {"ISO8859-7", 28597}, {"ISO8859-8", 28598}, {"ISO8859-9", 28599}, {"JOHAB", 1361}, {"KOI8-R", 20866}, {"KOI8-U", 21866}, {"KS_C_5601-1987", 949}, {"LATIN1", 1252}, {"LATIN2", 28592}, {"MACINTOSH", 10000}, {"SHIFT-JIS", 932}, {"SHIFT_JIS", 932}, {"SJIS", 932}, {"US", 1252}, {"US-ASCII", 1252}, {"UTF-16", 1200}, {"UTF-16BE", 1201}, {"UTF-16LE", 1200}, {"UTF-8", CP_UTF8}, {"X-EUROPA", 29001}, {"X-MAC-ARABIC", 10004}, {"X-MAC-CE", 10029}, {"X-MAC-CHINESEIMP", 10008}, {"X-MAC-CHINESETRAD", 10002}, {"X-MAC-CROATIAN", 10082}, {"X-MAC-CYRILLIC", 10007}, {"X-MAC-GREEK", 10006}, {"X-MAC-HEBREW", 10005}, {"X-MAC-ICELANDIC", 10079}, {"X-MAC-JAPANESE", 10001}, {"X-MAC-KOREAN", 10003}, {"X-MAC-ROMANIAN", 10010}, {"X-MAC-THAI", 10021}, {"X-MAC-TURKISH", 10081}, {"X-MAC-UKRAINIAN", 10017}, }; static unsigned make_codepage_from_charset(const char *charset) { char cs[16]; char *p; unsigned cp; int a, b; if (charset == NULL || strlen(charset) > 15) return -1; /* Copy name to uppercase. */ p = cs; while (*charset) { char c = *charset++; if (c >= 'a' && c <= 'z') c -= 'a' - 'A'; *p++ = c; } *p++ = '\0'; cp = -1; /* Look it up in the table first, so that we can easily * override CP367, which we map to 1252 instead of 367. */ a = 0; b = sizeof(charsets)/sizeof(charsets[0]); while (b > a) { int c = (b + a) / 2; int r = strcmp(charsets[c].name, cs); if (r < 0) a = c + 1; else if (r > 0) b = c; else return charsets[c].cp; } /* If it's not in the table, try to parse it. */ switch (*cs) { case 'C': if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') { cp = my_atoi(cs + 2); } else if (strcmp(cs, "CP_ACP") == 0) cp = get_current_codepage(); else if (strcmp(cs, "CP_OEMCP") == 0) cp = get_current_oemcp(); break; case 'I': if (cs[1] == 'B' && cs[2] == 'M' && cs[3] >= '0' && cs[3] <= '9') { cp = my_atoi(cs + 3); } break; case 'W': if (strncmp(cs, "WINDOWS-", 8) == 0) { cp = my_atoi(cs + 8); if (cp != 874 && (cp < 1250 || cp > 1258)) cp = -1;/* This may invalid code. */ } break; } return (cp); } /* * Return ANSI Code Page of current locale set by setlocale(). */ static unsigned get_current_codepage(void) { char *locale, *p; unsigned cp; locale = setlocale(LC_CTYPE, NULL); if (locale == NULL) return (GetACP()); if (locale[0] == 'C' && locale[1] == '\0') return (CP_C_LOCALE); p = strrchr(locale, '.'); if (p == NULL) return (GetACP()); cp = my_atoi(p+1); if (cp <= 0) return (GetACP()); return (cp); } /* * Translation table between Locale Name and ACP/OEMCP. */ static struct { unsigned acp; unsigned ocp; const char *locale; } acp_ocp_map[] = { { 950, 950, "Chinese_Taiwan" }, { 936, 936, "Chinese_People's Republic of China" }, { 950, 950, "Chinese_Taiwan" }, { 1250, 852, "Czech_Czech Republic" }, { 1252, 850, "Danish_Denmark" }, { 1252, 850, "Dutch_Netherlands" }, { 1252, 850, "Dutch_Belgium" }, { 1252, 437, "English_United States" }, { 1252, 850, "English_Australia" }, { 1252, 850, "English_Canada" }, { 1252, 850, "English_New Zealand" }, { 1252, 850, "English_United Kingdom" }, { 1252, 437, "English_United States" }, { 1252, 850, "Finnish_Finland" }, { 1252, 850, "French_France" }, { 1252, 850, "French_Belgium" }, { 1252, 850, "French_Canada" }, { 1252, 850, "French_Switzerland" }, { 1252, 850, "German_Germany" }, { 1252, 850, "German_Austria" }, { 1252, 850, "German_Switzerland" }, { 1253, 737, "Greek_Greece" }, { 1250, 852, "Hungarian_Hungary" }, { 1252, 850, "Icelandic_Iceland" }, { 1252, 850, "Italian_Italy" }, { 1252, 850, "Italian_Switzerland" }, { 932, 932, "Japanese_Japan" }, { 949, 949, "Korean_Korea" }, { 1252, 850, "Norwegian (BokmOl)_Norway" }, { 1252, 850, "Norwegian (BokmOl)_Norway" }, { 1252, 850, "Norwegian-Nynorsk_Norway" }, { 1250, 852, "Polish_Poland" }, { 1252, 850, "Portuguese_Portugal" }, { 1252, 850, "Portuguese_Brazil" }, { 1251, 866, "Russian_Russia" }, { 1250, 852, "Slovak_Slovakia" }, { 1252, 850, "Spanish_Spain" }, { 1252, 850, "Spanish_Mexico" }, { 1252, 850, "Spanish_Spain" }, { 1252, 850, "Swedish_Sweden" }, { 1254, 857, "Turkish_Turkey" }, { 0, 0, NULL} }; /* * Return OEM Code Page of current locale set by setlocale(). */ static unsigned get_current_oemcp(void) { int i; char *locale, *p; size_t len; locale = setlocale(LC_CTYPE, NULL); if (locale == NULL) return (GetOEMCP()); if (locale[0] == 'C' && locale[1] == '\0') return (CP_C_LOCALE); p = strrchr(locale, '.'); if (p == NULL) return (GetOEMCP()); len = p - locale; for (i = 0; acp_ocp_map[i].acp; i++) { if (strncmp(acp_ocp_map[i].locale, locale, len) == 0) return (acp_ocp_map[i].ocp); } return (GetOEMCP()); } #else /* * POSIX platform does not use CodePage. */ static unsigned get_current_codepage(void) { return (-1);/* Unknown */ } static unsigned make_codepage_from_charset(const char *charset) { (void)charset; /* UNUSED */ return (-1);/* Unknown */ } static unsigned get_current_oemcp(void) { return (-1);/* Unknown */ } #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ /* * Return a string conversion object. */ static struct archive_string_conv * get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag) { struct archive_string_conv *sc; unsigned current_codepage; /* Check if we have made the sconv object. */ sc = find_sconv_object(a, fc, tc); if (sc != NULL) return (sc); if (a == NULL) current_codepage = get_current_codepage(); else current_codepage = a->current_codepage; sc = create_sconv_object(canonical_charset_name(fc), canonical_charset_name(tc), current_codepage, flag); if (sc == NULL) { if (a != NULL) archive_set_error(a, ENOMEM, "Could not allocate memory for " "a string conversion object"); return (NULL); } /* * If there is no converter for current string conversion object, * we cannot handle this conversion. */ if (sc->nconverter == 0) { if (a != NULL) { #if HAVE_ICONV archive_set_error(a, ARCHIVE_ERRNO_MISC, "iconv_open failed : Cannot handle ``%s''", (flag & SCONV_TO_CHARSET)?tc:fc); #else archive_set_error(a, ARCHIVE_ERRNO_MISC, "A character-set conversion not fully supported " "on this platform"); #endif } /* Failed; free a sconv object. */ free_sconv_object(sc); return (NULL); } /* * Success! */ if (a != NULL) add_sconv_object(a, sc); return (sc); } static const char * get_current_charset(struct archive *a) { const char *cur_charset; if (a == NULL) cur_charset = default_iconv_charset(""); else { cur_charset = default_iconv_charset(a->current_code); if (a->current_code == NULL) { a->current_code = strdup(cur_charset); a->current_codepage = get_current_codepage(); a->current_oemcp = get_current_oemcp(); } } return (cur_charset); } /* * Make and Return a string conversion object. * Return NULL if the platform does not support the specified conversion * and best_effort is 0. * If best_effort is set, A string conversion object must be returned * unless memory allocation for the object fails, but the conversion * might fail when non-ASCII code is found. */ struct archive_string_conv * archive_string_conversion_to_charset(struct archive *a, const char *charset, int best_effort) { int flag = SCONV_TO_CHARSET; if (best_effort) flag |= SCONV_BEST_EFFORT; return (get_sconv_object(a, get_current_charset(a), charset, flag)); } struct archive_string_conv * archive_string_conversion_from_charset(struct archive *a, const char *charset, int best_effort) { int flag = SCONV_FROM_CHARSET; if (best_effort) flag |= SCONV_BEST_EFFORT; return (get_sconv_object(a, charset, get_current_charset(a), flag)); } /* * archive_string_default_conversion_*_archive() are provided for Windows * platform because other archiver application use CP_OEMCP for * MultiByteToWideChar() and WideCharToMultiByte() for the filenames * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP). * So we should make a string conversion between CP_ACP and CP_OEMCP * for compatibillty. */ #if defined(_WIN32) && !defined(__CYGWIN__) struct archive_string_conv * archive_string_default_conversion_for_read(struct archive *a) { const char *cur_charset = get_current_charset(a); char oemcp[16]; /* NOTE: a check of cur_charset is unneeded but we need * that get_current_charset() has been surely called at * this time whatever C compiler optimized. */ if (cur_charset != NULL && (a->current_codepage == CP_C_LOCALE || a->current_codepage == a->current_oemcp)) return (NULL);/* no conversion. */ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp); /* Make sure a null termination must be set. */ oemcp[sizeof(oemcp)-1] = '\0'; return (get_sconv_object(a, oemcp, cur_charset, SCONV_FROM_CHARSET)); } struct archive_string_conv * archive_string_default_conversion_for_write(struct archive *a) { const char *cur_charset = get_current_charset(a); char oemcp[16]; /* NOTE: a check of cur_charset is unneeded but we need * that get_current_charset() has been surely called at * this time whatever C compiler optimized. */ if (cur_charset != NULL && (a->current_codepage == CP_C_LOCALE || a->current_codepage == a->current_oemcp)) return (NULL);/* no conversion. */ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp); /* Make sure a null termination must be set. */ oemcp[sizeof(oemcp)-1] = '\0'; return (get_sconv_object(a, cur_charset, oemcp, SCONV_TO_CHARSET)); } #else struct archive_string_conv * archive_string_default_conversion_for_read(struct archive *a) { (void)a; /* UNUSED */ return (NULL); } struct archive_string_conv * archive_string_default_conversion_for_write(struct archive *a) { (void)a; /* UNUSED */ return (NULL); } #endif /* * Dispose of all character conversion objects in the archive object. */ void archive_string_conversion_free(struct archive *a) { struct archive_string_conv *sc; struct archive_string_conv *sc_next; for (sc = a->sconv; sc != NULL; sc = sc_next) { sc_next = sc->next; free_sconv_object(sc); } a->sconv = NULL; free(a->current_code); a->current_code = NULL; } /* * Return a conversion charset name. */ const char * archive_string_conversion_charset_name(struct archive_string_conv *sc) { if (sc->flag & SCONV_TO_CHARSET) return (sc->to_charset); else return (sc->from_charset); } /* * Change the behavior of a string conversion. */ void archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt) { switch (opt) { /* * A filename in UTF-8 was made with libarchive 2.x in a wrong * assumption that wchar_t was Unicode. * This option enables simulating the assumption in order to read - * that filname correctly. + * that filename correctly. */ case SCONV_SET_OPT_UTF8_LIBARCHIVE2X: #if (defined(_WIN32) && !defined(__CYGWIN__)) \ || defined(__STDC_ISO_10646__) || defined(__APPLE__) /* * Nothing to do for it since wchar_t on these platforms * is really Unicode. */ (void)sc; /* UNUSED */ #else if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) { sc->flag |= SCONV_UTF8_LIBARCHIVE_2; /* Set up string converters. */ setup_converter(sc); } #endif break; case SCONV_SET_OPT_NORMALIZATION_C: if ((sc->flag & SCONV_NORMALIZATION_C) == 0) { sc->flag |= SCONV_NORMALIZATION_C; sc->flag &= ~SCONV_NORMALIZATION_D; /* Set up string converters. */ setup_converter(sc); } break; case SCONV_SET_OPT_NORMALIZATION_D: #if defined(HAVE_ICONV) /* * If iconv will take the string, do not change the * setting of the normalization. */ if (!(sc->flag & SCONV_WIN_CP) && (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8))) break; #endif if ((sc->flag & SCONV_NORMALIZATION_D) == 0) { sc->flag |= SCONV_NORMALIZATION_D; sc->flag &= ~SCONV_NORMALIZATION_C; /* Set up string converters. */ setup_converter(sc); } break; default: break; } } /* * * Copy one archive_string to another in locale conversion. * * archive_strncat_l(); * archive_strncpy_l(); * */ static size_t mbsnbytes(const void *_p, size_t n) { size_t s; const char *p, *pp; if (_p == NULL) return (0); p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (s < n && *pp) { pp++; s++; } return (s); } static size_t utf16nbytes(const void *_p, size_t n) { size_t s; const char *p, *pp; if (_p == NULL) return (0); p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; n >>= 1; while (s < n && (pp[0] || pp[1])) { pp += 2; s++; } return (s<<1); } int archive_strncpy_l(struct archive_string *as, const void *_p, size_t n, struct archive_string_conv *sc) { as->length = 0; return (archive_strncat_l(as, _p, n, sc)); } int archive_strncat_l(struct archive_string *as, const void *_p, size_t n, struct archive_string_conv *sc) { const void *s; - size_t length; + size_t length = 0; int i, r = 0, r2; + if (_p != NULL && n > 0) { + if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) + length = utf16nbytes(_p, n); + else + length = mbsnbytes(_p, n); + } + /* We must allocate memory even if there is no data for conversion * or copy. This simulates archive_string_append behavior. */ - if (_p == NULL || n == 0) { + if (length == 0) { int tn = 1; if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) tn = 2; if (archive_string_ensure(as, as->length + tn) == NULL) return (-1); as->s[as->length] = 0; if (tn == 2) as->s[as->length+1] = 0; return (0); } /* * If sc is NULL, we just make a copy. */ if (sc == NULL) { - length = mbsnbytes(_p, n); if (archive_string_append(as, _p, length) == NULL) return (-1);/* No memory */ return (0); } - if (sc->flag & SCONV_FROM_UTF16) - length = utf16nbytes(_p, n); - else - length = mbsnbytes(_p, n); s = _p; i = 0; if (sc->nconverter > 1) { sc->utftmp.length = 0; r2 = sc->converter[0](&(sc->utftmp), s, length, sc); if (r2 != 0 && errno == ENOMEM) return (r2); if (r > r2) r = r2; s = sc->utftmp.s; length = sc->utftmp.length; ++i; } r2 = sc->converter[i](as, s, length, sc); if (r > r2) r = r2; return (r); } #if HAVE_ICONV /* * Return -1 if conversion fails. */ static int iconv_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { ICONV_CONST char *itp; size_t remaining; iconv_t cd; char *outp; size_t avail, bs; int return_value = 0; /* success */ int to_size, from_size; if (sc->flag & SCONV_TO_UTF16) to_size = 2; else to_size = 1; if (sc->flag & SCONV_FROM_UTF16) from_size = 2; else from_size = 1; if (archive_string_ensure(as, as->length + length*2+to_size) == NULL) return (-1); cd = sc->cd; itp = (char *)(uintptr_t)_p; remaining = length; outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; while (remaining >= (size_t)from_size) { size_t result = iconv(cd, &itp, &remaining, &outp, &avail); if (result != (size_t)-1) break; /* Conversion completed. */ if (errno == EILSEQ || errno == EINVAL) { /* * If an output charset is UTF-8 or UTF-16BE/LE, * unknown character should be U+FFFD * (replacement character). */ if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) { size_t rbytes; if (sc->flag & SCONV_TO_UTF8) rbytes = sizeof(utf8_replacement_char); else rbytes = 2; if (avail < rbytes) { as->length = outp - as->s; bs = as->buffer_length + (remaining * to_size) + rbytes; if (NULL == archive_string_ensure(as, bs)) return (-1); outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; } if (sc->flag & SCONV_TO_UTF8) memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char)); else if (sc->flag & SCONV_TO_UTF16BE) archive_be16enc(outp, UNICODE_R_CHAR); else archive_le16enc(outp, UNICODE_R_CHAR); outp += rbytes; avail -= rbytes; } else { /* Skip the illegal input bytes. */ *outp++ = '?'; avail--; } itp += from_size; remaining -= from_size; return_value = -1; /* failure */ } else { /* E2BIG no output buffer, * Increase an output buffer. */ as->length = outp - as->s; bs = as->buffer_length + remaining * 2; if (NULL == archive_string_ensure(as, bs)) return (-1); outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; } } as->length = outp - as->s; as->s[as->length] = 0; if (to_size == 2) as->s[as->length+1] = 0; return (return_value); } #endif /* HAVE_ICONV */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Translate a string from a some CodePage to an another CodePage by * Windows APIs, and copy the result. Return -1 if conversion fails. */ static int strncat_in_codepage(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { const char *s = (const char *)_p; struct archive_wstring aws; size_t l; int r, saved_flag; archive_string_init(&aws); saved_flag = sc->flag; sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C); r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc); sc->flag = saved_flag; if (r != 0) { archive_wstring_free(&aws); if (errno != ENOMEM) archive_string_append(as, s, length); return (-1); } l = as->length; r = archive_string_append_from_wcs_in_codepage( as, aws.s, aws.length, sc); if (r != 0 && errno != ENOMEM && l == as->length) archive_string_append(as, s, length); archive_wstring_free(&aws); return (r); } /* * Test whether MBS ==> WCS is okay. */ static int invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) { const char *p = (const char *)_p; unsigned codepage; DWORD mbflag = MB_ERR_INVALID_CHARS; if (sc->flag & SCONV_FROM_CHARSET) codepage = sc->to_cp; else codepage = sc->from_cp; if (codepage == CP_C_LOCALE) return (0); if (codepage != CP_UTF8) mbflag |= MB_PRECOMPOSED; if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0) return (-1); /* Invalid */ return (0); /* Okay */ } #else /* * Test whether MBS ==> WCS is okay. */ static int invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) { const char *p = (const char *)_p; size_t r; #if HAVE_MBRTOWC mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ mbtowc(NULL, NULL, 0); #endif while (n) { wchar_t wc; #if HAVE_MBRTOWC r = mbrtowc(&wc, p, n, &shift_state); #else r = mbtowc(&wc, p, n); #endif if (r == (size_t)-1 || r == (size_t)-2) return (-1);/* Invalid. */ if (r == 0) break; p += r; n -= r; } (void)sc; /* UNUSED */ return (0); /* All Okey. */ } #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ /* * Basically returns -1 because we cannot make a conversion of charset * without iconv but in some cases this would return 0. * Returns 0 if all copied characters are ASCII. * Returns 0 if both from-locale and to-locale are the same and those * can be WCS with no error. */ static int best_effort_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { size_t remaining; const uint8_t *itp; int return_value = 0; /* success */ /* * If both from-locale and to-locale is the same, this makes a copy. * And then this checks all copied MBS can be WCS if so returns 0. */ if (sc->same) { if (archive_string_append(as, _p, length) == NULL) return (-1);/* No memory */ return (invalid_mbs(_p, length, sc)); } /* * If a character is ASCII, this just copies it. If not, this * assigns '?' charater instead but in UTF-8 locale this assigns * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD, * a Replacement Character in Unicode. */ remaining = length; itp = (const uint8_t *)_p; while (*itp && remaining > 0) { if (*itp > 127) { // Non-ASCII: Substitute with suitable replacement if (sc->flag & SCONV_TO_UTF8) { if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) { __archive_errx(1, "Out of memory"); } } else { archive_strappend_char(as, '?'); } return_value = -1; } else { archive_strappend_char(as, *itp); } ++itp; } return (return_value); } /* * Unicode conversion functions. * - UTF-8 <===> UTF-8 in removing surrogate pairs. * - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs. * - UTF-8 made by libarchive 2.x ===> UTF-8. * - UTF-16BE <===> UTF-8. * */ /* * Utility to convert a single UTF-8 sequence. * * Usually return used bytes, return used byte in negative value when * a unicode character is replaced with U+FFFD. * See also http://unicode.org/review/pr-121.html Public Review Issue #121 * Recommended Practice for Replacement Characters. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch, i; int cnt; uint32_t wc; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalid sequence or there are not plenty bytes. */ if ((int)n < cnt) { cnt = (int)n; for (i = 1; i < cnt; i++) { if ((s[i] & 0xc0) != 0x80) { cnt = i; break; } } goto invalid_sequence; } /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) { cnt = 1; goto invalid_sequence; } *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) { cnt = 1; goto invalid_sequence; } if ((s[2] & 0xc0) != 0x80) { cnt = 2; goto invalid_sequence; } wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) goto invalid_sequence;/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) { cnt = 1; goto invalid_sequence; } if ((s[2] & 0xc0) != 0x80) { cnt = 2; goto invalid_sequence; } if ((s[3] & 0xc0) != 0x80) { cnt = 3; goto invalid_sequence; } wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) goto invalid_sequence;/* Overlong sequence. */ break; default: /* Others are all invalid sequence. */ if (ch == 0xc0 || ch == 0xc1) cnt = 2; else if (ch >= 0xf5 && ch <= 0xf7) cnt = 4; else if (ch >= 0xf8 && ch <= 0xfb) cnt = 5; else if (ch == 0xfc || ch == 0xfd) cnt = 6; else cnt = 1; if ((int)n < cnt) cnt = (int)n; for (i = 1; i < cnt; i++) { if ((s[i] & 0xc0) != 0x80) { cnt = i; break; } } goto invalid_sequence; } /* The code point larger than 0x10FFFF is not legal * Unicode values. */ if (wc > UNICODE_MAX) goto invalid_sequence; /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); invalid_sequence: *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */ return (cnt * -1); } static int utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { int cnt; cnt = _utf8_to_unicode(pwc, s, n); /* Any of Surrogate pair is not legal Unicode values. */ if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc)) return (-3); return (cnt); } static inline uint32_t combine_surrogate_pair(uint32_t uc, uint32_t uc2) { uc -= 0xD800; uc *= 0x400; uc += uc2 - 0xDC00; uc += 0x10000; return (uc); } /* * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in * removing surrogate pairs. * * CESU-8: The Compatibility Encoding Scheme for UTF-16. * * Usually return used bytes, return used byte in negative value when * a unicode character is replaced with U+FFFD. */ static int cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n) { uint32_t wc = 0; int cnt; cnt = _utf8_to_unicode(&wc, s, n); if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) { uint32_t wc2 = 0; if (n - 3 < 3) { /* Invalid byte sequence. */ goto invalid_sequence; } cnt = _utf8_to_unicode(&wc2, s+3, n-3); if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) { /* Invalid byte sequence. */ goto invalid_sequence; } wc = combine_surrogate_pair(wc, wc2); cnt = 6; } else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) { /* Invalid byte sequence. */ goto invalid_sequence; } *pwc = wc; return (cnt); invalid_sequence: *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */ if (cnt > 0) cnt *= -1; return (cnt); } /* * Convert a Unicode code point to a single UTF-8 sequence. * * NOTE:This function does not check if the Unicode is legal or not. * Please you definitely check it before calling this. */ static size_t unicode_to_utf8(char *p, size_t remaining, uint32_t uc) { char *_p = p; /* Invalid Unicode char maps to Replacement character */ if (uc > UNICODE_MAX) uc = UNICODE_R_CHAR; /* Translate code point to UTF8 */ if (uc <= 0x7f) { if (remaining == 0) return (0); *p++ = (char)uc; } else if (uc <= 0x7ff) { if (remaining < 2) return (0); *p++ = 0xc0 | ((uc >> 6) & 0x1f); *p++ = 0x80 | (uc & 0x3f); } else if (uc <= 0xffff) { if (remaining < 3) return (0); *p++ = 0xe0 | ((uc >> 12) & 0x0f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); } else { if (remaining < 4) return (0); *p++ = 0xf0 | ((uc >> 18) & 0x07); *p++ = 0x80 | ((uc >> 12) & 0x3f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); } return (p - _p); } static int utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n) { return (utf16_to_unicode(pwc, s, n, 1)); } static int utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n) { return (utf16_to_unicode(pwc, s, n, 0)); } static int utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be) { const char *utf16 = s; unsigned uc; if (n == 0) return (0); if (n == 1) { /* set the Replacement Character instead. */ *pwc = UNICODE_R_CHAR; return (-1); } if (be) uc = archive_be16dec(utf16); else uc = archive_le16dec(utf16); utf16 += 2; /* If this is a surrogate pair, assemble the full code point.*/ if (IS_HIGH_SURROGATE_LA(uc)) { unsigned uc2; if (n >= 4) { if (be) uc2 = archive_be16dec(utf16); else uc2 = archive_le16dec(utf16); } else uc2 = 0; if (IS_LOW_SURROGATE_LA(uc2)) { uc = combine_surrogate_pair(uc, uc2); utf16 += 2; } else { /* Undescribed code point should be U+FFFD * (replacement character). */ *pwc = UNICODE_R_CHAR; return (-2); } } /* * Surrogate pair values(0xd800 through 0xdfff) are only * used by UTF-16, so, after above culculation, the code * must not be surrogate values, and Unicode has no codes * larger than 0x10ffff. Thus, those are not legal Unicode * values. */ if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) { /* Undescribed code point should be U+FFFD * (replacement character). */ *pwc = UNICODE_R_CHAR; return (((int)(utf16 - s)) * -1); } *pwc = uc; return ((int)(utf16 - s)); } static size_t unicode_to_utf16be(char *p, size_t remaining, uint32_t uc) { char *utf16 = p; if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ if (remaining < 4) return (0); uc -= 0x10000; archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00); return (4); } else { if (remaining < 2) return (0); archive_be16enc(utf16, uc); return (2); } } static size_t unicode_to_utf16le(char *p, size_t remaining, uint32_t uc) { char *utf16 = p; if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ if (remaining < 4) return (0); uc -= 0x10000; archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00); return (4); } else { if (remaining < 2) return (0); archive_le16enc(utf16, uc); return (2); } } /* * Copy UTF-8 string in checking surrogate pair. * If any surrogate pair are found, it would be canonicalized. */ static int strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s; char *p, *endp; int n, ret = 0; (void)sc; /* UNUSED */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); s = (const char *)_p; p = as->s + as->length; endp = as->s + as->buffer_length -1; do { uint32_t uc; const char *ss = s; size_t w; /* * Forward byte sequence until a conversion of that is needed. */ while ((n = utf8_to_unicode(&uc, s, len)) > 0) { s += n; len -= n; } if (ss < s) { if (p + (s - ss) > endp) { as->length = p - as->s; if (archive_string_ensure(as, as->buffer_length + len + 1) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length -1; } memcpy(p, ss, s - ss); p += s - ss; } /* * If n is negative, current byte sequence needs a replacement. */ if (n < 0) { if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) { /* Current byte sequence may be CESU-8. */ n = cesu8_to_unicode(&uc, s, len); } if (n < 0) { ret = -1; n *= -1;/* Use a replaced unicode character. */ } /* Rebuild UTF-8 byte sequence. */ while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) { as->length = p - as->s; if (archive_string_ensure(as, as->buffer_length + len + 1) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length -1; } p += w; s += n; len -= n; } } while (n > 0); as->length = p - as->s; as->s[as->length] = '\0'; return (ret); } static int archive_string_append_unicode(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s; char *p, *endp; uint32_t uc; size_t w; int n, ret = 0, ts, tm; int (*parse)(uint32_t *, const char *, size_t); size_t (*unparse)(char *, size_t, uint32_t); if (sc->flag & SCONV_TO_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_TO_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else if (sc->flag & SCONV_TO_UTF8) { unparse = unicode_to_utf8; ts = 1; } else { /* * This case is going to be converted to another * character-set through iconv. */ if (sc->flag & SCONV_FROM_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_FROM_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else { unparse = unicode_to_utf8; ts = 1; } } if (sc->flag & SCONV_FROM_UTF16BE) { parse = utf16be_to_unicode; tm = 1; } else if (sc->flag & SCONV_FROM_UTF16LE) { parse = utf16le_to_unicode; tm = 1; } else { parse = cesu8_to_unicode; tm = ts; } if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) return (-1); s = (const char *)_p; p = as->s + as->length; endp = as->s + as->buffer_length - ts; while ((n = parse(&uc, s, len)) != 0) { if (n < 0) { /* Use a replaced unicode character. */ n *= -1; ret = -1; } s += n; len -= n; while ((w = unparse(p, endp - p, uc)) == 0) { /* There is not enough output buffer so * we have to expand it. */ as->length = p - as->s; if (archive_string_ensure(as, as->buffer_length + len * tm + ts) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length - ts; } p += w; } as->length = p - as->s; as->s[as->length] = '\0'; if (ts == 2) as->s[as->length+1] = '\0'; return (ret); } /* * Following Constants for Hangul compositions this information comes from * Unicode Standard Annex #15 http://unicode.org/reports/tr15/ */ #define HC_SBASE 0xAC00 #define HC_LBASE 0x1100 #define HC_VBASE 0x1161 #define HC_TBASE 0x11A7 #define HC_LCOUNT 19 #define HC_VCOUNT 21 #define HC_TCOUNT 28 #define HC_NCOUNT (HC_VCOUNT * HC_TCOUNT) #define HC_SCOUNT (HC_LCOUNT * HC_NCOUNT) static uint32_t get_nfc(uint32_t uc, uint32_t uc2) { int t, b; t = 0; b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1; while (b >= t) { int m = (t + b) / 2; if (u_composition_table[m].cp1 < uc) t = m + 1; else if (u_composition_table[m].cp1 > uc) b = m - 1; else if (u_composition_table[m].cp2 < uc2) t = m + 1; else if (u_composition_table[m].cp2 > uc2) b = m - 1; else return (u_composition_table[m].nfc); } return (0); } #define FDC_MAX 10 /* The maximum number of Following Decomposable * Characters. */ /* * Update first code point. */ #define UPDATE_UC(new_uc) do { \ uc = new_uc; \ ucptr = NULL; \ } while (0) /* * Replace first code point with second code point. */ #define REPLACE_UC_WITH_UC2() do { \ uc = uc2; \ ucptr = uc2ptr; \ n = n2; \ } while (0) #define EXPAND_BUFFER() do { \ as->length = p - as->s; \ if (archive_string_ensure(as, \ as->buffer_length + len * tm + ts) == NULL)\ return (-1); \ p = as->s + as->length; \ endp = as->s + as->buffer_length - ts; \ } while (0) #define UNPARSE(p, endp, uc) do { \ while ((w = unparse(p, (endp) - (p), uc)) == 0) {\ EXPAND_BUFFER(); \ } \ p += w; \ } while (0) /* * Write first code point. * If the code point has not be changed from its original code, * this just copies it from its original buffer pointer. * If not, this converts it to UTF-8 byte sequence and copies it. */ #define WRITE_UC() do { \ if (ucptr) { \ if (p + n > endp) \ EXPAND_BUFFER(); \ switch (n) { \ case 4: \ *p++ = *ucptr++; \ /* FALL THROUGH */ \ case 3: \ *p++ = *ucptr++; \ /* FALL THROUGH */ \ case 2: \ *p++ = *ucptr++; \ /* FALL THROUGH */ \ case 1: \ *p++ = *ucptr; \ break; \ } \ ucptr = NULL; \ } else { \ UNPARSE(p, endp, uc); \ } \ } while (0) /* * Collect following decomposable code points. */ #define COLLECT_CPS(start) do { \ int _i; \ for (_i = start; _i < FDC_MAX ; _i++) { \ nx = parse(&ucx[_i], s, len); \ if (nx <= 0) \ break; \ cx = CCC(ucx[_i]); \ if (cl >= cx && cl != 228 && cx != 228)\ break; \ s += nx; \ len -= nx; \ cl = cx; \ ccx[_i] = cx; \ } \ if (_i >= FDC_MAX) { \ ret = -1; \ ucx_size = FDC_MAX; \ } else \ ucx_size = _i; \ } while (0) /* * Normalize UTF-8/UTF-16BE characters to Form C and copy the result. * * TODO: Convert composition exclusions,which are never converted * from NFC,NFD,NFKC and NFKD, to Form C. */ static int archive_string_normalize_C(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s = (const char *)_p; char *p, *endp; uint32_t uc, uc2; size_t w; int always_replace, n, n2, ret = 0, spair, ts, tm; int (*parse)(uint32_t *, const char *, size_t); size_t (*unparse)(char *, size_t, uint32_t); always_replace = 1; ts = 1;/* text size. */ if (sc->flag & SCONV_TO_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; if (sc->flag & SCONV_FROM_UTF16BE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; if (sc->flag & SCONV_FROM_UTF16LE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF8) { unparse = unicode_to_utf8; if (sc->flag & SCONV_FROM_UTF8) always_replace = 0; } else { /* * This case is going to be converted to another * character-set through iconv. */ always_replace = 0; if (sc->flag & SCONV_FROM_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_FROM_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else { unparse = unicode_to_utf8; } } if (sc->flag & SCONV_FROM_UTF16BE) { parse = utf16be_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else if (sc->flag & SCONV_FROM_UTF16LE) { parse = utf16le_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else { parse = cesu8_to_unicode; tm = ts; spair = 6;/* surrogate pair size in UTF-8. */ } if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length - ts; while ((n = parse(&uc, s, len)) != 0) { const char *ucptr, *uc2ptr; if (n < 0) { /* Use a replaced unicode character. */ UNPARSE(p, endp, uc); s += n*-1; len -= n*-1; ret = -1; continue; } else if (n == spair || always_replace) /* uc is converted from a surrogate pair. * this should be treated as a changed code. */ ucptr = NULL; else ucptr = s; s += n; len -= n; /* Read second code point. */ while ((n2 = parse(&uc2, s, len)) > 0) { uint32_t ucx[FDC_MAX]; int ccx[FDC_MAX]; int cl, cx, i, nx, ucx_size; int LIndex,SIndex; uint32_t nfc; if (n2 == spair || always_replace) /* uc2 is converted from a surrogate pair. * this should be treated as a changed code. */ uc2ptr = NULL; else uc2ptr = s; s += n2; len -= n2; /* * If current second code point is out of decomposable * code points, finding compositions is unneeded. */ if (!IS_DECOMPOSABLE_BLOCK(uc2)) { WRITE_UC(); REPLACE_UC_WITH_UC2(); continue; } /* * Try to combine current code points. */ /* * We have to combine Hangul characters according to * http://uniicode.org/reports/tr15/#Hangul */ if (0 <= (LIndex = uc - HC_LBASE) && LIndex < HC_LCOUNT) { /* * Hangul Composition. * 1. Two current code points are L and V. */ int VIndex = uc2 - HC_VBASE; if (0 <= VIndex && VIndex < HC_VCOUNT) { /* Make syllable of form LV. */ UPDATE_UC(HC_SBASE + (LIndex * HC_VCOUNT + VIndex) * HC_TCOUNT); } else { WRITE_UC(); REPLACE_UC_WITH_UC2(); } continue; } else if (0 <= (SIndex = uc - HC_SBASE) && SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) { /* * Hangul Composition. * 2. Two current code points are LV and T. */ int TIndex = uc2 - HC_TBASE; if (0 < TIndex && TIndex < HC_TCOUNT) { /* Make syllable of form LVT. */ UPDATE_UC(uc + TIndex); } else { WRITE_UC(); REPLACE_UC_WITH_UC2(); } continue; } else if ((nfc = get_nfc(uc, uc2)) != 0) { /* A composition to current code points * is found. */ UPDATE_UC(nfc); continue; } else if ((cl = CCC(uc2)) == 0) { /* Clearly 'uc2' the second code point is not * a decomposable code. */ WRITE_UC(); REPLACE_UC_WITH_UC2(); continue; } /* * Collect following decomposable code points. */ cx = 0; ucx[0] = uc2; ccx[0] = cl; COLLECT_CPS(1); /* * Find a composed code in the collected code points. */ i = 1; while (i < ucx_size) { int j; if ((nfc = get_nfc(uc, ucx[i])) == 0) { i++; continue; } /* * nfc is composed of uc and ucx[i]. */ UPDATE_UC(nfc); /* * Remove ucx[i] by shifting * following code points. */ for (j = i; j+1 < ucx_size; j++) { ucx[j] = ucx[j+1]; ccx[j] = ccx[j+1]; } ucx_size --; /* * Collect following code points blocked * by ucx[i] the removed code point. */ if (ucx_size > 0 && i == ucx_size && nx > 0 && cx == cl) { cl = ccx[ucx_size-1]; COLLECT_CPS(ucx_size); } /* * Restart finding a composed code with * the updated uc from the top of the * collected code points. */ i = 0; } /* * Apparently the current code points are not * decomposed characters or already composed. */ WRITE_UC(); for (i = 0; i < ucx_size; i++) UNPARSE(p, endp, ucx[i]); /* * Flush out remaining canonical combining characters. */ if (nx > 0 && cx == cl && len > 0) { while ((nx = parse(&ucx[0], s, len)) > 0) { cx = CCC(ucx[0]); if (cl > cx) break; s += nx; len -= nx; cl = cx; UNPARSE(p, endp, ucx[0]); } } break; } if (n2 < 0) { WRITE_UC(); /* Use a replaced unicode character. */ UNPARSE(p, endp, uc2); s += n2*-1; len -= n2*-1; ret = -1; continue; } else if (n2 == 0) { WRITE_UC(); break; } } as->length = p - as->s; as->s[as->length] = '\0'; if (ts == 2) as->s[as->length+1] = '\0'; return (ret); } static int get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc) { int t, b; /* * These are not converted to NFD on Mac OS. */ if ((uc >= 0x2000 && uc <= 0x2FFF) || (uc >= 0xF900 && uc <= 0xFAFF) || (uc >= 0x2F800 && uc <= 0x2FAFF)) return (0); /* * Those code points are not converted to NFD on Mac OS. * I do not know the reason because it is undocumented. * NFC NFD * 1109A ==> 11099 110BA * 1109C ==> 1109B 110BA * 110AB ==> 110A5 110BA */ if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB) return (0); t = 0; b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1; while (b >= t) { int m = (t + b) / 2; if (u_decomposition_table[m].nfc < uc) t = m + 1; else if (u_decomposition_table[m].nfc > uc) b = m - 1; else { *cp1 = u_decomposition_table[m].cp1; *cp2 = u_decomposition_table[m].cp2; return (1); } } return (0); } #define REPLACE_UC_WITH(cp) do { \ uc = cp; \ ucptr = NULL; \ } while (0) /* * Normalize UTF-8 characters to Form D and copy the result. */ static int archive_string_normalize_D(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s = (const char *)_p; char *p, *endp; uint32_t uc, uc2; size_t w; int always_replace, n, n2, ret = 0, spair, ts, tm; int (*parse)(uint32_t *, const char *, size_t); size_t (*unparse)(char *, size_t, uint32_t); always_replace = 1; ts = 1;/* text size. */ if (sc->flag & SCONV_TO_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; if (sc->flag & SCONV_FROM_UTF16BE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; if (sc->flag & SCONV_FROM_UTF16LE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF8) { unparse = unicode_to_utf8; if (sc->flag & SCONV_FROM_UTF8) always_replace = 0; } else { /* * This case is going to be converted to another * character-set through iconv. */ always_replace = 0; if (sc->flag & SCONV_FROM_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_FROM_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else { unparse = unicode_to_utf8; } } if (sc->flag & SCONV_FROM_UTF16BE) { parse = utf16be_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else if (sc->flag & SCONV_FROM_UTF16LE) { parse = utf16le_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else { parse = cesu8_to_unicode; tm = ts; spair = 6;/* surrogate pair size in UTF-8. */ } if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length - ts; while ((n = parse(&uc, s, len)) != 0) { const char *ucptr; uint32_t cp1, cp2; int SIndex; struct { uint32_t uc; int ccc; } fdc[FDC_MAX]; int fdi, fdj; int ccc; check_first_code: if (n < 0) { /* Use a replaced unicode character. */ UNPARSE(p, endp, uc); s += n*-1; len -= n*-1; ret = -1; continue; } else if (n == spair || always_replace) /* uc is converted from a surrogate pair. * this should be treated as a changed code. */ ucptr = NULL; else ucptr = s; s += n; len -= n; /* Hangul Decomposition. */ if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) { int L = HC_LBASE + SIndex / HC_NCOUNT; int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT; int T = HC_TBASE + SIndex % HC_TCOUNT; REPLACE_UC_WITH(L); WRITE_UC(); REPLACE_UC_WITH(V); WRITE_UC(); if (T != HC_TBASE) { REPLACE_UC_WITH(T); WRITE_UC(); } continue; } if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) { WRITE_UC(); continue; } fdi = 0; while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) { int k; for (k = fdi; k > 0; k--) fdc[k] = fdc[k-1]; fdc[0].ccc = CCC(cp2); fdc[0].uc = cp2; fdi++; REPLACE_UC_WITH(cp1); } /* Read following code points. */ while ((n2 = parse(&uc2, s, len)) > 0 && (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) { int j, k; s += n2; len -= n2; for (j = 0; j < fdi; j++) { if (fdc[j].ccc > ccc) break; } if (j < fdi) { for (k = fdi; k > j; k--) fdc[k] = fdc[k-1]; fdc[j].ccc = ccc; fdc[j].uc = uc2; } else { fdc[fdi].ccc = ccc; fdc[fdi].uc = uc2; } fdi++; } WRITE_UC(); for (fdj = 0; fdj < fdi; fdj++) { REPLACE_UC_WITH(fdc[fdj].uc); WRITE_UC(); } if (n2 == 0) break; REPLACE_UC_WITH(uc2); n = n2; goto check_first_code; } as->length = p - as->s; as->s[as->length] = '\0'; if (ts == 2) as->s[as->length+1] = '\0'; return (ret); } /* * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption * that WCS is Unicode. It is true for several platforms but some are false. * And then people who did not use UTF-8 locale on the non Unicode WCS * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those * now cannot get right filename from libarchive 3.x and later since we * fixed the wrong assumption and it is incompatible to older its versions. * So we provide special option, "compat-2x.x", for resolving it. * That option enable the string conversion of libarchive 2.x. * * Translates the wrong UTF-8 string made by libarchive 2.x into current * locale character set and appends to the archive_string. * Note: returns -1 if conversion fails. */ static int strncat_from_utf8_libarchive2(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s; int n; char *p; char *end; uint32_t unicode; #if HAVE_WCRTOMB mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ wctomb(NULL, L'\0'); #endif (void)sc; /* UNUSED */ /* * Allocate buffer for MBS. * We need this allocation here since it is possible that * as->s is still NULL. */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); s = (const char *)_p; p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) { wchar_t wc; if (p >= end) { as->length = p - as->s; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, as->length + len * 2 + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; } /* * As libarchie 2.x, translates the UTF-8 characters into * wide-characters in the assumption that WCS is Unicode. */ if (n < 0) { n *= -1; wc = L'?'; } else wc = (wchar_t)unicode; s += n; len -= n; /* * Translates the wide-character into the current locale MBS. */ #if HAVE_WCRTOMB n = (int)wcrtomb(p, wc, &shift_state); #else n = (int)wctomb(p, wc); #endif if (n == -1) return (-1); p += n; } as->length = p - as->s; as->s[as->length] = '\0'; return (0); } /* * Conversion functions between current locale dependent MBS and UTF-16BE. * strncat_from_utf16be() : UTF-16BE --> MBS * strncat_to_utf16be() : MBS --> UTF16BE */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Convert a UTF-16BE/LE string to current locale and copy the result. * Return -1 if conversion fails. */ static int win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc, int be) { struct archive_string tmp; const char *u16; int ll; BOOL defchar; char *mbs; size_t mbs_size, b; int ret = 0; bytes &= ~1; if (archive_string_ensure(as, as->length + bytes +1) == NULL) return (-1); mbs = as->s + as->length; mbs_size = as->buffer_length - as->length -1; if (sc->to_cp == CP_C_LOCALE) { /* * "C" locale special process. */ u16 = _p; ll = 0; for (b = 0; b < bytes; b += 2) { uint16_t val; if (be) val = archive_be16dec(u16+b); else val = archive_le16dec(u16+b); if (val > 255) { *mbs++ = '?'; ret = -1; } else *mbs++ = (char)(val&0xff); ll++; } as->length += ll; as->s[as->length] = '\0'; return (ret); } archive_string_init(&tmp); if (be) { if (is_big_endian()) { u16 = _p; } else { if (archive_string_ensure(&tmp, bytes+2) == NULL) return (-1); memcpy(tmp.s, _p, bytes); for (b = 0; b < bytes; b += 2) { uint16_t val = archive_be16dec(tmp.s+b); archive_le16enc(tmp.s+b, val); } u16 = tmp.s; } } else { if (!is_big_endian()) { u16 = _p; } else { if (archive_string_ensure(&tmp, bytes+2) == NULL) return (-1); memcpy(tmp.s, _p, bytes); for (b = 0; b < bytes; b += 2) { uint16_t val = archive_le16dec(tmp.s+b); archive_be16enc(tmp.s+b, val); } u16 = tmp.s; } } do { defchar = 0; ll = WideCharToMultiByte(sc->to_cp, 0, (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size, NULL, &defchar); /* Exit loop if we succeeded */ if (ll != 0 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { break; } /* Else expand buffer and loop to try again. */ ll = WideCharToMultiByte(sc->to_cp, 0, (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL); if (archive_string_ensure(as, ll +1) == NULL) return (-1); mbs = as->s + as->length; mbs_size = as->buffer_length - as->length -1; } while (1); archive_string_free(&tmp); as->length += ll; as->s[as->length] = '\0'; if (ll == 0 || defchar) ret = -1; return (ret); } static int win_strncat_from_utf16be(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (win_strncat_from_utf16(as, _p, bytes, sc, 1)); } static int win_strncat_from_utf16le(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (win_strncat_from_utf16(as, _p, bytes, sc, 0)); } static int is_big_endian(void) { uint16_t d = 1; return (archive_be16dec(&d) == 1); } /* * Convert a current locale string to UTF-16BE/LE and copy the result. * Return -1 if conversion fails. */ static int win_strncat_to_utf16(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc, int bigendian) { const char *s = (const char *)_p; char *u16; size_t count, avail; if (archive_string_ensure(as16, as16->length + (length + 1) * 2) == NULL) return (-1); u16 = as16->s + as16->length; avail = as16->buffer_length - 2; if (sc->from_cp == CP_C_LOCALE) { /* * "C" locale special process. */ count = 0; while (count < length && *s) { if (bigendian) archive_be16enc(u16, *s); else archive_le16enc(u16, *s); u16 += 2; s++; count++; } as16->length += count << 1; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; return (0); } do { count = MultiByteToWideChar(sc->from_cp, MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1); /* Exit loop if we succeeded */ if (count != 0 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { break; } /* Expand buffer and try again */ count = MultiByteToWideChar(sc->from_cp, MB_PRECOMPOSED, s, (int)length, NULL, 0); if (archive_string_ensure(as16, (count +1) * 2) == NULL) return (-1); u16 = as16->s + as16->length; avail = as16->buffer_length - 2; } while (1); as16->length += count * 2; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; if (count == 0) return (-1); if (is_big_endian()) { if (!bigendian) { while (count > 0) { uint16_t v = archive_be16dec(u16); archive_le16enc(u16, v); u16 += 2; count--; } } } else { if (bigendian) { while (count > 0) { uint16_t v = archive_le16dec(u16); archive_be16enc(u16, v); u16 += 2; count--; } } } return (0); } static int win_strncat_to_utf16be(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (win_strncat_to_utf16(as16, _p, length, sc, 1)); } static int win_strncat_to_utf16le(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (win_strncat_to_utf16(as16, _p, length, sc, 0)); } #endif /* _WIN32 && !__CYGWIN__ */ /* * Do the best effort for conversions. * We cannot handle UTF-16BE character-set without such iconv, * but there is a chance if a string consists just ASCII code or * a current locale is UTF-8. */ /* * Convert a UTF-16BE string to current locale and copy the result. * Return -1 if conversion fails. */ static int best_effort_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc, int be) { const char *utf16 = (const char *)_p; char *mbs; uint32_t uc; int n, ret; (void)sc; /* UNUSED */ /* * Other case, we should do the best effort. * If all character are ASCII(<0x7f), we can convert it. * if not , we set a alternative character and return -1. */ ret = 0; if (archive_string_ensure(as, as->length + bytes +1) == NULL) return (-1); mbs = as->s + as->length; while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) { if (n < 0) { n *= -1; ret = -1; } bytes -= n; utf16 += n; if (uc > 127) { /* We cannot handle it. */ *mbs++ = '?'; ret = -1; } else *mbs++ = (char)uc; } as->length = mbs - as->s; as->s[as->length] = '\0'; return (ret); } static int best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1)); } static int best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0)); } /* * Convert a current locale string to UTF-16BE/LE and copy the result. * Return -1 if conversion fails. */ static int best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc, int bigendian) { const char *s = (const char *)_p; char *utf16; size_t remaining; int ret; (void)sc; /* UNUSED */ /* * Other case, we should do the best effort. * If all character are ASCII(<0x7f), we can convert it. * if not , we set a alternative character and return -1. */ ret = 0; remaining = length; if (archive_string_ensure(as16, as16->length + (length + 1) * 2) == NULL) return (-1); utf16 = as16->s + as16->length; while (remaining--) { unsigned c = *s++; if (c > 127) { /* We cannot handle it. */ c = UNICODE_R_CHAR; ret = -1; } if (bigendian) archive_be16enc(utf16, c); else archive_le16enc(utf16, c); utf16 += 2; } as16->length = utf16 - as16->s; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; return (ret); } static int best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1)); } static int best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0)); } /* * Multistring operations. */ void archive_mstring_clean(struct archive_mstring *aes) { archive_wstring_free(&(aes->aes_wcs)); archive_string_free(&(aes->aes_mbs)); archive_string_free(&(aes->aes_utf8)); archive_string_free(&(aes->aes_mbs_in_locale)); aes->aes_set = 0; } void archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src) { dest->aes_set = src->aes_set; archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs)); } int archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes, const char **p) { struct archive_string_conv *sc; int r; /* If we already have a UTF8 form, return that immediately. */ if (aes->aes_set & AES_SET_UTF8) { *p = aes->aes_utf8.s; return (0); } *p = NULL; if (aes->aes_set & AES_SET_MBS) { sc = archive_string_conversion_to_charset(a, "UTF-8", 1); if (sc == NULL) return (-1);/* Couldn't allocate memory for sc. */ r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s, aes->aes_mbs.length, sc); if (a == NULL) free_sconv_object(sc); if (r == 0) { aes->aes_set |= AES_SET_UTF8; *p = aes->aes_utf8.s; return (0);/* success. */ } else return (-1);/* failure. */ } return (0);/* success. */ } int archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes, const char **p) { int r, ret = 0; (void)a; /* UNUSED */ /* If we already have an MBS form, return that immediately. */ if (aes->aes_set & AES_SET_MBS) { *p = aes->aes_mbs.s; return (ret); } *p = NULL; /* If there's a WCS form, try converting with the native locale. */ if (aes->aes_set & AES_SET_WCS) { archive_string_empty(&(aes->aes_mbs)); r = archive_string_append_from_wcs(&(aes->aes_mbs), aes->aes_wcs.s, aes->aes_wcs.length); *p = aes->aes_mbs.s; if (r == 0) { aes->aes_set |= AES_SET_MBS; return (ret); } else ret = -1; } /* * Only a UTF-8 form cannot avail because its conversion already * failed at archive_mstring_update_utf8(). */ return (ret); } int archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes, const wchar_t **wp) { int r, ret = 0; (void)a;/* UNUSED */ /* Return WCS form if we already have it. */ if (aes->aes_set & AES_SET_WCS) { *wp = aes->aes_wcs.s; return (ret); } *wp = NULL; /* Try converting MBS to WCS using native locale. */ if (aes->aes_set & AES_SET_MBS) { archive_wstring_empty(&(aes->aes_wcs)); r = archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s, aes->aes_mbs.length); if (r == 0) { aes->aes_set |= AES_SET_WCS; *wp = aes->aes_wcs.s; } else ret = -1;/* failure. */ } return (ret); } int archive_mstring_get_mbs_l(struct archive_mstring *aes, const char **p, size_t *length, struct archive_string_conv *sc) { int r, ret = 0; #if defined(_WIN32) && !defined(__CYGWIN__) /* * Internationalization programing on Windows must use Wide * characters because Windows platform cannot make locale UTF-8. */ if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) { archive_string_empty(&(aes->aes_mbs_in_locale)); r = archive_string_append_from_wcs_in_codepage( &(aes->aes_mbs_in_locale), aes->aes_wcs.s, aes->aes_wcs.length, sc); if (r == 0) { *p = aes->aes_mbs_in_locale.s; if (length != NULL) *length = aes->aes_mbs_in_locale.length; return (0); } else if (errno == ENOMEM) return (-1); else ret = -1; } #endif /* If there is not an MBS form but is a WCS form, try converting * with the native locale to be used for translating it to specified * character-set. */ if ((aes->aes_set & AES_SET_MBS) == 0 && (aes->aes_set & AES_SET_WCS) != 0) { archive_string_empty(&(aes->aes_mbs)); r = archive_string_append_from_wcs(&(aes->aes_mbs), aes->aes_wcs.s, aes->aes_wcs.length); if (r == 0) aes->aes_set |= AES_SET_MBS; else if (errno == ENOMEM) return (-1); else ret = -1; } /* If we already have an MBS form, use it to be translated to * specified character-set. */ if (aes->aes_set & AES_SET_MBS) { if (sc == NULL) { /* Conversion is unneeded. */ *p = aes->aes_mbs.s; if (length != NULL) *length = aes->aes_mbs.length; return (0); } ret = archive_strncpy_l(&(aes->aes_mbs_in_locale), aes->aes_mbs.s, aes->aes_mbs.length, sc); *p = aes->aes_mbs_in_locale.s; if (length != NULL) *length = aes->aes_mbs_in_locale.length; } else { *p = NULL; if (length != NULL) *length = 0; } return (ret); } int archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs) { if (mbs == NULL) { aes->aes_set = 0; return (0); } return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs))); } int archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs, size_t len) { if (mbs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ archive_strncpy(&(aes->aes_mbs), mbs, len); archive_string_empty(&(aes->aes_utf8)); archive_wstring_empty(&(aes->aes_wcs)); return (0); } int archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs) { return archive_mstring_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs)); } int archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; } aes->aes_set = AES_SET_UTF8; archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_wcs)); archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8)); return (int)strlen(utf8); } int archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs, size_t len) { if (wcs == NULL) { aes->aes_set = 0; } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_utf8)); archive_wstrncpy(&(aes->aes_wcs), wcs, len); return (0); } int archive_mstring_copy_mbs_len_l(struct archive_mstring *aes, const char *mbs, size_t len, struct archive_string_conv *sc) { int r; if (mbs == NULL) { aes->aes_set = 0; return (0); } archive_string_empty(&(aes->aes_mbs)); archive_wstring_empty(&(aes->aes_wcs)); archive_string_empty(&(aes->aes_utf8)); #if defined(_WIN32) && !defined(__CYGWIN__) /* * Internationalization programing on Windows must use Wide * characters because Windows platform cannot make locale UTF-8. */ if (sc == NULL) { if (archive_string_append(&(aes->aes_mbs), mbs, mbsnbytes(mbs, len)) == NULL) { aes->aes_set = 0; r = -1; } else { aes->aes_set = AES_SET_MBS; r = 0; } #if defined(HAVE_ICONV) } else if (sc != NULL && sc->cd_w != (iconv_t)-1) { /* * This case happens only when MultiByteToWideChar() cannot * handle sc->from_cp, and we have to iconv in order to * translate character-set to wchar_t,UTF-16. */ iconv_t cd = sc->cd; unsigned from_cp; int flag; /* * Translate multi-bytes from some character-set to UTF-8. */ sc->cd = sc->cd_w; r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc); sc->cd = cd; if (r != 0) { aes->aes_set = 0; return (r); } aes->aes_set = AES_SET_UTF8; /* * Append the UTF-8 string into wstring. */ flag = sc->flag; sc->flag &= ~(SCONV_NORMALIZATION_C | SCONV_TO_UTF16| SCONV_FROM_UTF16); from_cp = sc->from_cp; sc->from_cp = CP_UTF8; r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), aes->aes_utf8.s, aes->aes_utf8.length, sc); sc->flag = flag; sc->from_cp = from_cp; if (r == 0) aes->aes_set |= AES_SET_WCS; #endif } else { r = archive_wstring_append_from_mbs_in_codepage( &(aes->aes_wcs), mbs, len, sc); if (r == 0) aes->aes_set = AES_SET_WCS; else aes->aes_set = 0; } #else r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc); if (r == 0) aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ else aes->aes_set = 0; #endif return (r); } /* * The 'update' form tries to proactively update all forms of * this string (WCS and MBS) and returns an error if any of * them fail. This is used by the 'pax' handler, for instance, * to detect and report character-conversion failures early while * still allowing clients to get potentially useful values from * the more tolerant lazy conversions. (get_mbs and get_wcs will * strive to give the user something useful, so you can get hopefully * usable values even if some of the character conversions are failing.) */ int archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes, const char *utf8) { struct archive_string_conv *sc; int r; if (utf8 == NULL) { aes->aes_set = 0; return (0); /* Succeeded in clearing everything. */ } /* Save the UTF8 string. */ archive_strcpy(&(aes->aes_utf8), utf8); /* Empty the mbs and wcs strings. */ archive_string_empty(&(aes->aes_mbs)); archive_wstring_empty(&(aes->aes_wcs)); aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ /* Try converting UTF-8 to MBS, return false on failure. */ sc = archive_string_conversion_from_charset(a, "UTF-8", 1); if (sc == NULL) return (-1);/* Couldn't allocate memory for sc. */ r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc); if (a == NULL) free_sconv_object(sc); if (r != 0) return (-1); aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */ /* Try converting MBS to WCS, return false on failure. */ if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s, aes->aes_mbs.length)) return (-1); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; /* All conversions succeeded. */ return (0); } Index: vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c (revision 310621) +++ vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c (revision 310622) @@ -1,8160 +1,8160 @@ /*- * Copyright (c) 2009-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_write_private.h" #if defined(_WIN32) && !defined(__CYGWIN__) #define getuid() 0 #define getgid() 0 #endif /*#define DEBUG 1*/ #ifdef DEBUG /* To compare to the ISO image file made by mkisofs. */ #define COMPAT_MKISOFS 1 #endif #define LOGICAL_BLOCK_BITS 11 #define LOGICAL_BLOCK_SIZE 2048 #define PATH_TABLE_BLOCK_SIZE 4096 #define SYSTEM_AREA_BLOCK 16 #define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 1 #define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 1 #define BOOT_RECORD_DESCRIPTOR_BLOCK 1 #define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK 1 #define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK 1 #define RRIP_ER_BLOCK 1 #define PADDING_BLOCK 150 #define FD_1_2M_SIZE (1024 * 1200) #define FD_1_44M_SIZE (1024 * 1440) #define FD_2_88M_SIZE (1024 * 2880) #define MULTI_EXTENT_SIZE (ARCHIVE_LITERAL_LL(1) << 32) /* 4Gi bytes. */ #define MAX_DEPTH 8 #define RR_CE_SIZE 28 /* SUSP "CE" extension size */ #define FILE_FLAG_EXISTENCE 0x01 #define FILE_FLAG_DIRECTORY 0x02 #define FILE_FLAG_ASSOCIATED 0x04 #define FILE_FLAG_RECORD 0x08 #define FILE_FLAG_PROTECTION 0x10 #define FILE_FLAG_MULTI_EXTENT 0x80 static const char rrip_identifier[] = "RRIP_1991A"; static const char rrip_descriptor[] = "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR " "POSIX FILE SYSTEM SEMANTICS"; static const char rrip_source[] = "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. " "SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR " "CONTACT INFORMATION."; #define RRIP_ER_ID_SIZE (sizeof(rrip_identifier)-1) #define RRIP_ER_DSC_SIZE (sizeof(rrip_descriptor)-1) #define RRIP_ER_SRC_SIZE (sizeof(rrip_source)-1) #define RRIP_ER_SIZE (8 + RRIP_ER_ID_SIZE + \ RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE) static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; #define ZF_HEADER_SIZE 16 /* zisofs header size. */ #define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */ #define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS) /* * Manage extra records. */ struct extr_rec { int location; int offset; unsigned char buf[LOGICAL_BLOCK_SIZE]; struct extr_rec *next; }; struct ctl_extr_rec { int use_extr; unsigned char *bp; struct isoent *isoent; unsigned char *ce_ptr; int cur_len; int dr_len; int limit; int extr_off; int extr_loc; }; #define DR_SAFETY RR_CE_SIZE #define DR_LIMIT (254 - DR_SAFETY) /* * The relation of struct isofile and isoent and archive_entry. * * Primary volume tree --> struct isoent * | * v * struct isofile --> archive_entry * ^ * | * Joliet volume tree --> struct isoent * * struct isoent has specific information for volume. */ struct isofile { /* Used for managing struct isofile list. */ struct isofile *allnext; struct isofile *datanext; /* Used for managing a hardlined struct isofile list. */ struct isofile *hlnext; struct isofile *hardlink_target; struct archive_entry *entry; /* * Used for making a directory tree. */ struct archive_string parentdir; struct archive_string basename; struct archive_string basename_utf16; struct archive_string symlink; int dircnt; /* The number of elements of * its parent directory */ /* * Used for a Directory Record. */ struct content { int64_t offset_of_temp; int64_t size; int blocks; uint32_t location; /* * One extent equals one content. * If this entry has multi extent, `next' variable points * next content data. */ struct content *next; /* next content */ } content, *cur_content; int write_content; enum { NO = 0, BOOT_CATALOG, BOOT_IMAGE } boot; /* * Used for a zisofs. */ struct { unsigned char header_size; unsigned char log2_bs; uint32_t uncompressed_size; } zisofs; }; struct isoent { /* Keep `rbnode' at the first member of struct isoent. */ struct archive_rb_node rbnode; struct isofile *file; struct isoent *parent; /* A list of children.(use chnext) */ struct { struct isoent *first; struct isoent **last; int cnt; } children; struct archive_rb_tree rbtree; /* A list of sub directories.(use drnext) */ struct { struct isoent *first; struct isoent **last; int cnt; } subdirs; /* A sorted list of sub directories. */ struct isoent **children_sorted; /* Used for managing struct isoent list. */ struct isoent *chnext; struct isoent *drnext; struct isoent *ptnext; /* * Used for making a Directory Record. */ int dir_number; struct { int vd; int self; int parent; int normal; } dr_len; uint32_t dir_location; int dir_block; /* * Identifier: * on primary, ISO9660 file/directory name. * on joliet, UCS2 file/directory name. * ext_off : offset of identifier extension. * ext_len : length of identifier extension. * id_len : byte size of identifier. * on primary, this is ext_off + ext_len + version length. * on joliet, this is ext_off + ext_len. * mb_len : length of multibyte-character of identifier. * on primary, mb_len and id_len are always the same. * on joliet, mb_len and id_len are different. */ char *identifier; int ext_off; int ext_len; int id_len; int mb_len; /* * Used for making a Rockridge extension. * This is a part of Directory Records. */ struct isoent *rr_parent; struct isoent *rr_child; /* Extra Record.(which we call in this source file) * A maximum size of the Directory Record is 254. * so, if generated RRIP data of a file cannot into a Directory * Record because of its size, that surplus data relocate this * Extra Record. */ struct { struct extr_rec *first; struct extr_rec **last; struct extr_rec *current; } extr_rec_list; int virtual:1; /* If set to one, this file type is a directory. * A convenience flag to be used as * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". */ int dir:1; }; struct hardlink { struct archive_rb_node rbnode; int nlink; struct { struct isofile *first; struct isofile **last; } file_list; }; /* * ISO writer options */ struct iso_option { /* * Usage : abstract-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -abstract * * Specifies Abstract Filename. * This file shall be described in the Root Directory * and containing a abstract statement. */ unsigned int abstract_file:1; #define OPT_ABSTRACT_FILE_DEFAULT 0 /* Not specified */ #define ABSTRACT_FILE_SIZE 37 /* * Usage : application-id= * Type : string, max 128 bytes * Default: Not specified * COMPAT : mkisofs -A/-appid . * * Specifies Application Identifier. * If the first byte is set to '_'(5F), the remaining * bytes of this option shall specify an identifier * for a file containing the identification of the * application. * This file shall be described in the Root Directory. */ unsigned int application_id:1; #define OPT_APPLICATION_ID_DEFAULT 0 /* Use default identifier */ #define APPLICATION_IDENTIFIER_SIZE 128 /* * Usage : !allow-vernum * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT: mkisofs -N * * Allow filenames to use version numbers. */ unsigned int allow_vernum:1; #define OPT_ALLOW_VERNUM_DEFAULT 1 /* Enabled */ /* * Usage : biblio-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -biblio * * Specifies Bibliographic Filename. * This file shall be described in the Root Directory * and containing bibliographic records. */ unsigned int biblio_file:1; #define OPT_BIBLIO_FILE_DEFAULT 0 /* Not specified */ #define BIBLIO_FILE_SIZE 37 /* * Usage : boot= * Type : string * Default: Not specified * COMPAT : mkisofs -b/-eltorito-boot * * Specifies "El Torito" boot image file to make * a bootable CD. */ unsigned int boot:1; #define OPT_BOOT_DEFAULT 0 /* Not specified */ /* * Usage : boot-catalog= * Type : string * Default: "boot.catalog" * COMPAT : mkisofs -c/-eltorito-catalog * * Specifies a fullpath of El Torito boot catalog. */ unsigned int boot_catalog:1; #define OPT_BOOT_CATALOG_DEFAULT 0 /* Not specified */ /* * Usage : boot-info-table * Type : boolean * Default: Disabled * COMPAT : mkisofs -boot-info-table * * Modify the boot image file specified by `boot' * option; ISO writer stores boot file information * into the boot file in ISO image at offset 8 * through offset 64. */ unsigned int boot_info_table:1; #define OPT_BOOT_INFO_TABLE_DEFAULT 0 /* Disabled */ /* * Usage : boot-load-seg= * Type : hexadecimal * Default: Not specified * COMPAT : mkisofs -boot-load-seg * * Specifies a load segment for boot image. * This is used with no-emulation mode. */ unsigned int boot_load_seg:1; #define OPT_BOOT_LOAD_SEG_DEFAULT 0 /* Not specified */ /* * Usage : boot-load-size= * Type : decimal * Default: Not specified * COMPAT : mkisofs -boot-load-size * * Specifies a sector count for boot image. * This is used with no-emulation mode. */ unsigned int boot_load_size:1; #define OPT_BOOT_LOAD_SIZE_DEFAULT 0 /* Not specified */ /* * Usage : boot-type= * : 'no-emulation' : 'no emulation' image * : 'fd' : floppy disk image * : 'hard-disk' : hard disk image * Type : string * Default: Auto detect * : We check a size of boot image; * : If the size is just 1.22M/1.44M/2.88M, * : we assume boot_type is 'fd'; * : otherwise boot_type is 'no-emulation'. * COMPAT : * boot=no-emulation * mkisofs -no-emul-boot * boot=fd * This is a default on the mkisofs. * boot=hard-disk * mkisofs -hard-disk-boot * * Specifies a type of "El Torito" boot image. */ unsigned int boot_type:2; #define OPT_BOOT_TYPE_AUTO 0 /* auto detect */ #define OPT_BOOT_TYPE_NO_EMU 1 /* ``no emulation'' image */ #define OPT_BOOT_TYPE_FD 2 /* floppy disk image */ #define OPT_BOOT_TYPE_HARD_DISK 3 /* hard disk image */ #define OPT_BOOT_TYPE_DEFAULT OPT_BOOT_TYPE_AUTO /* * Usage : compression-level= * Type : decimal * Default: Not specified * COMPAT : NONE * * Specifies compression level for option zisofs=direct. */ unsigned int compression_level:1; #define OPT_COMPRESSION_LEVEL_DEFAULT 0 /* Not specified */ /* * Usage : copyright-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -copyright * * Specifies Copyright Filename. * This file shall be described in the Root Directory * and containing a copyright statement. */ unsigned int copyright_file:1; #define OPT_COPYRIGHT_FILE_DEFAULT 0 /* Not specified */ #define COPYRIGHT_FILE_SIZE 37 /* * Usage : gid= * Type : decimal * Default: Not specified * COMPAT : mkisofs -gid * * Specifies a group id to rewrite the group id of all files. */ unsigned int gid:1; #define OPT_GID_DEFAULT 0 /* Not specified */ /* * Usage : iso-level=[1234] * Type : decimal * Default: 1 * COMPAT : mkisofs -iso-level * * Specifies ISO9600 Level. * Level 1: [DEFAULT] * - limits each file size less than 4Gi bytes; * - a File Name shall not contain more than eight * d-characters or eight d1-characters; * - a File Name Extension shall not contain more than * three d-characters or three d1-characters; * - a Directory Identifier shall not contain more * than eight d-characters or eight d1-characters. * Level 2: * - limits each file size less than 4Giga bytes; * - a File Name shall not contain more than thirty * d-characters or thirty d1-characters; * - a File Name Extension shall not contain more than * thirty d-characters or thirty d1-characters; * - a Directory Identifier shall not contain more * than thirty-one d-characters or thirty-one * d1-characters. * Level 3: * - no limit of file size; use multi extent. * Level 4: * - this level 4 simulates mkisofs option * '-iso-level 4'; * - crate a enhanced volume as mkisofs doing; * - allow a File Name to have leading dot; * - allow a File Name to have all ASCII letters; * - allow a File Name to have multiple dots; * - allow more then 8 depths of directory trees; * - disable a version number to a File Name; * - disable a forced period to the tail of a File Name; * - the maxinum length of files and directories is raised to 193. * if rockridge option is disabled, raised to 207. */ unsigned int iso_level:3; #define OPT_ISO_LEVEL_DEFAULT 1 /* ISO Level 1 */ /* * Usage : joliet[=long] * : !joliet * : Do not generate Joliet Volume and Records. * : joliet [DEFAULT] * : Generates Joliet Volume and Directory Records. * : [COMPAT: mkisofs -J/-joliet] * : joliet=long * : The joliet filenames are up to 103 Unicode * : characters. * : This option breaks the Joliet specification. * : [COMPAT: mkisofs -J -joliet-long] * Type : boolean/string * Default: Enabled * COMPAT : mkisofs -J / -joliet-long * * Generates Joliet Volume and Directory Records. */ unsigned int joliet:2; #define OPT_JOLIET_DISABLE 0 /* Not generate Joliet Records. */ #define OPT_JOLIET_ENABLE 1 /* Generate Joliet Records. */ #define OPT_JOLIET_LONGNAME 2 /* Use long joliet filenames.*/ #define OPT_JOLIET_DEFAULT OPT_JOLIET_ENABLE /* * Usage : !limit-depth * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT : mkisofs -D/-disable-deep-relocation * * The number of levels in hierarchy cannot exceed eight. */ unsigned int limit_depth:1; #define OPT_LIMIT_DEPTH_DEFAULT 1 /* Enabled */ /* * Usage : !limit-dirs * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT : mkisofs -no-limit-pathtables * * Limits the number of directories less than 65536 due * to the size of the Parent Directory Number of Path * Table. */ unsigned int limit_dirs:1; #define OPT_LIMIT_DIRS_DEFAULT 1 /* Enabled */ /* * Usage : !pad * Type : boolean * Default: Enabled * COMPAT : -pad/-no-pad * * Pads the end of the ISO image by null of 300Ki bytes. */ unsigned int pad:1; #define OPT_PAD_DEFAULT 1 /* Enabled */ /* * Usage : publisher= * Type : string, max 128 bytes * Default: Not specified * COMPAT : mkisofs -publisher * * Specifies Publisher Identifier. * If the first byte is set to '_'(5F), the remaining * bytes of this option shall specify an identifier * for a file containing the identification of the user. * This file shall be described in the Root Directory. */ unsigned int publisher:1; #define OPT_PUBLISHER_DEFAULT 0 /* Not specified */ #define PUBLISHER_IDENTIFIER_SIZE 128 /* * Usage : rockridge * : !rockridge * : disable to generate SUSP and RR records. * : rockridge * : the same as 'rockridge=useful'. * : rockridge=strict * : generate SUSP and RR records. * : [COMPAT: mkisofs -R] * : rockridge=useful [DEFAULT] * : generate SUSP and RR records. * : [COMPAT: mkisofs -r] * : NOTE Our rockridge=useful option does not set a zero * : to uid and gid, you should use application * : option such as --gid,--gname,--uid and --uname * : badtar options instead. * Type : boolean/string * Default: Enabled as rockridge=useful * COMPAT : mkisofs -r / -R * * Generates SUSP and RR records. */ unsigned int rr:2; #define OPT_RR_DISABLED 0 #define OPT_RR_STRICT 1 #define OPT_RR_USEFUL 2 #define OPT_RR_DEFAULT OPT_RR_USEFUL /* * Usage : volume-id= * Type : string, max 32 bytes * Default: Not specified * COMPAT : mkisofs -V * * Specifies Volume Identifier. */ unsigned int volume_id:1; #define OPT_VOLUME_ID_DEFAULT 0 /* Use default identifier */ #define VOLUME_IDENTIFIER_SIZE 32 /* * Usage : !zisofs [DEFAULT] * : Disable to generate RRIP 'ZF' extension. * : zisofs * : Make files zisofs file and generate RRIP 'ZF' * : extension. So you do not need mkzftree utility * : for making zisofs. * : When the file size is less than one Logical Block * : size, that file will not zisofs'ed since it does * : reduece an ISO-image size. * : * : When you specify option 'boot=', that * : 'boot-image' file won't be converted to zisofs file. * Type : boolean * Default: Disabled * * Generates RRIP 'ZF' System Use Entry. */ unsigned int zisofs:1; #define OPT_ZISOFS_DISABLED 0 #define OPT_ZISOFS_DIRECT 1 #define OPT_ZISOFS_DEFAULT OPT_ZISOFS_DISABLED }; struct iso9660 { /* The creation time of ISO image. */ time_t birth_time; /* A file stream of a temporary file, which file contents - * save to until ISO iamge can be created. */ + * save to until ISO image can be created. */ int temp_fd; struct isofile *cur_file; struct isoent *cur_dirent; struct archive_string cur_dirstr; uint64_t bytes_remaining; int need_multi_extent; /* Temporary string buffer for Joliet extension. */ struct archive_string utf16be; struct archive_string mbs; struct archive_string_conv *sconv_to_utf16be; struct archive_string_conv *sconv_from_utf16be; /* A list of all of struct isofile entries. */ struct { struct isofile *first; struct isofile **last; } all_file_list; /* A list of struct isofile entries which have its * contents and are not a directory, a hardlined file * and a symlink file. */ struct { struct isofile *first; struct isofile **last; } data_file_list; /* Used for managing to find hardlinking files. */ struct archive_rb_tree hardlink_rbtree; /* Used for making the Path Table Record. */ struct vdd { /* the root of entry tree. */ struct isoent *rootent; enum vdd_type { VDD_PRIMARY, VDD_JOLIET, VDD_ENHANCED } vdd_type; struct path_table { struct isoent *first; struct isoent **last; struct isoent **sorted; int cnt; } *pathtbl; int max_depth; int path_table_block; int path_table_size; int location_type_L_path_table; int location_type_M_path_table; int total_dir_block; } primary, joliet; /* Used for making a Volume Descriptor. */ int volume_space_size; int volume_sequence_number; int total_file_block; struct archive_string volume_identifier; struct archive_string publisher_identifier; struct archive_string data_preparer_identifier; struct archive_string application_identifier; struct archive_string copyright_file_identifier; struct archive_string abstract_file_identifier; struct archive_string bibliographic_file_identifier; /* Used for making rockridge extensions. */ int location_rrip_er; /* Used for making zisofs. */ struct { int detect_magic:1; int making:1; int allzero:1; unsigned char magic_buffer[64]; int magic_cnt; #ifdef HAVE_ZLIB_H /* * Copy a compressed file to iso9660.zisofs.temp_fd * and also copy a uncompressed file(original file) to * iso9660.temp_fd . If the number of logical block * of the compressed file is less than the number of * logical block of the uncompressed file, use it and * remove the copy of the uncompressed file. * but if not, we use uncompressed file and remove * the copy of the compressed file. */ uint32_t *block_pointers; size_t block_pointers_allocated; int block_pointers_cnt; int block_pointers_idx; int64_t total_size; int64_t block_offset; z_stream stream; int stream_valid; int64_t remaining; int compression_level; #endif } zisofs; struct isoent *directories_too_deep; int dircnt_max; /* Write buffer. */ #define wb_buffmax() (LOGICAL_BLOCK_SIZE * 32) #define wb_remaining(a) (((struct iso9660 *)(a)->format_data)->wbuff_remaining) #define wb_offset(a) (((struct iso9660 *)(a)->format_data)->wbuff_offset \ + wb_buffmax() - wb_remaining(a)) unsigned char wbuff[LOGICAL_BLOCK_SIZE * 32]; size_t wbuff_remaining; enum { WB_TO_STREAM, WB_TO_TEMP } wbuff_type; int64_t wbuff_offset; int64_t wbuff_written; int64_t wbuff_tail; /* 'El Torito' boot data. */ struct { /* boot catalog file */ struct archive_string catalog_filename; struct isoent *catalog; /* boot image file */ struct archive_string boot_filename; struct isoent *boot; unsigned char platform_id; #define BOOT_PLATFORM_X86 0 #define BOOT_PLATFORM_PPC 1 #define BOOT_PLATFORM_MAC 2 struct archive_string id; unsigned char media_type; #define BOOT_MEDIA_NO_EMULATION 0 #define BOOT_MEDIA_1_2M_DISKETTE 1 #define BOOT_MEDIA_1_44M_DISKETTE 2 #define BOOT_MEDIA_2_88M_DISKETTE 3 #define BOOT_MEDIA_HARD_DISK 4 unsigned char system_type; uint16_t boot_load_seg; uint16_t boot_load_size; #define BOOT_LOAD_SIZE 4 } el_torito; struct iso_option opt; }; /* * Types of Volume Descriptor */ enum VD_type { VDT_BOOT_RECORD=0, /* Boot Record Volume Descriptor */ VDT_PRIMARY=1, /* Primary Volume Descriptor */ VDT_SUPPLEMENTARY=2, /* Supplementary Volume Descriptor */ VDT_TERMINATOR=255 /* Volume Descriptor Set Terminator */ }; /* * Types of Directory Record */ enum dir_rec_type { DIR_REC_VD, /* Stored in Volume Descriptor. */ DIR_REC_SELF, /* Stored as Current Directory. */ DIR_REC_PARENT, /* Stored as Parent Directory. */ DIR_REC_NORMAL /* Stored as Child. */ }; /* * Kinds of Volume Descriptor Character */ enum vdc { VDC_STD, VDC_LOWERCASE, VDC_UCS2, VDC_UCS2_DIRECT }; /* * IDentifier Resolver. * Used for resolving duplicated filenames. */ struct idr { struct idrent { struct archive_rb_node rbnode; /* Used in wait_list. */ struct idrent *wnext; struct idrent *avail; struct isoent *isoent; int weight; int noff; int rename_num; } *idrent_pool; struct archive_rb_tree rbtree; struct { struct idrent *first; struct idrent **last; } wait_list; int pool_size; int pool_idx; int num_size; int null_size; char char_map[0x80]; }; enum char_type { A_CHAR, D_CHAR }; static int iso9660_options(struct archive_write *, const char *, const char *); static int iso9660_write_header(struct archive_write *, struct archive_entry *); static ssize_t iso9660_write_data(struct archive_write *, const void *, size_t); static int iso9660_finish_entry(struct archive_write *); static int iso9660_close(struct archive_write *); static int iso9660_free(struct archive_write *); static void get_system_identitier(char *, size_t); static void set_str(unsigned char *, const char *, size_t, char, const char *); static inline int joliet_allowed_char(unsigned char, unsigned char); static int set_str_utf16be(struct archive_write *, unsigned char *, const char *, size_t, uint16_t, enum vdc); static int set_str_a_characters_bp(struct archive_write *, unsigned char *, int, int, const char *, enum vdc); static int set_str_d_characters_bp(struct archive_write *, unsigned char *, int, int, const char *, enum vdc); static void set_VD_bp(unsigned char *, enum VD_type, unsigned char); static inline void set_unused_field_bp(unsigned char *, int, int); static unsigned char *extra_open_record(unsigned char *, int, struct isoent *, struct ctl_extr_rec *); static void extra_close_record(struct ctl_extr_rec *, int); static unsigned char * extra_next_record(struct ctl_extr_rec *, int); static unsigned char *extra_get_record(struct isoent *, int *, int *, int *); static void extra_tell_used_size(struct ctl_extr_rec *, int); static int extra_setup_location(struct isoent *, int); static int set_directory_record_rr(unsigned char *, int, struct isoent *, struct iso9660 *, enum dir_rec_type); static int set_directory_record(unsigned char *, size_t, struct isoent *, struct iso9660 *, enum dir_rec_type, enum vdd_type); static inline int get_dir_rec_size(struct iso9660 *, struct isoent *, enum dir_rec_type, enum vdd_type); static inline unsigned char *wb_buffptr(struct archive_write *); static int wb_write_out(struct archive_write *); static int wb_consume(struct archive_write *, size_t); #ifdef HAVE_ZLIB_H static int wb_set_offset(struct archive_write *, int64_t); #endif static int write_null(struct archive_write *, size_t); static int write_VD_terminator(struct archive_write *); static int set_file_identifier(unsigned char *, int, int, enum vdc, struct archive_write *, struct vdd *, struct archive_string *, const char *, int, enum char_type); static int write_VD(struct archive_write *, struct vdd *); static int write_VD_boot_record(struct archive_write *); static int write_information_block(struct archive_write *); static int write_path_table(struct archive_write *, int, struct vdd *); static int write_directory_descriptors(struct archive_write *, struct vdd *); static int write_file_descriptors(struct archive_write *); static int write_rr_ER(struct archive_write *); static void calculate_path_table_size(struct vdd *); static void isofile_init_entry_list(struct iso9660 *); static void isofile_add_entry(struct iso9660 *, struct isofile *); static void isofile_free_all_entries(struct iso9660 *); static void isofile_init_entry_data_file_list(struct iso9660 *); static void isofile_add_data_file(struct iso9660 *, struct isofile *); static struct isofile * isofile_new(struct archive_write *, struct archive_entry *); static void isofile_free(struct isofile *); static int isofile_gen_utility_names(struct archive_write *, struct isofile *); static int isofile_register_hardlink(struct archive_write *, struct isofile *); static void isofile_connect_hardlink_files(struct iso9660 *); static void isofile_init_hardlinks(struct iso9660 *); static void isofile_free_hardlinks(struct iso9660 *); static struct isoent *isoent_new(struct isofile *); static int isoent_clone_tree(struct archive_write *, struct isoent **, struct isoent *); static void _isoent_free(struct isoent *isoent); static void isoent_free_all(struct isoent *); static struct isoent * isoent_create_virtual_dir(struct archive_write *, struct iso9660 *, const char *); static int isoent_cmp_node(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key(const struct archive_rb_node *, const void *); static int isoent_add_child_head(struct isoent *, struct isoent *); static int isoent_add_child_tail(struct isoent *, struct isoent *); static void isoent_remove_child(struct isoent *, struct isoent *); static void isoent_setup_directory_location(struct iso9660 *, int, struct vdd *); static void isoent_setup_file_location(struct iso9660 *, int); static int get_path_component(char *, size_t, const char *); static int isoent_tree(struct archive_write *, struct isoent **); static struct isoent *isoent_find_child(struct isoent *, const char *); static struct isoent *isoent_find_entry(struct isoent *, const char *); static void idr_relaxed_filenames(char *); static void idr_init(struct iso9660 *, struct vdd *, struct idr *); static void idr_cleanup(struct idr *); static int idr_ensure_poolsize(struct archive_write *, struct idr *, int); static int idr_start(struct archive_write *, struct idr *, int, int, int, int, const struct archive_rb_tree_ops *); static void idr_register(struct idr *, struct isoent *, int, int); static void idr_extend_identifier(struct idrent *, int, int); static void idr_resolve(struct idr *, void (*)(unsigned char *, int)); static void idr_set_num(unsigned char *, int); static void idr_set_num_beutf16(unsigned char *, int); static int isoent_gen_iso9660_identifier(struct archive_write *, struct isoent *, struct idr *); static int isoent_gen_joliet_identifier(struct archive_write *, struct isoent *, struct idr *); static int isoent_cmp_iso9660_identifier(const struct isoent *, const struct isoent *); static int isoent_cmp_node_iso9660(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key_iso9660(const struct archive_rb_node *, const void *); static int isoent_cmp_joliet_identifier(const struct isoent *, const struct isoent *); static int isoent_cmp_node_joliet(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key_joliet(const struct archive_rb_node *, const void *); static inline void path_table_add_entry(struct path_table *, struct isoent *); static inline struct isoent * path_table_last_entry(struct path_table *); static int isoent_make_path_table(struct archive_write *); static int isoent_find_out_boot_file(struct archive_write *, struct isoent *); static int isoent_create_boot_catalog(struct archive_write *, struct isoent *); static size_t fd_boot_image_size(int); static int make_boot_catalog(struct archive_write *); static int setup_boot_information(struct archive_write *); static int zisofs_init(struct archive_write *, struct isofile *); static void zisofs_detect_magic(struct archive_write *, const void *, size_t); static int zisofs_write_to_temp(struct archive_write *, const void *, size_t); static int zisofs_finish_entry(struct archive_write *); static int zisofs_rewind_boot_file(struct archive_write *); static int zisofs_free(struct archive_write *); int archive_write_set_format_iso9660(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct iso9660 *iso9660; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); iso9660 = calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } iso9660->birth_time = 0; iso9660->temp_fd = -1; iso9660->cur_file = NULL; iso9660->primary.max_depth = 0; iso9660->primary.vdd_type = VDD_PRIMARY; iso9660->primary.pathtbl = NULL; iso9660->joliet.rootent = NULL; iso9660->joliet.max_depth = 0; iso9660->joliet.vdd_type = VDD_JOLIET; iso9660->joliet.pathtbl = NULL; isofile_init_entry_list(iso9660); isofile_init_entry_data_file_list(iso9660); isofile_init_hardlinks(iso9660); iso9660->directories_too_deep = NULL; iso9660->dircnt_max = 1; iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_TEMP; iso9660->wbuff_offset = 0; iso9660->wbuff_written = 0; iso9660->wbuff_tail = 0; archive_string_init(&(iso9660->utf16be)); archive_string_init(&(iso9660->mbs)); /* * Init Identifiers used for PVD and SVD. */ archive_string_init(&(iso9660->volume_identifier)); archive_strcpy(&(iso9660->volume_identifier), "CDROM"); archive_string_init(&(iso9660->publisher_identifier)); archive_string_init(&(iso9660->data_preparer_identifier)); archive_string_init(&(iso9660->application_identifier)); archive_strcpy(&(iso9660->application_identifier), archive_version_string()); archive_string_init(&(iso9660->copyright_file_identifier)); archive_string_init(&(iso9660->abstract_file_identifier)); archive_string_init(&(iso9660->bibliographic_file_identifier)); /* * Init El Torito bootable CD variables. */ archive_string_init(&(iso9660->el_torito.catalog_filename)); iso9660->el_torito.catalog = NULL; /* Set default file name of boot catalog */ archive_strcpy(&(iso9660->el_torito.catalog_filename), "boot.catalog"); archive_string_init(&(iso9660->el_torito.boot_filename)); iso9660->el_torito.boot = NULL; iso9660->el_torito.platform_id = BOOT_PLATFORM_X86; archive_string_init(&(iso9660->el_torito.id)); iso9660->el_torito.boot_load_seg = 0; iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE; /* * Init zisofs variables. */ #ifdef HAVE_ZLIB_H iso9660->zisofs.block_pointers = NULL; iso9660->zisofs.block_pointers_allocated = 0; iso9660->zisofs.stream_valid = 0; iso9660->zisofs.compression_level = 9; memset(&(iso9660->zisofs.stream), 0, sizeof(iso9660->zisofs.stream)); #endif /* * Set default value of iso9660 options. */ iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT; iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT; iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT; iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT; iso9660->opt.boot = OPT_BOOT_DEFAULT; iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT; iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT; iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT; iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT; iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT; iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT; iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT; iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT; iso9660->opt.joliet = OPT_JOLIET_DEFAULT; iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT; iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT; iso9660->opt.pad = OPT_PAD_DEFAULT; iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT; iso9660->opt.rr = OPT_RR_DEFAULT; iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT; iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT; /* Create the root directory. */ iso9660->primary.rootent = isoent_create_virtual_dir(a, iso9660, ""); if (iso9660->primary.rootent == NULL) { free(iso9660); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } iso9660->primary.rootent->parent = iso9660->primary.rootent; iso9660->cur_dirent = iso9660->primary.rootent; archive_string_init(&(iso9660->cur_dirstr)); archive_string_ensure(&(iso9660->cur_dirstr), 1); iso9660->cur_dirstr.s[0] = 0; iso9660->sconv_to_utf16be = NULL; iso9660->sconv_from_utf16be = NULL; a->format_data = iso9660; a->format_name = "iso9660"; a->format_options = iso9660_options; a->format_write_header = iso9660_write_header; a->format_write_data = iso9660_write_data; a->format_finish_entry = iso9660_finish_entry; a->format_close = iso9660_close; a->format_free = iso9660_free; a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; return (ARCHIVE_OK); } static int get_str_opt(struct archive_write *a, struct archive_string *s, size_t maxsize, const char *key, const char *value) { if (strlen(value) > maxsize) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Value is longer than %zu characters " "for option ``%s''", maxsize, key); return (ARCHIVE_FATAL); } archive_strcpy(s, value); return (ARCHIVE_OK); } static int get_num_opt(struct archive_write *a, int *num, int high, int low, const char *key, const char *value) { const char *p = value; int data = 0; int neg = 0; if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(empty) for option ``%s''", key); return (ARCHIVE_FATAL); } if (*p == '-') { neg = 1; p++; } while (*p) { if (*p >= '0' && *p <= '9') data = data * 10 + *p - '0'; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value for option ``%s''", key); return (ARCHIVE_FATAL); } if (data > high) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(over %d) for " "option ``%s''", high, key); return (ARCHIVE_FATAL); } if (data < low) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(under %d) for " "option ``%s''", low, key); return (ARCHIVE_FATAL); } p++; } if (neg) data *= -1; *num = data; return (ARCHIVE_OK); } static int iso9660_options(struct archive_write *a, const char *key, const char *value) { struct iso9660 *iso9660 = a->format_data; const char *p; int r; switch (key[0]) { case 'a': if (strcmp(key, "abstract-file") == 0) { r = get_str_opt(a, &(iso9660->abstract_file_identifier), ABSTRACT_FILE_SIZE, key, value); iso9660->opt.abstract_file = r == ARCHIVE_OK; return (r); } if (strcmp(key, "application-id") == 0) { r = get_str_opt(a, &(iso9660->application_identifier), APPLICATION_IDENTIFIER_SIZE, key, value); iso9660->opt.application_id = r == ARCHIVE_OK; return (r); } if (strcmp(key, "allow-vernum") == 0) { iso9660->opt.allow_vernum = value != NULL; return (ARCHIVE_OK); } break; case 'b': if (strcmp(key, "biblio-file") == 0) { r = get_str_opt(a, &(iso9660->bibliographic_file_identifier), BIBLIO_FILE_SIZE, key, value); iso9660->opt.biblio_file = r == ARCHIVE_OK; return (r); } if (strcmp(key, "boot") == 0) { if (value == NULL) iso9660->opt.boot = 0; else { iso9660->opt.boot = 1; archive_strcpy( &(iso9660->el_torito.boot_filename), value); } return (ARCHIVE_OK); } if (strcmp(key, "boot-catalog") == 0) { r = get_str_opt(a, &(iso9660->el_torito.catalog_filename), 1024, key, value); iso9660->opt.boot_catalog = r == ARCHIVE_OK; return (r); } if (strcmp(key, "boot-info-table") == 0) { iso9660->opt.boot_info_table = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "boot-load-seg") == 0) { uint32_t seg; iso9660->opt.boot_load_seg = 0; if (value == NULL) goto invalid_value; seg = 0; p = value; if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) p += 2; while (*p) { if (seg) seg <<= 4; if (*p >= 'A' && *p <= 'F') seg += *p - 'A' + 0x0a; else if (*p >= 'a' && *p <= 'f') seg += *p - 'a' + 0x0a; else if (*p >= '0' && *p <= '9') seg += *p - '0'; else goto invalid_value; if (seg > 0xffff) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(over 0xffff) for " "option ``%s''", key); return (ARCHIVE_FATAL); } p++; } iso9660->el_torito.boot_load_seg = (uint16_t)seg; iso9660->opt.boot_load_seg = 1; return (ARCHIVE_OK); } if (strcmp(key, "boot-load-size") == 0) { int num = 0; r = get_num_opt(a, &num, 0xffff, 1, key, value); iso9660->opt.boot_load_size = r == ARCHIVE_OK; if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->el_torito.boot_load_size = (uint16_t)num; return (ARCHIVE_OK); } if (strcmp(key, "boot-type") == 0) { if (value == NULL) goto invalid_value; if (strcmp(value, "no-emulation") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU; else if (strcmp(value, "fd") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_FD; else if (strcmp(value, "hard-disk") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK; else goto invalid_value; return (ARCHIVE_OK); } break; case 'c': if (strcmp(key, "compression-level") == 0) { #ifdef HAVE_ZLIB_H if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || value[1] != '\0') goto invalid_value; iso9660->zisofs.compression_level = value[0] - '0'; iso9660->opt.compression_level = 1; return (ARCHIVE_OK); #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Option ``%s'' " "is not supported on this platform.", key); return (ARCHIVE_FATAL); #endif } if (strcmp(key, "copyright-file") == 0) { r = get_str_opt(a, &(iso9660->copyright_file_identifier), COPYRIGHT_FILE_SIZE, key, value); iso9660->opt.copyright_file = r == ARCHIVE_OK; return (r); } #ifdef DEBUG /* Specifies Volume creation date and time; * year(4),month(2),day(2),hour(2),minute(2),second(2). * e.g. "20090929033757" */ if (strcmp(key, "creation") == 0) { struct tm tm; char buf[5]; p = value; if (p == NULL || strlen(p) < 14) goto invalid_value; memset(&tm, 0, sizeof(tm)); memcpy(buf, p, 4); buf[4] = '\0'; p += 4; tm.tm_year = strtol(buf, NULL, 10) - 1900; memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_mon = strtol(buf, NULL, 10) - 1; memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_mday = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_hour = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_min = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; tm.tm_sec = strtol(buf, NULL, 10); iso9660->birth_time = mktime(&tm); return (ARCHIVE_OK); } #endif break; case 'i': if (strcmp(key, "iso-level") == 0) { if (value != NULL && value[1] == '\0' && (value[0] >= '1' && value[0] <= '4')) { iso9660->opt.iso_level = value[0]-'0'; return (ARCHIVE_OK); } goto invalid_value; } break; case 'j': if (strcmp(key, "joliet") == 0) { if (value == NULL) iso9660->opt.joliet = OPT_JOLIET_DISABLE; else if (strcmp(value, "1") == 0) iso9660->opt.joliet = OPT_JOLIET_ENABLE; else if (strcmp(value, "long") == 0) iso9660->opt.joliet = OPT_JOLIET_LONGNAME; else goto invalid_value; return (ARCHIVE_OK); } break; case 'l': if (strcmp(key, "limit-depth") == 0) { iso9660->opt.limit_depth = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "limit-dirs") == 0) { iso9660->opt.limit_dirs = value != NULL; return (ARCHIVE_OK); } break; case 'p': if (strcmp(key, "pad") == 0) { iso9660->opt.pad = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "publisher") == 0) { r = get_str_opt(a, &(iso9660->publisher_identifier), PUBLISHER_IDENTIFIER_SIZE, key, value); iso9660->opt.publisher = r == ARCHIVE_OK; return (r); } break; case 'r': if (strcmp(key, "rockridge") == 0 || strcmp(key, "Rockridge") == 0) { if (value == NULL) iso9660->opt.rr = OPT_RR_DISABLED; else if (strcmp(value, "1") == 0) iso9660->opt.rr = OPT_RR_USEFUL; else if (strcmp(value, "strict") == 0) iso9660->opt.rr = OPT_RR_STRICT; else if (strcmp(value, "useful") == 0) iso9660->opt.rr = OPT_RR_USEFUL; else goto invalid_value; return (ARCHIVE_OK); } break; case 'v': if (strcmp(key, "volume-id") == 0) { r = get_str_opt(a, &(iso9660->volume_identifier), VOLUME_IDENTIFIER_SIZE, key, value); iso9660->opt.volume_id = r == ARCHIVE_OK; return (r); } break; case 'z': if (strcmp(key, "zisofs") == 0) { if (value == NULL) iso9660->opt.zisofs = OPT_ZISOFS_DISABLED; else { #ifdef HAVE_ZLIB_H iso9660->opt.zisofs = OPT_ZISOFS_DIRECT; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "``zisofs'' " "is not supported on this platform."); return (ARCHIVE_FATAL); #endif } return (ARCHIVE_OK); } break; } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); invalid_value: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value for option ``%s''", key); return (ARCHIVE_FAILED); } static int iso9660_write_header(struct archive_write *a, struct archive_entry *entry) { struct iso9660 *iso9660; struct isofile *file; struct isoent *isoent; int r, ret = ARCHIVE_OK; iso9660 = a->format_data; iso9660->cur_file = NULL; iso9660->bytes_remaining = 0; iso9660->need_multi_extent = 0; if (archive_entry_filetype(entry) == AE_IFLNK && iso9660->opt.rr == OPT_RR_DISABLED) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore symlink file."); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) >= MULTI_EXTENT_SIZE) { if (iso9660->opt.iso_level < 3) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore over %lld bytes file. " "This file too large.", MULTI_EXTENT_SIZE); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } iso9660->need_multi_extent = 1; } file = isofile_new(a, entry); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } r = isofile_gen_utility_names(a, file); if (r < ARCHIVE_WARN) { isofile_free(file); return (r); } else if (r < ret) ret = r; /* * Ignore a path which looks like the top of directory name * since we have already made the root directory of an ISO image. */ if (archive_strlen(&(file->parentdir)) == 0 && archive_strlen(&(file->basename)) == 0) { isofile_free(file); return (r); } isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } if (isoent->file->dircnt > iso9660->dircnt_max) iso9660->dircnt_max = isoent->file->dircnt; /* Add the current file into tree */ r = isoent_tree(a, &isoent); if (r != ARCHIVE_OK) return (r); /* If there is the same file in tree and * the current file is older than the file in tree. * So we don't need the current file data anymore. */ if (isoent->file != file) return (ARCHIVE_OK); /* Non regular files contents are unneeded to be saved to * temporary files. */ if (archive_entry_filetype(file->entry) != AE_IFREG) return (ret); /* * Set the current file to cur_file to read its contents. */ iso9660->cur_file = file; if (archive_entry_nlink(file->entry) > 1) { r = isofile_register_hardlink(a, file); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* * Prepare to save the contents of the file. */ if (iso9660->temp_fd < 0) { iso9660->temp_fd = __archive_mktemp(NULL); if (iso9660->temp_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } } /* Save an offset of current file in temporary file. */ file->content.offset_of_temp = wb_offset(a); file->cur_content = &(file->content); r = zisofs_init(a, file); if (r < ret) ret = r; iso9660->bytes_remaining = archive_entry_size(file->entry); return (ret); } static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; ssize_t written; const unsigned char *b; b = (const unsigned char *)buff; while (s) { written = write(iso9660->temp_fd, b, s); if (written < 0) { archive_set_error(&a->archive, errno, "Can't write to temporary file"); return (ARCHIVE_FATAL); } s -= written; b += written; } return (ARCHIVE_OK); } static int wb_write_to_temp(struct archive_write *a, const void *buff, size_t s) { const char *xp = buff; size_t xs = s; /* * If a written data size is big enough to use system-call * and there is no waiting data, this calls write_to_temp() in * order to reduce a extra memory copy. */ if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; xs = s % LOGICAL_BLOCK_SIZE; iso9660->wbuff_offset += s - xs; if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (xs == 0) return (ARCHIVE_OK); xp += s - xs; } while (xs) { size_t size = xs; if (size > wb_remaining(a)) size = wb_remaining(a); memcpy(wb_buffptr(a), xp, size); if (wb_consume(a, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); xs -= size; xp += size; } return (ARCHIVE_OK); } static int wb_write_padding_to_temp(struct archive_write *a, int64_t csize) { size_t ns; int ret; ns = (size_t)(csize % LOGICAL_BLOCK_SIZE); if (ns != 0) ret = write_null(a, LOGICAL_BLOCK_SIZE - ns); else ret = ARCHIVE_OK; return (ret); } static ssize_t write_iso9660_data(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; size_t ws; if (iso9660->temp_fd < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } ws = s; if (iso9660->need_multi_extent && (iso9660->cur_file->cur_content->size + ws) >= (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) { struct content *con; size_t ts; ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE - iso9660->cur_file->cur_content->size); if (iso9660->zisofs.detect_magic) zisofs_detect_magic(a, buff, ts); if (iso9660->zisofs.making) { if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->cur_file->cur_content->size += ts; } /* Write padding. */ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Compute the logical block number. */ iso9660->cur_file->cur_content->blocks = (int) ((iso9660->cur_file->cur_content->size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); /* * Make next extent. */ ws -= ts; buff = (const void *)(((const unsigned char *)buff) + ts); /* Make a content for next extent. */ con = calloc(1, sizeof(*con)); if (con == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate content data"); return (ARCHIVE_FATAL); } con->offset_of_temp = wb_offset(a); iso9660->cur_file->cur_content->next = con; iso9660->cur_file->cur_content = con; #ifdef HAVE_ZLIB_H iso9660->zisofs.block_offset = 0; #endif } if (iso9660->zisofs.detect_magic) zisofs_detect_magic(a, buff, ws); if (iso9660->zisofs.making) { if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->cur_file->cur_content->size += ws; } return (s); } static ssize_t iso9660_write_data(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; ssize_t r; if (iso9660->cur_file == NULL) return (0); if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) return (0); if (s > iso9660->bytes_remaining) s = (size_t)iso9660->bytes_remaining; if (s == 0) return (0); r = write_iso9660_data(a, buff, s); if (r > 0) iso9660->bytes_remaining -= r; return (r); } static int iso9660_finish_entry(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; if (iso9660->cur_file == NULL) return (ARCHIVE_OK); if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) return (ARCHIVE_OK); if (iso9660->cur_file->content.size == 0) return (ARCHIVE_OK); /* If there are unwritten data, write null data instead. */ while (iso9660->bytes_remaining > 0) { size_t s; s = (iso9660->bytes_remaining > a->null_length)? a->null_length: (size_t)iso9660->bytes_remaining; if (write_iso9660_data(a, a->nulls, s) < 0) return (ARCHIVE_FATAL); iso9660->bytes_remaining -= s; } if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write padding. */ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Compute the logical block number. */ iso9660->cur_file->cur_content->blocks = (int) ((iso9660->cur_file->cur_content->size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); /* Add the current file to data file list. */ isofile_add_data_file(iso9660, iso9660->cur_file); return (ARCHIVE_OK); } static int iso9660_close(struct archive_write *a) { struct iso9660 *iso9660; int ret, blocks; iso9660 = a->format_data; /* * Write remaining data out to the temporary file. */ if (wb_remaining(a) > 0) { ret = wb_write_out(a); if (ret < 0) return (ret); } /* * Preparations... */ #ifdef DEBUG if (iso9660->birth_time == 0) #endif time(&(iso9660->birth_time)); /* * Prepare a bootable ISO image. */ if (iso9660->opt.boot) { /* Find out the boot file entry. */ ret = isoent_find_out_boot_file(a, iso9660->primary.rootent); if (ret < 0) return (ret); /* Reconvert the boot file from zisofs'ed form to * plain form. */ ret = zisofs_rewind_boot_file(a); if (ret < 0) return (ret); /* Write remaining data out to the temporary file. */ if (wb_remaining(a) > 0) { ret = wb_write_out(a); if (ret < 0) return (ret); } /* Create the boot catalog. */ ret = isoent_create_boot_catalog(a, iso9660->primary.rootent); if (ret < 0) return (ret); } /* * Prepare joliet extensions. */ if (iso9660->opt.joliet) { /* Make a new tree for joliet. */ ret = isoent_clone_tree(a, &(iso9660->joliet.rootent), iso9660->primary.rootent); if (ret < 0) return (ret); /* Make sure we have UTF-16BE convertors. * if there is no file entry, convertors are still * uninitilized. */ if (iso9660->sconv_to_utf16be == NULL) { iso9660->sconv_to_utf16be = archive_string_conversion_to_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_to_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); iso9660->sconv_from_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_from_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } } /* * Make Path Tables. */ ret = isoent_make_path_table(a); if (ret < 0) return (ret); /* * Calculate a total volume size and setup all locations of * contents of an iso9660 image. */ blocks = SYSTEM_AREA_BLOCK + PRIMARY_VOLUME_DESCRIPTOR_BLOCK + VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK + NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; if (iso9660->opt.boot) blocks += BOOT_RECORD_DESCRIPTOR_BLOCK; if (iso9660->opt.joliet) blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; if (iso9660->opt.iso_level == 4) blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; /* Setup the locations of Path Table. */ iso9660->primary.location_type_L_path_table = blocks; blocks += iso9660->primary.path_table_block; iso9660->primary.location_type_M_path_table = blocks; blocks += iso9660->primary.path_table_block; if (iso9660->opt.joliet) { iso9660->joliet.location_type_L_path_table = blocks; blocks += iso9660->joliet.path_table_block; iso9660->joliet.location_type_M_path_table = blocks; blocks += iso9660->joliet.path_table_block; } /* Setup the locations of directories. */ isoent_setup_directory_location(iso9660, blocks, &(iso9660->primary)); blocks += iso9660->primary.total_dir_block; if (iso9660->opt.joliet) { isoent_setup_directory_location(iso9660, blocks, &(iso9660->joliet)); blocks += iso9660->joliet.total_dir_block; } if (iso9660->opt.rr) { iso9660->location_rrip_er = blocks; blocks += RRIP_ER_BLOCK; } /* Setup the locations of all file contents. */ isoent_setup_file_location(iso9660, blocks); blocks += iso9660->total_file_block; if (iso9660->opt.boot && iso9660->opt.boot_info_table) { ret = setup_boot_information(a); if (ret < 0) return (ret); } /* Now we have a total volume size. */ iso9660->volume_space_size = blocks; if (iso9660->opt.pad) iso9660->volume_space_size += PADDING_BLOCK; iso9660->volume_sequence_number = 1; /* * Write an ISO 9660 image. */ - /* Switc to start using wbuff as file buffer. */ + /* Switch to start using wbuff as file buffer. */ iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_STREAM; iso9660->wbuff_offset = 0; iso9660->wbuff_written = 0; iso9660->wbuff_tail = 0; /* Write The System Area */ ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Primary Volume Descriptor */ ret = write_VD(a, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.boot) { /* Write Boot Record Volume Descriptor */ ret = write_VD_boot_record(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.iso_level == 4) { /* Write Enhanced Volume Descriptor */ iso9660->primary.vdd_type = VDD_ENHANCED; ret = write_VD(a, &(iso9660->primary)); iso9660->primary.vdd_type = VDD_PRIMARY; if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.joliet) { ret = write_VD(a, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write Volume Descriptor Set Terminator */ ret = write_VD_terminator(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Non-ISO File System Information */ ret = write_information_block(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type L Path Table */ ret = write_path_table(a, 0, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type M Path Table */ ret = write_path_table(a, 1, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.joliet) { /* Write Type L Path Table */ ret = write_path_table(a, 0, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type M Path Table */ ret = write_path_table(a, 1, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write Directory Descriptors */ ret = write_directory_descriptors(a, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.joliet) { ret = write_directory_descriptors(a, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.rr) { /* Write Rockridge ER(Extensions Reference) */ ret = write_rr_ER(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write File Descriptors */ ret = write_file_descriptors(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Padding */ if (iso9660->opt.pad) { ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->directories_too_deep != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Directories too deep.", archive_entry_pathname( iso9660->directories_too_deep->file->entry)); return (ARCHIVE_WARN); } /* Write remaining data out. */ ret = wb_write_out(a); return (ret); } static int iso9660_free(struct archive_write *a) { struct iso9660 *iso9660; int i, ret; iso9660 = a->format_data; /* Close the temporary file. */ if (iso9660->temp_fd >= 0) close(iso9660->temp_fd); /* Free some stuff for zisofs operations. */ ret = zisofs_free(a); /* Remove directory entries in tree which includes file entries. */ isoent_free_all(iso9660->primary.rootent); for (i = 0; i < iso9660->primary.max_depth; i++) free(iso9660->primary.pathtbl[i].sorted); free(iso9660->primary.pathtbl); if (iso9660->opt.joliet) { isoent_free_all(iso9660->joliet.rootent); for (i = 0; i < iso9660->joliet.max_depth; i++) free(iso9660->joliet.pathtbl[i].sorted); free(iso9660->joliet.pathtbl); } /* Remove isofile entries. */ isofile_free_all_entries(iso9660); isofile_free_hardlinks(iso9660); archive_string_free(&(iso9660->cur_dirstr)); archive_string_free(&(iso9660->volume_identifier)); archive_string_free(&(iso9660->publisher_identifier)); archive_string_free(&(iso9660->data_preparer_identifier)); archive_string_free(&(iso9660->application_identifier)); archive_string_free(&(iso9660->copyright_file_identifier)); archive_string_free(&(iso9660->abstract_file_identifier)); archive_string_free(&(iso9660->bibliographic_file_identifier)); archive_string_free(&(iso9660->el_torito.catalog_filename)); archive_string_free(&(iso9660->el_torito.boot_filename)); archive_string_free(&(iso9660->el_torito.id)); archive_string_free(&(iso9660->utf16be)); archive_string_free(&(iso9660->mbs)); free(iso9660); a->format_data = NULL; return (ret); } /* * Get the System Identifier */ static void get_system_identitier(char *system_id, size_t size) { #if defined(HAVE_SYS_UTSNAME_H) struct utsname u; uname(&u); strncpy(system_id, u.sysname, size-1); system_id[size-1] = '\0'; #elif defined(_WIN32) && !defined(__CYGWIN__) strncpy(system_id, "Windows", size-1); system_id[size-1] = '\0'; #else #error no way to get the system identifier on your platform. #endif } static void set_str(unsigned char *p, const char *s, size_t l, char f, const char *map) { unsigned char c; if (s == NULL) s = ""; while ((c = *s++) != 0 && l > 0) { if (c >= 0x80 || map[c] == 0) { /* illegal character */ if (c >= 'a' && c <= 'z') { /* convert c from a-z to A-Z */ c -= 0x20; } else c = 0x5f; } *p++ = c; l--; } /* If l isn't zero, fill p buffer by the character * which indicated by f. */ if (l > 0) memset(p , f, l); } static inline int joliet_allowed_char(unsigned char high, unsigned char low) { int utf16 = (high << 8) | low; if (utf16 <= 0x001F) return (0); switch (utf16) { case 0x002A: /* '*' */ case 0x002F: /* '/' */ case 0x003A: /* ':' */ case 0x003B: /* ';' */ case 0x003F: /* '?' */ case 0x005C: /* '\' */ return (0);/* Not allowed. */ } return (1); } static int set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s, size_t l, uint16_t uf, enum vdc vdc) { size_t size, i; int onepad; if (s == NULL) s = ""; if (l & 0x01) { onepad = 1; l &= ~1; } else onepad = 0; if (vdc == VDC_UCS2) { struct iso9660 *iso9660 = a->format_data; if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s), iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } size = iso9660->utf16be.length; if (size > l) size = l; memcpy(p, iso9660->utf16be.s, size); } else { const uint16_t *u16 = (const uint16_t *)s; size = 0; while (*u16++) size += 2; if (size > l) size = l; memcpy(p, s, size); } for (i = 0; i < size; i += 2, p += 2) { if (!joliet_allowed_char(p[0], p[1])) archive_be16enc(p, 0x005F);/* '_' */ } l -= size; while (l > 0) { archive_be16enc(p, uf); p += 2; l -= 2; } if (onepad) *p = 0; return (ARCHIVE_OK); } static const char a_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char a1_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char d_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char d1_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ }; static int set_str_a_characters_bp(struct archive_write *a, unsigned char *bp, int from, int to, const char *s, enum vdc vdc) { int r; switch (vdc) { case VDC_STD: set_str(bp+from, s, to - from + 1, 0x20, a_characters_map); r = ARCHIVE_OK; break; case VDC_LOWERCASE: set_str(bp+from, s, to - from + 1, 0x20, a1_characters_map); r = ARCHIVE_OK; break; case VDC_UCS2: case VDC_UCS2_DIRECT: r = set_str_utf16be(a, bp+from, s, to - from + 1, 0x0020, vdc); break; default: r = ARCHIVE_FATAL; } return (r); } static int set_str_d_characters_bp(struct archive_write *a, unsigned char *bp, int from, int to, const char *s, enum vdc vdc) { int r; switch (vdc) { case VDC_STD: set_str(bp+from, s, to - from + 1, 0x20, d_characters_map); r = ARCHIVE_OK; break; case VDC_LOWERCASE: set_str(bp+from, s, to - from + 1, 0x20, d1_characters_map); r = ARCHIVE_OK; break; case VDC_UCS2: case VDC_UCS2_DIRECT: r = set_str_utf16be(a, bp+from, s, to - from + 1, 0x0020, vdc); break; default: r = ARCHIVE_FATAL; } return (r); } static void set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver) { /* Volume Descriptor Type */ bp[1] = (unsigned char)type; /* Standard Identifier */ memcpy(bp + 2, "CD001", 5); /* Volume Descriptor Version */ bp[7] = ver; } static inline void set_unused_field_bp(unsigned char *bp, int from, int to) { memset(bp + from, 0, to - from + 1); } /* * 8-bit unsigned numerical values. * ISO9660 Standard 7.1.1 */ static inline void set_num_711(unsigned char *p, unsigned char value) { *p = value; } /* * 8-bit signed numerical values. * ISO9660 Standard 7.1.2 */ static inline void set_num_712(unsigned char *p, char value) { *((char *)p) = value; } /* * Least significant byte first. * ISO9660 Standard 7.2.1 */ static inline void set_num_721(unsigned char *p, uint16_t value) { archive_le16enc(p, value); } /* * Most significant byte first. * ISO9660 Standard 7.2.2 */ static inline void set_num_722(unsigned char *p, uint16_t value) { archive_be16enc(p, value); } /* * Both-byte orders. * ISO9660 Standard 7.2.3 */ static void set_num_723(unsigned char *p, uint16_t value) { archive_le16enc(p, value); archive_be16enc(p+2, value); } /* * Least significant byte first. * ISO9660 Standard 7.3.1 */ static inline void set_num_731(unsigned char *p, uint32_t value) { archive_le32enc(p, value); } /* * Most significant byte first. * ISO9660 Standard 7.3.2 */ static inline void set_num_732(unsigned char *p, uint32_t value) { archive_be32enc(p, value); } /* * Both-byte orders. * ISO9660 Standard 7.3.3 */ static inline void set_num_733(unsigned char *p, uint32_t value) { archive_le32enc(p, value); archive_be32enc(p+4, value); } static void set_digit(unsigned char *p, size_t s, int value) { while (s--) { p[s] = '0' + (value % 10); value /= 10; } } #if defined(HAVE_STRUCT_TM_TM_GMTOFF) #define get_gmoffset(tm) ((tm)->tm_gmtoff) #elif defined(HAVE_STRUCT_TM___TM_GMTOFF) #define get_gmoffset(tm) ((tm)->__tm_gmtoff) #else static long get_gmoffset(struct tm *tm) { long offset; #if defined(HAVE__GET_TIMEZONE) _get_timezone(&offset); #elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) offset = _timezone; #else offset = timezone; #endif offset *= -1; if (tm->tm_isdst) offset += 3600; return (offset); } #endif static void get_tmfromtime(struct tm *tm, time_t *t) { #if HAVE_LOCALTIME_R tzset(); localtime_r(t, tm); #elif HAVE__LOCALTIME64_S _localtime64_s(tm, t); #else memcpy(tm, localtime(t), sizeof(*tm)); #endif } /* * Date and Time Format. * ISO9660 Standard 8.4.26.1 */ static void set_date_time(unsigned char *p, time_t t) { struct tm tm; get_tmfromtime(&tm, &t); set_digit(p, 4, tm.tm_year + 1900); set_digit(p+4, 2, tm.tm_mon + 1); set_digit(p+6, 2, tm.tm_mday); set_digit(p+8, 2, tm.tm_hour); set_digit(p+10, 2, tm.tm_min); set_digit(p+12, 2, tm.tm_sec); set_digit(p+14, 2, 0); set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15))); } static void set_date_time_null(unsigned char *p) { memset(p, '0', 16); p[16] = 0; } static void set_time_915(unsigned char *p, time_t t) { struct tm tm; get_tmfromtime(&tm, &t); set_num_711(p+0, tm.tm_year); set_num_711(p+1, tm.tm_mon+1); set_num_711(p+2, tm.tm_mday); set_num_711(p+3, tm.tm_hour); set_num_711(p+4, tm.tm_min); set_num_711(p+5, tm.tm_sec); set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15))); } /* * Write SUSP "CE" System Use Entry. */ static int set_SUSP_CE(unsigned char *p, int location, int offset, int size) { unsigned char *bp = p -1; /* Extend the System Use Area * "CE" Format: * len ver * +----+----+----+----+-----------+-----------+ * | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 | * +----+----+----+----+-----------+-----------+ * 0 1 2 3 4 12 20 * +-----------+ * | LOCATION3 | * +-----------+ * 20 28 * LOCATION1 : Location of Continuation of System Use Area. * LOCATION2 : Offset to Start of Continuation. * LOCATION3 : Length of the Continuation. */ bp[1] = 'C'; bp[2] = 'E'; bp[3] = RR_CE_SIZE; /* length */ bp[4] = 1; /* version */ set_num_733(bp+5, location); set_num_733(bp+13, offset); set_num_733(bp+21, size); return (RR_CE_SIZE); } /* * The functions, which names are beginning with extra_, are used to * control extra records. * The maximum size of a Directory Record is 254. When a filename is * very long, all of RRIP data of a file won't stored to the Directory * Record and so remaining RRIP data store to an extra record instead. */ static unsigned char * extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent, struct ctl_extr_rec *ctl) { ctl->bp = bp; if (bp != NULL) bp += dr_len; ctl->use_extr = 0; ctl->isoent = isoent; ctl->ce_ptr = NULL; ctl->cur_len = ctl->dr_len = dr_len; ctl->limit = DR_LIMIT; return (bp); } static void extra_close_record(struct ctl_extr_rec *ctl, int ce_size) { int padding = 0; if (ce_size > 0) extra_tell_used_size(ctl, ce_size); /* Padding. */ if (ctl->cur_len & 0x01) { ctl->cur_len++; if (ctl->bp != NULL) ctl->bp[ctl->cur_len] = 0; padding = 1; } if (ctl->use_extr) { if (ctl->ce_ptr != NULL) set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc, ctl->extr_off, ctl->cur_len - padding); } else ctl->dr_len = ctl->cur_len; } #define extra_space(ctl) ((ctl)->limit - (ctl)->cur_len) static unsigned char * extra_next_record(struct ctl_extr_rec *ctl, int length) { int cur_len = ctl->cur_len;/* save cur_len */ /* Close the current extra record or Directory Record. */ extra_close_record(ctl, RR_CE_SIZE); /* Get a next extra record. */ ctl->use_extr = 1; if (ctl->bp != NULL) { /* Storing data into an extra record. */ unsigned char *p; /* Save the pointer where a CE extension will be * stored to. */ ctl->ce_ptr = &ctl->bp[cur_len+1]; p = extra_get_record(ctl->isoent, &ctl->limit, &ctl->extr_off, &ctl->extr_loc); ctl->bp = p - 1;/* the base of bp offset is 1. */ } else /* Calculating the size of an extra record. */ (void)extra_get_record(ctl->isoent, &ctl->limit, NULL, NULL); ctl->cur_len = 0; /* Check if an extra record is almost full. * If so, get a next one. */ if (extra_space(ctl) < length) (void)extra_next_record(ctl, length); return (ctl->bp); } static inline struct extr_rec * extra_last_record(struct isoent *isoent) { if (isoent->extr_rec_list.first == NULL) return (NULL); return ((struct extr_rec *)(void *) ((char *)(isoent->extr_rec_list.last) - offsetof(struct extr_rec, next))); } static unsigned char * extra_get_record(struct isoent *isoent, int *space, int *off, int *loc) { struct extr_rec *rec; isoent = isoent->parent; if (off != NULL) { /* Storing data into an extra record. */ rec = isoent->extr_rec_list.current; if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) rec = rec->next; } else { /* Calculating the size of an extra record. */ rec = extra_last_record(isoent); if (rec == NULL || DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) { rec = malloc(sizeof(*rec)); if (rec == NULL) return (NULL); rec->location = 0; rec->offset = 0; /* Insert `rec` into the tail of isoent->extr_rec_list */ rec->next = NULL; /* * Note: testing isoent->extr_rec_list.last == NULL * here is really unneeded since it has been already * initialized at isoent_new function but Clang Static * Analyzer claims that it is dereference of null * pointer. */ if (isoent->extr_rec_list.last == NULL) isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); *isoent->extr_rec_list.last = rec; isoent->extr_rec_list.last = &(rec->next); } } *space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY; if (*space & 0x01) *space -= 1;/* Keep padding space. */ if (off != NULL) *off = rec->offset; if (loc != NULL) *loc = rec->location; isoent->extr_rec_list.current = rec; return (&rec->buf[rec->offset]); } static void extra_tell_used_size(struct ctl_extr_rec *ctl, int size) { struct isoent *isoent; struct extr_rec *rec; if (ctl->use_extr) { isoent = ctl->isoent->parent; rec = isoent->extr_rec_list.current; if (rec != NULL) rec->offset += size; } ctl->cur_len += size; } static int extra_setup_location(struct isoent *isoent, int location) { struct extr_rec *rec; int cnt; cnt = 0; rec = isoent->extr_rec_list.first; isoent->extr_rec_list.current = rec; while (rec) { cnt++; rec->location = location++; rec->offset = 0; rec = rec->next; } return (cnt); } /* * Create the RRIP entries. */ static int set_directory_record_rr(unsigned char *bp, int dr_len, struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t) { /* Flags(BP 5) of the Rockridge "RR" System Use Field */ unsigned char rr_flag; #define RR_USE_PX 0x01 #define RR_USE_PN 0x02 #define RR_USE_SL 0x04 #define RR_USE_NM 0x08 #define RR_USE_CL 0x10 #define RR_USE_PL 0x20 #define RR_USE_RE 0x40 #define RR_USE_TF 0x80 int length; struct ctl_extr_rec ctl; struct isoent *rr_parent, *pxent; struct isofile *file; bp = extra_open_record(bp, dr_len, isoent, &ctl); if (t == DIR_REC_PARENT) { rr_parent = isoent->rr_parent; pxent = isoent->parent; if (rr_parent != NULL) isoent = rr_parent; else isoent = isoent->parent; } else { rr_parent = NULL; pxent = isoent; } file = isoent->file; if (t != DIR_REC_NORMAL) { rr_flag = RR_USE_PX | RR_USE_TF; if (rr_parent != NULL) rr_flag |= RR_USE_PL; } else { rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF; if (archive_entry_filetype(file->entry) == AE_IFLNK) rr_flag |= RR_USE_SL; if (isoent->rr_parent != NULL) rr_flag |= RR_USE_RE; if (isoent->rr_child != NULL) rr_flag |= RR_USE_CL; if (archive_entry_filetype(file->entry) == AE_IFCHR || archive_entry_filetype(file->entry) == AE_IFBLK) rr_flag |= RR_USE_PN; #ifdef COMPAT_MKISOFS /* * mkisofs 2.01.01a63 records "RE" extension to * the entry of "rr_moved" directory. * I don't understand this behavior. */ if (isoent->virtual && isoent->parent == iso9660->primary.rootent && strcmp(isoent->file->basename.s, "rr_moved") == 0) rr_flag |= RR_USE_RE; #endif } /* Write "SP" System Use Entry. */ if (t == DIR_REC_SELF && isoent == isoent->parent) { length = 7; if (bp != NULL) { bp[1] = 'S'; bp[2] = 'P'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = 0xBE; /* Check Byte */ bp[6] = 0xEF; /* Check Byte */ bp[7] = 0; bp += length; } extra_tell_used_size(&ctl, length); } /* Write "RR" System Use Entry. */ length = 5; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'R'; bp[2] = 'R'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = rr_flag; bp += length; } extra_tell_used_size(&ctl, length); /* Write "NM" System Use Entry. */ if (rr_flag & RR_USE_NM) { /* * "NM" Format: * e.g. a basename is 'foo' * len ver flg * +----+----+----+----+----+----+----+----+ * | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'| * +----+----+----+----+----+----+----+----+ * <----------------- len -----------------> */ size_t nmlen = file->basename.length; const char *nm = file->basename.s; size_t nmmax; if (extra_space(&ctl) < 6) bp = extra_next_record(&ctl, 6); if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; bp[4] = 1; /* version */ } nmmax = extra_space(&ctl); if (nmmax > 0xff) nmmax = 0xff; while (nmlen + 5 > nmmax) { length = (int)nmmax; if (bp != NULL) { bp[3] = length; bp[5] = 0x01;/* Alternate Name continues * in next "NM" field */ memcpy(bp+6, nm, length - 5); bp += length; } nmlen -= length - 5; nm += length - 5; extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 6) { bp = extra_next_record(&ctl, 6); nmmax = extra_space(&ctl); if (nmmax > 0xff) nmmax = 0xff; } if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; bp[4] = 1; /* version */ } } length = 5 + (int)nmlen; if (bp != NULL) { bp[3] = length; bp[5] = 0; memcpy(bp+6, nm, nmlen); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PX" System Use Entry. */ if (rr_flag & RR_USE_PX) { /* * "PX" Format: * len ver * +----+----+----+----+-----------+-----------+ * | 'P'| 'X'| 2C | 01 | FILE MODE | LINKS | * +----+----+----+----+-----------+-----------+ * 0 1 2 3 4 12 20 * +-----------+-----------+------------------+ * | USER ID | GROUP ID |FILE SERIAL NUMBER| * +-----------+-----------+------------------+ * 20 28 36 44 */ length = 44; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { mode_t mode; int64_t uid; int64_t gid; mode = archive_entry_mode(file->entry); uid = archive_entry_uid(file->entry); gid = archive_entry_gid(file->entry); if (iso9660->opt.rr == OPT_RR_USEFUL) { /* * This action is simular mkisofs -r option * but our rockridge=useful option does not * set a zero to uid and gid. */ /* set all read bit ON */ mode |= 0444; #if !defined(_WIN32) && !defined(__CYGWIN__) if (mode & 0111) #endif /* set all exec bit ON */ mode |= 0111; /* clear all write bits. */ mode &= ~0222; /* clear setuid,setgid,sticky bits. */ mode &= ~07000; } bp[1] = 'P'; bp[2] = 'X'; bp[3] = length; bp[4] = 1; /* version */ /* file mode */ set_num_733(bp+5, mode); /* file links (stat.st_nlink) */ set_num_733(bp+13, archive_entry_nlink(file->entry)); set_num_733(bp+21, (uint32_t)uid); set_num_733(bp+29, (uint32_t)gid); /* File Serial Number */ if (pxent->dir) set_num_733(bp+37, pxent->dir_location); else if (file->hardlink_target != NULL) set_num_733(bp+37, file->hardlink_target->cur_content->location); else set_num_733(bp+37, file->cur_content->location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "SL" System Use Entry. */ if (rr_flag & RR_USE_SL) { /* * "SL" Format: * e.g. a symbolic name is 'foo/bar' * len ver flg * +----+----+----+----+----+------------+ * | 'S'| 'L'| 0F | 01 | 00 | components | * +----+----+----+----+----+-----+------+ * 0 1 2 3 4 5 ...|... 15 * <----------------- len --------+------> * components : | * cflg clen | * +----+----+----+----+----+ | * | 00 | 03 | 'f'| 'o'| 'o'| <---+ * +----+----+----+----+----+ | * 5 6 7 8 9 10 | * cflg clen | * +----+----+----+----+----+ | * | 00 | 03 | 'b'| 'a'| 'r'| <---+ * +----+----+----+----+----+ * 10 11 12 13 14 15 * * - cflg : flag of component * - clen : length of component */ const char *sl; char sl_last; if (extra_space(&ctl) < 7) bp = extra_next_record(&ctl, 7); sl = file->symlink.s; sl_last = '\0'; if (bp != NULL) { bp[1] = 'S'; bp[2] = 'L'; bp[4] = 1; /* version */ } for (;;) { unsigned char *nc, *cf, *cl, cldmy = 0; int sllen, slmax; slmax = extra_space(&ctl); if (slmax > 0xff) slmax = 0xff; if (bp != NULL) nc = &bp[6]; else nc = NULL; cf = cl = NULL; sllen = 0; while (*sl && sllen + 11 < slmax) { if (sl_last == '\0' && sl[0] == '/') { /* * flg len * +----+----+ * | 08 | 00 | ROOT component. * +----+----+ ("/") * * Root component has to appear * at the first component only. */ if (nc != NULL) { cf = nc++; *cf = 0x08; /* ROOT */ *nc++ = 0; } sllen += 2; sl++; sl_last = '/'; cl = NULL; continue; } if (((sl_last == '\0' || sl_last == '/') && sl[0] == '.' && sl[1] == '.' && (sl[2] == '/' || sl[2] == '\0')) || (sl[0] == '/' && sl[1] == '.' && sl[2] == '.' && (sl[3] == '/' || sl[3] == '\0'))) { /* * flg len * +----+----+ * | 04 | 00 | PARENT component. * +----+----+ ("..") */ if (nc != NULL) { cf = nc++; *cf = 0x04; /* PARENT */ *nc++ = 0; } sllen += 2; if (sl[0] == '/') sl += 3;/* skip "/.." */ else sl += 2;/* skip ".." */ sl_last = '.'; cl = NULL; continue; } if (((sl_last == '\0' || sl_last == '/') && sl[0] == '.' && (sl[1] == '/' || sl[1] == '\0')) || (sl[0] == '/' && sl[1] == '.' && (sl[2] == '/' || sl[2] == '\0'))) { /* * flg len * +----+----+ * | 02 | 00 | CURREENT component. * +----+----+ (".") */ if (nc != NULL) { cf = nc++; *cf = 0x02; /* CURRENT */ *nc++ = 0; } sllen += 2; if (sl[0] == '/') sl += 2;/* skip "/." */ else sl ++; /* skip "." */ sl_last = '.'; cl = NULL; continue; } if (sl[0] == '/' || cl == NULL) { if (nc != NULL) { cf = nc++; *cf = 0; cl = nc++; *cl = 0; } else cl = &cldmy; sllen += 2; if (sl[0] == '/') { sl_last = *sl++; continue; } } sl_last = *sl++; if (nc != NULL) { *nc++ = sl_last; (*cl) ++; } sllen++; } if (*sl) { length = 5 + sllen; if (bp != NULL) { /* * Mark flg as CONTINUE component. */ *cf |= 0x01; /* * len ver flg * +----+----+----+----+----+- * | 'S'| 'L'| XX | 01 | 01 | * +----+----+----+----+----+- * ^ * continues in next "SL" */ bp[3] = length; bp[5] = 0x01;/* This Symbolic Link * continues in next * "SL" field */ bp += length; } extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 11) bp = extra_next_record(&ctl, 11); if (bp != NULL) { /* Next 'SL' */ bp[1] = 'S'; bp[2] = 'L'; bp[4] = 1; /* version */ } } else { length = 5 + sllen; if (bp != NULL) { bp[3] = length; bp[5] = 0; bp += length; } extra_tell_used_size(&ctl, length); break; } } } /* Write "TF" System Use Entry. */ if (rr_flag & RR_USE_TF) { /* * "TF" Format: * len ver * +----+----+----+----+-----+-------------+ * | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS | * +----+----+----+----+-----+-------------+ * 0 1 2 3 4 5 XX * TIME STAMPS : ISO 9660 Standard 9.1.5. * If TF_LONG_FORM FLAGS is set, * use ISO9660 Standard 8.4.26.1. */ #define TF_CREATION 0x01 /* Creation time recorded */ #define TF_MODIFY 0x02 /* Modification time recorded */ #define TF_ACCESS 0x04 /* Last Access time recorded */ #define TF_ATTRIBUTES 0x08 /* Last Attribute Change time recorded */ #define TF_BACKUP 0x10 /* Last Backup time recorded */ #define TF_EXPIRATION 0x20 /* Expiration time recorded */ #define TF_EFFECTIVE 0x40 /* Effective time recorded */ #define TF_LONG_FORM 0x80 /* ISO 9660 17-byte time format used */ unsigned char tf_flags; length = 5; tf_flags = 0; #ifndef COMPAT_MKISOFS if (archive_entry_birthtime_is_set(file->entry) && archive_entry_birthtime(file->entry) <= archive_entry_mtime(file->entry)) { length += 7; tf_flags |= TF_CREATION; } #endif if (archive_entry_mtime_is_set(file->entry)) { length += 7; tf_flags |= TF_MODIFY; } if (archive_entry_atime_is_set(file->entry)) { length += 7; tf_flags |= TF_ACCESS; } if (archive_entry_ctime_is_set(file->entry)) { length += 7; tf_flags |= TF_ATTRIBUTES; } if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'T'; bp[2] = 'F'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = tf_flags; bp += 5; /* Creation time */ if (tf_flags & TF_CREATION) { set_time_915(bp+1, archive_entry_birthtime(file->entry)); bp += 7; } /* Modification time */ if (tf_flags & TF_MODIFY) { set_time_915(bp+1, archive_entry_mtime(file->entry)); bp += 7; } /* Last Access time */ if (tf_flags & TF_ACCESS) { set_time_915(bp+1, archive_entry_atime(file->entry)); bp += 7; } /* Last Attribute Change time */ if (tf_flags & TF_ATTRIBUTES) { set_time_915(bp+1, archive_entry_ctime(file->entry)); bp += 7; } } extra_tell_used_size(&ctl, length); } /* Write "RE" System Use Entry. */ if (rr_flag & RR_USE_RE) { /* * "RE" Format: * len ver * +----+----+----+----+ * | 'R'| 'E'| 04 | 01 | * +----+----+----+----+ * 0 1 2 3 4 */ length = 4; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'R'; bp[2] = 'E'; bp[3] = length; bp[4] = 1; /* version */ bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PL" System Use Entry. */ if (rr_flag & RR_USE_PL) { /* * "PL" Format: * len ver * +----+----+----+----+------------+ * | 'P'| 'L'| 0C | 01 | *LOCATION | * +----+----+----+----+------------+ * 0 1 2 3 4 12 * *LOCATION: location of parent directory */ length = 12; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'P'; bp[2] = 'L'; bp[3] = length; bp[4] = 1; /* version */ set_num_733(bp + 5, rr_parent->dir_location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "CL" System Use Entry. */ if (rr_flag & RR_USE_CL) { /* * "CL" Format: * len ver * +----+----+----+----+------------+ * | 'C'| 'L'| 0C | 01 | *LOCATION | * +----+----+----+----+------------+ * 0 1 2 3 4 12 * *LOCATION: location of child directory */ length = 12; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'C'; bp[2] = 'L'; bp[3] = length; bp[4] = 1; /* version */ set_num_733(bp + 5, isoent->rr_child->dir_location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PN" System Use Entry. */ if (rr_flag & RR_USE_PN) { /* * "PN" Format: * len ver * +----+----+----+----+------------+------------+ * | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low | * +----+----+----+----+------------+------------+ * 0 1 2 3 4 12 20 */ length = 20; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { uint64_t dev; bp[1] = 'P'; bp[2] = 'N'; bp[3] = length; bp[4] = 1; /* version */ dev = (uint64_t)archive_entry_rdev(file->entry); set_num_733(bp + 5, (uint32_t)(dev >> 32)); set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF)); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "ZF" System Use Entry. */ if (file->zisofs.header_size) { /* * "ZF" Format: * len ver * +----+----+----+----+----+----+-------------+ * | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size | * +----+----+----+----+----+----+-------------+ * 0 1 2 3 4 5 6 7 * +--------------------+-------------------+ * | Log2 of block Size | Uncompressed Size | * +--------------------+-------------------+ * 7 8 16 */ length = 16; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'Z'; bp[2] = 'F'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = 'p'; bp[6] = 'z'; bp[7] = file->zisofs.header_size; bp[8] = file->zisofs.log2_bs; set_num_733(bp + 9, file->zisofs.uncompressed_size); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "CE" System Use Entry. */ if (t == DIR_REC_SELF && isoent == isoent->parent) { length = RR_CE_SIZE; if (bp != NULL) set_SUSP_CE(bp+1, iso9660->location_rrip_er, 0, RRIP_ER_SIZE); extra_tell_used_size(&ctl, length); } extra_close_record(&ctl, 0); return (ctl.dr_len); } /* * Write data of a Directory Record or calculate writing bytes itself. * If parameter `p' is NULL, calculates the size of writing data, which * a Directory Record needs to write, then it saved and return * the calculated size. * Parameter `n' is a remaining size of buffer. when parameter `p' is * not NULL, check whether that `n' is not less than the saved size. * if that `n' is small, return zero. * * This format of the Directory Record is according to * ISO9660 Standard 9.1 */ static int set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t, enum vdd_type vdd_type) { unsigned char *bp; size_t dr_len; size_t fi_len; if (p != NULL) { /* * Check whether a write buffer size is less than the * saved size which is needed to write this Directory * Record. */ switch (t) { case DIR_REC_VD: dr_len = isoent->dr_len.vd; break; case DIR_REC_SELF: dr_len = isoent->dr_len.self; break; case DIR_REC_PARENT: dr_len = isoent->dr_len.parent; break; case DIR_REC_NORMAL: default: dr_len = isoent->dr_len.normal; break; } if (dr_len > n) return (0);/* Needs more buffer size. */ } if (t == DIR_REC_NORMAL && isoent->identifier != NULL) fi_len = isoent->id_len; else fi_len = 1; if (p != NULL) { struct isoent *xisoent; struct isofile *file; unsigned char flag; if (t == DIR_REC_PARENT) xisoent = isoent->parent; else xisoent = isoent; file = isoent->file; if (file->hardlink_target != NULL) file = file->hardlink_target; /* Make a file flag. */ if (xisoent->dir) flag = FILE_FLAG_DIRECTORY; else { if (file->cur_content->next != NULL) flag = FILE_FLAG_MULTI_EXTENT; else flag = 0; } bp = p -1; /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ if (xisoent->dir) set_num_733(bp+3, xisoent->dir_location); else set_num_733(bp+3, file->cur_content->location); /* Data Length */ if (xisoent->dir) set_num_733(bp+11, xisoent->dir_block * LOGICAL_BLOCK_SIZE); else set_num_733(bp+11, (uint32_t)file->cur_content->size); /* Recording Date and Time */ /* NOTE: * If a file type is symbolic link, you are seeing this * field value is different from a value mkisofs makes. * libarchive uses lstat to get this one, but it * seems mkisofs uses stat to get. */ set_time_915(bp+19, archive_entry_mtime(xisoent->file->entry)); /* File Flags */ bp[26] = flag; /* File Unit Size */ set_num_711(bp+27, 0); /* Interleave Gap Size */ set_num_711(bp+28, 0); /* Volume Sequence Number */ set_num_723(bp+29, iso9660->volume_sequence_number); /* Length of File Identifier */ set_num_711(bp+33, (unsigned char)fi_len); /* File Identifier */ switch (t) { case DIR_REC_VD: case DIR_REC_SELF: set_num_711(bp+34, 0); break; case DIR_REC_PARENT: set_num_711(bp+34, 1); break; case DIR_REC_NORMAL: if (isoent->identifier != NULL) memcpy(bp+34, isoent->identifier, fi_len); else set_num_711(bp+34, 0); break; } } else bp = NULL; dr_len = 33 + fi_len; /* Padding Field */ if (dr_len & 0x01) { dr_len ++; if (p != NULL) bp[dr_len] = 0; } /* Volume Descriptor does not record extension. */ if (t == DIR_REC_VD) { if (p != NULL) /* Length of Directory Record */ set_num_711(p, (unsigned char)dr_len); else isoent->dr_len.vd = (int)dr_len; return ((int)dr_len); } /* Rockridge */ if (iso9660->opt.rr && vdd_type != VDD_JOLIET) dr_len = set_directory_record_rr(bp, (int)dr_len, isoent, iso9660, t); if (p != NULL) /* Length of Directory Record */ set_num_711(p, (unsigned char)dr_len); else { /* * Save the size which is needed to write this * Directory Record. */ switch (t) { case DIR_REC_VD: /* This case does not come, but compiler * complains that DIR_REC_VD not handled * in switch .... */ break; case DIR_REC_SELF: isoent->dr_len.self = (int)dr_len; break; case DIR_REC_PARENT: isoent->dr_len.parent = (int)dr_len; break; case DIR_REC_NORMAL: isoent->dr_len.normal = (int)dr_len; break; } } return ((int)dr_len); } /* * Calculate the size of a directory record. */ static inline int get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent, enum dir_rec_type t, enum vdd_type vdd_type) { return (set_directory_record(NULL, SIZE_MAX, isoent, iso9660, t, vdd_type)); } /* * Manage to write ISO-image data with wbuff to reduce calling * __archive_write_output() for performance. */ static inline unsigned char * wb_buffptr(struct archive_write *a) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; return (&(iso9660->wbuff[sizeof(iso9660->wbuff) - iso9660->wbuff_remaining])); } static int wb_write_out(struct archive_write *a) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; size_t wsize, nw; int r; wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; nw = wsize % LOGICAL_BLOCK_SIZE; if (iso9660->wbuff_type == WB_TO_STREAM) r = __archive_write_output(a, iso9660->wbuff, wsize - nw); else r = write_to_temp(a, iso9660->wbuff, wsize - nw); /* Increase the offset. */ iso9660->wbuff_offset += wsize - nw; if (iso9660->wbuff_offset > iso9660->wbuff_written) iso9660->wbuff_written = iso9660->wbuff_offset; iso9660->wbuff_remaining = sizeof(iso9660->wbuff); if (nw) { iso9660->wbuff_remaining -= nw; memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw); } return (r); } static int wb_consume(struct archive_write *a, size_t size) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; if (size > iso9660->wbuff_remaining || iso9660->wbuff_remaining == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal Programing error: iso9660:wb_consume()" " size=%jd, wbuff_remaining=%jd", (intmax_t)size, (intmax_t)iso9660->wbuff_remaining); return (ARCHIVE_FATAL); } iso9660->wbuff_remaining -= size; if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE) return (wb_write_out(a)); return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int wb_set_offset(struct archive_write *a, int64_t off) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; int64_t used, ext_bytes; if (iso9660->wbuff_type != WB_TO_TEMP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal Programing error: iso9660:wb_set_offset()"); return (ARCHIVE_FATAL); } used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; if (iso9660->wbuff_offset + used > iso9660->wbuff_tail) iso9660->wbuff_tail = iso9660->wbuff_offset + used; if (iso9660->wbuff_offset < iso9660->wbuff_written) { if (used > 0 && write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->wbuff_offset = iso9660->wbuff_written; lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET); iso9660->wbuff_remaining = sizeof(iso9660->wbuff); used = 0; } if (off < iso9660->wbuff_offset) { /* * Write out waiting data. */ if (used > 0) { if (wb_write_out(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); } lseek(iso9660->temp_fd, off, SEEK_SET); iso9660->wbuff_offset = off; iso9660->wbuff_remaining = sizeof(iso9660->wbuff); } else if (off <= iso9660->wbuff_tail) { iso9660->wbuff_remaining = (size_t) (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset)); } else { ext_bytes = off - iso9660->wbuff_tail; iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff) - (iso9660->wbuff_tail - iso9660->wbuff_offset)); while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) { if (write_null(a, (size_t)iso9660->wbuff_remaining) != ARCHIVE_OK) return (ARCHIVE_FATAL); ext_bytes -= iso9660->wbuff_remaining; } if (ext_bytes > 0) { if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ static int write_null(struct archive_write *a, size_t size) { size_t remaining; unsigned char *p, *old; int r; remaining = wb_remaining(a); p = wb_buffptr(a); if (size <= remaining) { memset(p, 0, size); return (wb_consume(a, size)); } memset(p, 0, remaining); r = wb_consume(a, remaining); if (r != ARCHIVE_OK) return (r); size -= remaining; old = p; p = wb_buffptr(a); memset(p, 0, old - p); remaining = wb_remaining(a); while (size) { size_t wsize = size; if (wsize > remaining) wsize = remaining; r = wb_consume(a, wsize); if (r != ARCHIVE_OK) return (r); size -= wsize; } return (ARCHIVE_OK); } /* * Write Volume Descriptor Set Terminator */ static int write_VD_terminator(struct archive_write *a) { unsigned char *bp; bp = wb_buffptr(a) -1; set_VD_bp(bp, VDT_TERMINATOR, 1); set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc, struct archive_write *a, struct vdd *vdd, struct archive_string *id, const char *label, int leading_under, enum char_type char_type) { char identifier[256]; struct isoent *isoent; const char *ids; size_t len; int r; if (id->length > 0 && leading_under && id->s[0] != '_') { if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc); else r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc); } else if (id->length > 0) { ids = id->s; if (leading_under) ids++; isoent = isoent_find_entry(vdd->rootent, ids); if (isoent == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Not Found %s `%s'.", label, ids); return (ARCHIVE_FATAL); } len = isoent->ext_off + isoent->ext_len; if (vdd->vdd_type == VDD_JOLIET) { if (len > sizeof(identifier)-2) len = sizeof(identifier)-2; } else { if (len > sizeof(identifier)-1) len = sizeof(identifier)-1; } memcpy(identifier, isoent->identifier, len); identifier[len] = '\0'; if (vdd->vdd_type == VDD_JOLIET) { identifier[len+1] = 0; vdc = VDC_UCS2_DIRECT; } if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, identifier, vdc); else r = set_str_d_characters_bp(a, bp, from, to, identifier, vdc); } else { if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc); else r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc); } return (r); } /* * Write Primary/Supplementary Volume Descriptor */ static int write_VD(struct archive_write *a, struct vdd *vdd) { struct iso9660 *iso9660; unsigned char *bp; uint16_t volume_set_size = 1; char identifier[256]; enum VD_type vdt; enum vdc vdc; unsigned char vd_ver, fst_ver; int r; iso9660 = a->format_data; switch (vdd->vdd_type) { case VDD_JOLIET: vdt = VDT_SUPPLEMENTARY; vd_ver = fst_ver = 1; vdc = VDC_UCS2; break; case VDD_ENHANCED: vdt = VDT_SUPPLEMENTARY; vd_ver = fst_ver = 2; vdc = VDC_LOWERCASE; break; case VDD_PRIMARY: default: vdt = VDT_PRIMARY; vd_ver = fst_ver = 1; #ifdef COMPAT_MKISOFS vdc = VDC_LOWERCASE; #else vdc = VDC_STD; #endif break; } bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, vdt, vd_ver); /* Unused Field */ set_unused_field_bp(bp, 8, 8); /* System Identifier */ get_system_identitier(identifier, sizeof(identifier)); r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc); if (r != ARCHIVE_OK) return (r); /* Volume Identifier */ r = set_str_d_characters_bp(a, bp, 41, 72, iso9660->volume_identifier.s, vdc); if (r != ARCHIVE_OK) return (r); /* Unused Field */ set_unused_field_bp(bp, 73, 80); /* Volume Space Size */ set_num_733(bp+81, iso9660->volume_space_size); if (vdd->vdd_type == VDD_JOLIET) { /* Escape Sequences */ bp[89] = 0x25;/* UCS-2 Level 3 */ bp[90] = 0x2F; bp[91] = 0x45; memset(bp + 92, 0, 120 - 92 + 1); } else { /* Unused Field */ set_unused_field_bp(bp, 89, 120); } /* Volume Set Size */ set_num_723(bp+121, volume_set_size); /* Volume Sequence Number */ set_num_723(bp+125, iso9660->volume_sequence_number); /* Logical Block Size */ set_num_723(bp+129, LOGICAL_BLOCK_SIZE); /* Path Table Size */ set_num_733(bp+133, vdd->path_table_size); /* Location of Occurrence of Type L Path Table */ set_num_731(bp+141, vdd->location_type_L_path_table); /* Location of Optional Occurrence of Type L Path Table */ set_num_731(bp+145, 0); /* Location of Occurrence of Type M Path Table */ set_num_732(bp+149, vdd->location_type_M_path_table); /* Location of Optional Occurrence of Type M Path Table */ set_num_732(bp+153, 0); /* Directory Record for Root Directory(BP 157 to 190) */ set_directory_record(bp+157, 190-157+1, vdd->rootent, iso9660, DIR_REC_VD, vdd->vdd_type); /* Volume Set Identifier */ r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc); if (r != ARCHIVE_OK) return (r); /* Publisher Identifier */ r = set_file_identifier(bp, 319, 446, vdc, a, vdd, &(iso9660->publisher_identifier), "Publisher File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Data Preparer Identifier */ r = set_file_identifier(bp, 447, 574, vdc, a, vdd, &(iso9660->data_preparer_identifier), "Data Preparer File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Application Identifier */ r = set_file_identifier(bp, 575, 702, vdc, a, vdd, &(iso9660->application_identifier), "Application File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Copyright File Identifier */ r = set_file_identifier(bp, 703, 739, vdc, a, vdd, &(iso9660->copyright_file_identifier), "Copyright File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Abstract File Identifier */ r = set_file_identifier(bp, 740, 776, vdc, a, vdd, &(iso9660->abstract_file_identifier), "Abstract File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Bibliongraphic File Identifier */ r = set_file_identifier(bp, 777, 813, vdc, a, vdd, &(iso9660->bibliographic_file_identifier), "Bibliongraphic File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Volume Creation Date and Time */ set_date_time(bp+814, iso9660->birth_time); /* Volume Modification Date and Time */ set_date_time(bp+831, iso9660->birth_time); /* Volume Expiration Date and Time(obsolete) */ set_date_time_null(bp+848); /* Volume Effective Date and Time */ set_date_time(bp+865, iso9660->birth_time); /* File Structure Version */ bp[882] = fst_ver; /* Reserved */ bp[883] = 0; /* Application Use */ memset(bp + 884, 0x20, 1395 - 884 + 1); /* Reserved */ set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } /* * Write Boot Record Volume Descriptor */ static int write_VD_boot_record(struct archive_write *a) { struct iso9660 *iso9660; unsigned char *bp; iso9660 = a->format_data; bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, VDT_BOOT_RECORD, 1); /* Boot System Identifier */ memcpy(bp+8, "EL TORITO SPECIFICATION", 23); set_unused_field_bp(bp, 8+23, 39); /* Unused */ set_unused_field_bp(bp, 40, 71); /* Absolute pointer to first sector of Boot Catalog */ set_num_731(bp+72, iso9660->el_torito.catalog->file->content.location); /* Unused */ set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } enum keytype { KEY_FLG, KEY_STR, KEY_INT, KEY_HEX }; static void set_option_info(struct archive_string *info, int *opt, const char *key, enum keytype type, ...) { va_list ap; char prefix; const char *s; int d; prefix = (*opt==0)? ' ':','; va_start(ap, type); switch (type) { case KEY_FLG: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s%s", prefix, (d == 0)?"!":"", key); break; case KEY_STR: s = va_arg(ap, const char *); archive_string_sprintf(info, "%c%s=%s", prefix, key, s); break; case KEY_INT: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s=%d", prefix, key, d); break; case KEY_HEX: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s=%x", prefix, key, d); break; } va_end(ap); *opt = 1; } /* * Make Non-ISO File System Information */ static int write_information_block(struct archive_write *a) { struct iso9660 *iso9660; char buf[128]; const char *v; int opt, r; struct archive_string info; size_t info_size = LOGICAL_BLOCK_SIZE * NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; iso9660 = (struct iso9660 *)a->format_data; if (info_size > wb_remaining(a)) { r = wb_write_out(a); if (r != ARCHIVE_OK) return (r); } archive_string_init(&info); if (archive_string_ensure(&info, info_size) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memset(info.s, 0, info_size); opt = 0; #if defined(HAVE__CTIME64_S) _ctime64_s(buf, sizeof(buf), &(iso9660->birth_time)); #elif defined(HAVE_CTIME_R) ctime_r(&(iso9660->birth_time), buf); #else strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; #endif archive_string_sprintf(&info, "INFO %s%s", buf, archive_version_string()); if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT) set_option_info(&info, &opt, "abstract-file", KEY_STR, iso9660->abstract_file_identifier.s); if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT) set_option_info(&info, &opt, "application-id", KEY_STR, iso9660->application_identifier.s); if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT) set_option_info(&info, &opt, "allow-vernum", KEY_FLG, iso9660->opt.allow_vernum); if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT) set_option_info(&info, &opt, "biblio-file", KEY_STR, iso9660->bibliographic_file_identifier.s); if (iso9660->opt.boot != OPT_BOOT_DEFAULT) set_option_info(&info, &opt, "boot", KEY_STR, iso9660->el_torito.boot_filename.s); if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT) set_option_info(&info, &opt, "boot-catalog", KEY_STR, iso9660->el_torito.catalog_filename.s); if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT) set_option_info(&info, &opt, "boot-info-table", KEY_FLG, iso9660->opt.boot_info_table); if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT) set_option_info(&info, &opt, "boot-load-seg", KEY_HEX, iso9660->el_torito.boot_load_seg); if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT) set_option_info(&info, &opt, "boot-load-size", KEY_INT, iso9660->el_torito.boot_load_size); if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) { v = "no-emulation"; if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD) v = "fd"; if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK) v = "hard-disk"; set_option_info(&info, &opt, "boot-type", KEY_STR, v); } #ifdef HAVE_ZLIB_H if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT) set_option_info(&info, &opt, "compression-level", KEY_INT, iso9660->zisofs.compression_level); #endif if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT) set_option_info(&info, &opt, "copyright-file", KEY_STR, iso9660->copyright_file_identifier.s); if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT) set_option_info(&info, &opt, "iso-level", KEY_INT, iso9660->opt.iso_level); if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) { if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) set_option_info(&info, &opt, "joliet", KEY_STR, "long"); else set_option_info(&info, &opt, "joliet", KEY_FLG, iso9660->opt.joliet); } if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT) set_option_info(&info, &opt, "limit-depth", KEY_FLG, iso9660->opt.limit_depth); if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT) set_option_info(&info, &opt, "limit-dirs", KEY_FLG, iso9660->opt.limit_dirs); if (iso9660->opt.pad != OPT_PAD_DEFAULT) set_option_info(&info, &opt, "pad", KEY_FLG, iso9660->opt.pad); if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT) set_option_info(&info, &opt, "publisher", KEY_STR, iso9660->publisher_identifier.s); if (iso9660->opt.rr != OPT_RR_DEFAULT) { if (iso9660->opt.rr == OPT_RR_DISABLED) set_option_info(&info, &opt, "rockridge", KEY_FLG, iso9660->opt.rr); else if (iso9660->opt.rr == OPT_RR_STRICT) set_option_info(&info, &opt, "rockridge", KEY_STR, "strict"); else if (iso9660->opt.rr == OPT_RR_USEFUL) set_option_info(&info, &opt, "rockridge", KEY_STR, "useful"); } if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT) set_option_info(&info, &opt, "volume-id", KEY_STR, iso9660->volume_identifier.s); if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT) set_option_info(&info, &opt, "zisofs", KEY_FLG, iso9660->opt.zisofs); memcpy(wb_buffptr(a), info.s, info_size); archive_string_free(&info); return (wb_consume(a, info_size)); } static int write_rr_ER(struct archive_write *a) { unsigned char *p; p = wb_buffptr(a); memset(p, 0, LOGICAL_BLOCK_SIZE); p[0] = 'E'; p[1] = 'R'; p[3] = 0x01; p[2] = RRIP_ER_SIZE; p[4] = RRIP_ER_ID_SIZE; p[5] = RRIP_ER_DSC_SIZE; p[6] = RRIP_ER_SRC_SIZE; p[7] = 0x01; memcpy(&p[8], rrip_identifier, p[4]); memcpy(&p[8+p[4]], rrip_descriptor, p[5]); memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static void calculate_path_table_size(struct vdd *vdd) { int depth, size; struct path_table *pt; pt = vdd->pathtbl; size = 0; for (depth = 0; depth < vdd->max_depth; depth++) { struct isoent **ptbl; int i, cnt; if ((cnt = pt[depth].cnt) == 0) break; ptbl = pt[depth].sorted; for (i = 0; i < cnt; i++) { int len; if (ptbl[i]->identifier == NULL) len = 1; /* root directory */ else len = ptbl[i]->id_len; if (len & 0x01) len++; /* Padding Field */ size += 8 + len; } } vdd->path_table_size = size; vdd->path_table_block = ((size + PATH_TABLE_BLOCK_SIZE -1) / PATH_TABLE_BLOCK_SIZE) * (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE); } static int _write_path_table(struct archive_write *a, int type_m, int depth, struct vdd *vdd) { unsigned char *bp, *wb; struct isoent **ptbl; size_t wbremaining; int i, r, wsize; if (vdd->pathtbl[depth].cnt == 0) return (0); wsize = 0; wb = wb_buffptr(a); wbremaining = wb_remaining(a); bp = wb - 1; ptbl = vdd->pathtbl[depth].sorted; for (i = 0; i < vdd->pathtbl[depth].cnt; i++) { struct isoent *np; size_t len; np = ptbl[i]; if (np->identifier == NULL) len = 1; /* root directory */ else len = np->id_len; if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) { r = wb_consume(a, (bp+1) - wb); if (r < 0) return (r); wb = wb_buffptr(a); wbremaining = wb_remaining(a); bp = wb -1; } /* Length of Directory Identifier */ set_num_711(bp+1, (unsigned char)len); /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ if (type_m) set_num_732(bp+3, np->dir_location); else set_num_731(bp+3, np->dir_location); /* Parent Directory Number */ if (type_m) set_num_722(bp+7, np->parent->dir_number); else set_num_721(bp+7, np->parent->dir_number); /* Directory Identifier */ if (np->identifier == NULL) bp[9] = 0; else memcpy(&bp[9], np->identifier, len); if (len & 0x01) { /* Padding Field */ bp[9+len] = 0; len++; } wsize += 8 + (int)len; bp += 8 + len; } if ((bp + 1) > wb) { r = wb_consume(a, (bp+1)-wb); if (r < 0) return (r); } return (wsize); } static int write_path_table(struct archive_write *a, int type_m, struct vdd *vdd) { int depth, r; size_t path_table_size; r = ARCHIVE_OK; path_table_size = 0; for (depth = 0; depth < vdd->max_depth; depth++) { r = _write_path_table(a, type_m, depth, vdd); if (r < 0) return (r); path_table_size += r; } /* Write padding data. */ path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE; if (path_table_size > 0) r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size); return (r); } static int calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd, struct isoent *isoent, int depth) { struct isoent **enttbl; int bs, block, i; block = 1; bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type); bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type); if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) return (block); enttbl = isoent->children_sorted; for (i = 0; i < isoent->children.cnt; i++) { struct isoent *np = enttbl[i]; struct isofile *file; file = np->file; if (file->hardlink_target != NULL) file = file->hardlink_target; file->cur_content = &(file->content); do { int dr_l; dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL, vdd->vdd_type); if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) { block ++; bs = dr_l; } else bs += dr_l; file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } return (block); } static int _write_directory_descriptors(struct archive_write *a, struct vdd *vdd, struct isoent *isoent, int depth) { struct iso9660 *iso9660 = a->format_data; struct isoent **enttbl; unsigned char *p, *wb; int i, r; int dr_l; p = wb = wb_buffptr(a); #define WD_REMAINING (LOGICAL_BLOCK_SIZE - (p - wb)) p += set_directory_record(p, WD_REMAINING, isoent, iso9660, DIR_REC_SELF, vdd->vdd_type); p += set_directory_record(p, WD_REMAINING, isoent, iso9660, DIR_REC_PARENT, vdd->vdd_type); if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) { memset(p, 0, WD_REMAINING); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } enttbl = isoent->children_sorted; for (i = 0; i < isoent->children.cnt; i++) { struct isoent *np = enttbl[i]; struct isofile *file = np->file; if (file->hardlink_target != NULL) file = file->hardlink_target; file->cur_content = &(file->content); do { dr_l = set_directory_record(p, WD_REMAINING, np, iso9660, DIR_REC_NORMAL, vdd->vdd_type); if (dr_l == 0) { memset(p, 0, WD_REMAINING); r = wb_consume(a, LOGICAL_BLOCK_SIZE); if (r < 0) return (r); p = wb = wb_buffptr(a); dr_l = set_directory_record(p, WD_REMAINING, np, iso9660, DIR_REC_NORMAL, vdd->vdd_type); } p += dr_l; file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } memset(p, 0, WD_REMAINING); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int write_directory_descriptors(struct archive_write *a, struct vdd *vdd) { struct isoent *np; int depth, r; depth = 0; np = vdd->rootent; do { struct extr_rec *extr; r = _write_directory_descriptors(a, vdd, np, depth); if (r < 0) return (r); if (vdd->vdd_type != VDD_JOLIET) { /* * This extract record is used by SUSP,RRIP. * Not for joliet. */ for (extr = np->extr_rec_list.first; extr != NULL; extr = extr->next) { unsigned char *wb; wb = wb_buffptr(a); memcpy(wb, extr->buf, extr->offset); memset(wb + extr->offset, 0, LOGICAL_BLOCK_SIZE - extr->offset); r = wb_consume(a, LOGICAL_BLOCK_SIZE); if (r < 0) return (r); } } if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } /* * Read file contents from the temporary file, and write it. */ static int write_file_contents(struct archive_write *a, int64_t offset, int64_t size) { struct iso9660 *iso9660 = a->format_data; int r; lseek(iso9660->temp_fd, offset, SEEK_SET); while (size) { size_t rsize; ssize_t rs; unsigned char *wb; wb = wb_buffptr(a); rsize = wb_remaining(a); if (rsize > (size_t)size) rsize = (size_t)size; rs = read(iso9660->temp_fd, wb, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } size -= rs; r = wb_consume(a, rs); if (r < 0) return (r); } return (ARCHIVE_OK); } static int write_file_descriptors(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; int64_t blocks, offset; int r; blocks = 0; offset = 0; /* Make the boot catalog contents, and write it. */ if (iso9660->el_torito.catalog != NULL) { r = make_boot_catalog(a); if (r < 0) return (r); } /* Write the boot file contents. */ if (iso9660->el_torito.boot != NULL) { file = iso9660->el_torito.boot->file; blocks = file->content.blocks; offset = file->content.offset_of_temp; if (offset != 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); blocks = 0; offset = 0; } } /* Write out all file contents. */ for (file = iso9660->data_file_list.first; file != NULL; file = file->datanext) { if (!file->write_content) continue; if ((offset + (blocks << LOGICAL_BLOCK_BITS)) < file->content.offset_of_temp) { if (blocks > 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); } blocks = 0; offset = file->content.offset_of_temp; } file->cur_content = &(file->content); do { blocks += file->cur_content->blocks; - /* Next fragument */ + /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } /* Flush out remaining blocks. */ if (blocks > 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); } return (ARCHIVE_OK); } static void isofile_init_entry_list(struct iso9660 *iso9660) { iso9660->all_file_list.first = NULL; iso9660->all_file_list.last = &(iso9660->all_file_list.first); } static void isofile_add_entry(struct iso9660 *iso9660, struct isofile *file) { file->allnext = NULL; *iso9660->all_file_list.last = file; iso9660->all_file_list.last = &(file->allnext); } static void isofile_free_all_entries(struct iso9660 *iso9660) { struct isofile *file, *file_next; file = iso9660->all_file_list.first; while (file != NULL) { file_next = file->allnext; isofile_free(file); file = file_next; } } static void isofile_init_entry_data_file_list(struct iso9660 *iso9660) { iso9660->data_file_list.first = NULL; iso9660->data_file_list.last = &(iso9660->data_file_list.first); } static void isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file) { file->datanext = NULL; *iso9660->data_file_list.last = file; iso9660->data_file_list.last = &(file->datanext); } static struct isofile * isofile_new(struct archive_write *a, struct archive_entry *entry) { struct isofile *file; file = calloc(1, sizeof(*file)); if (file == NULL) return (NULL); if (entry != NULL) file->entry = archive_entry_clone(entry); else file->entry = archive_entry_new2(&a->archive); if (file->entry == NULL) { free(file); return (NULL); } archive_string_init(&(file->parentdir)); archive_string_init(&(file->basename)); archive_string_init(&(file->basename_utf16)); archive_string_init(&(file->symlink)); file->cur_content = &(file->content); return (file); } static void isofile_free(struct isofile *file) { struct content *con, *tmp; con = file->content.next; while (con != NULL) { tmp = con; con = con->next; free(tmp); } archive_entry_free(file->entry); archive_string_free(&(file->parentdir)); archive_string_free(&(file->basename)); archive_string_free(&(file->basename_utf16)); archive_string_free(&(file->symlink)); free(file); } #if defined(_WIN32) || defined(__CYGWIN__) static int cleanup_backslash_1(char *p) { int mb, dos; mb = dos = 0; while (*p) { if (*(unsigned char *)p > 127) mb = 1; if (*p == '\\') { /* If we have not met any multi-byte characters, * we can replace '\' with '/'. */ if (!mb) *p = '/'; dos = 1; } p++; } if (!mb || !dos) return (0); return (-1); } static void cleanup_backslash_2(wchar_t *p) { /* Convert a path-separator from '\' to '/' */ while (*p != L'\0') { if (*p == L'\\') *p = L'/'; p++; } } #endif /* * Generate a parent directory name and a base name from a pathname. */ static int isofile_gen_utility_names(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660; const char *pathname; char *p, *dirname, *slash; size_t len; int ret = ARCHIVE_OK; iso9660 = a->format_data; archive_string_empty(&(file->parentdir)); archive_string_empty(&(file->basename)); archive_string_empty(&(file->basename_utf16)); archive_string_empty(&(file->symlink)); pathname = archive_entry_pathname(file->entry); if (pathname == NULL || pathname[0] == '\0') {/* virtual root */ file->dircnt = 0; return (ret); } /* * Make a UTF-16BE basename if Joliet extension enabled. */ if (iso9660->opt.joliet) { const char *u16, *ulast; size_t u16len, ulen_last; if (iso9660->sconv_to_utf16be == NULL) { iso9660->sconv_to_utf16be = archive_string_conversion_to_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_to_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); iso9660->sconv_from_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_from_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } /* - * Converte a filename to UTF-16BE. + * Convert a filename to UTF-16BE. */ if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len, iso9660->sconv_to_utf16be)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A filename cannot be converted to UTF-16BE;" "You should disable making Joliet extension"); ret = ARCHIVE_WARN; } /* * Make sure a path separator is not in the last; * Remove trailing '/'. */ while (u16len >= 2) { #if defined(_WIN32) || defined(__CYGWIN__) if (u16[u16len-2] == 0 && (u16[u16len-1] == '/' || u16[u16len-1] == '\\')) #else if (u16[u16len-2] == 0 && u16[u16len-1] == '/') #endif { u16len -= 2; } else break; } /* * Find a basename in UTF-16BE. */ ulast = u16; u16len >>= 1; ulen_last = u16len; while (u16len > 0) { #if defined(_WIN32) || defined(__CYGWIN__) if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\')) #else if (u16[0] == 0 && u16[1] == '/') #endif { ulast = u16 + 2; ulen_last = u16len -1; } u16 += 2; u16len --; } ulen_last <<= 1; if (archive_string_ensure(&(file->basename_utf16), ulen_last) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } /* * Set UTF-16BE basename. */ memcpy(file->basename_utf16.s, ulast, ulen_last); file->basename_utf16.length = ulen_last; } archive_strcpy(&(file->parentdir), pathname); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (cleanup_backslash_1(file->parentdir.s) != 0) { const wchar_t *wp = archive_entry_pathname_w(file->entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->parentdir)); r = archive_string_append_from_wcs(&(file->parentdir), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #endif len = file->parentdir.length; p = dirname = file->parentdir.s; /* * Remove leading '/', '../' and './' elements */ while (*p) { if (p[0] == '/') { p++; len--; } else if (p[0] != '.') break; else if (p[1] == '.' && p[2] == '/') { p += 3; len -= 3; } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) { p += 2; len -= 2; } else if (p[1] == '\0') { p++; len--; } else break; } if (p != dirname) { memmove(dirname, p, len+1); p = dirname; } /* * Remove "/","/." and "/.." elements from tail. */ while (len > 0) { size_t ll = len; if (len > 0 && p[len-1] == '/') { p[len-1] = '\0'; len--; } if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { p[len-2] = '\0'; len -= 2; } if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && p[len-1] == '.') { p[len-3] = '\0'; len -= 3; } if (ll == len) break; } while (*p) { if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ strcpy(p, p+1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ strcpy(p, p+2); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' */ char *rp = p -1; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { strcpy(rp, p+3); p = rp; } else { strcpy(dirname, p+4); p = dirname; } } else p++; } else p++; } p = dirname; len = strlen(p); if (archive_entry_filetype(file->entry) == AE_IFLNK) { /* Convert symlink name too. */ pathname = archive_entry_symlink(file->entry); archive_strcpy(&(file->symlink), pathname); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (archive_strlen(&(file->symlink)) > 0 && cleanup_backslash_1(file->symlink.s) != 0) { const wchar_t *wp = archive_entry_symlink_w(file->entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->symlink)); r = archive_string_append_from_wcs( &(file->symlink), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #endif } /* * - Count up directory elements. * - Find out the position which points the last position of * path separator('/'). */ slash = NULL; file->dircnt = 0; for (; *p != '\0'; p++) if (*p == '/') { slash = p; file->dircnt++; } if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; archive_string_copy(&(file->basename), &(file->parentdir)); archive_string_empty(&(file->parentdir)); *file->parentdir.s = '\0'; return (ret); } /* Make a basename from dirname and slash */ *slash = '\0'; file->parentdir.length = slash - dirname; archive_strcpy(&(file->basename), slash + 1); if (archive_entry_filetype(file->entry) == AE_IFDIR) file->dircnt ++; return (ret); } /* * Register a entry to get a hardlink target. */ static int isofile_register_hardlink(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660 = a->format_data; struct hardlink *hl; const char *pathname; archive_entry_set_nlink(file->entry, 1); pathname = archive_entry_hardlink(file->entry); if (pathname == NULL) { /* This `file` is a hardlink target. */ hl = malloc(sizeof(*hl)); if (hl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } hl->nlink = 1; /* A hardlink target must be the first position. */ file->hlnext = NULL; hl->file_list.first = file; hl->file_list.last = &(file->hlnext); __archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree), (struct archive_rb_node *)hl); } else { hl = (struct hardlink *)__archive_rb_tree_find_node( &(iso9660->hardlink_rbtree), pathname); if (hl != NULL) { /* Insert `file` entry into the tail. */ file->hlnext = NULL; *hl->file_list.last = file; hl->file_list.last = &(file->hlnext); hl->nlink++; } archive_entry_unset_size(file->entry); } return (ARCHIVE_OK); } /* * Hardlinked files have to have the same location of extent. * We have to find out hardlink target entries for the entries * which have a hardlink target name. */ static void isofile_connect_hardlink_files(struct iso9660 *iso9660) { struct archive_rb_node *n; struct hardlink *hl; struct isofile *target, *nf; ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) { hl = (struct hardlink *)n; /* The first entry must be a hardlink target. */ target = hl->file_list.first; archive_entry_set_nlink(target->entry, hl->nlink); /* Set a hardlink target to reference entries. */ for (nf = target->hlnext; nf != NULL; nf = nf->hlnext) { nf->hardlink_target = target; archive_entry_set_nlink(nf->entry, hl->nlink); } } } static int isofile_hd_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct hardlink *h1 = (const struct hardlink *)n1; const struct hardlink *h2 = (const struct hardlink *)n2; return (strcmp(archive_entry_pathname(h1->file_list.first->entry), archive_entry_pathname(h2->file_list.first->entry))); } static int isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key) { const struct hardlink *h = (const struct hardlink *)n; return (strcmp(archive_entry_pathname(h->file_list.first->entry), (const char *)key)); } static void isofile_init_hardlinks(struct iso9660 *iso9660) { static const struct archive_rb_tree_ops rb_ops = { isofile_hd_cmp_node, isofile_hd_cmp_key, }; __archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops); } static void isofile_free_hardlinks(struct iso9660 *iso9660) { struct archive_rb_node *n, *next; for (n = ARCHIVE_RB_TREE_MIN(&(iso9660->hardlink_rbtree)); n;) { next = __archive_rb_tree_iterate(&(iso9660->hardlink_rbtree), n, ARCHIVE_RB_DIR_RIGHT); free(n); n = next; } } static struct isoent * isoent_new(struct isofile *file) { struct isoent *isoent; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node, isoent_cmp_key, }; isoent = calloc(1, sizeof(*isoent)); if (isoent == NULL) return (NULL); isoent->file = file; isoent->children.first = NULL; isoent->children.last = &(isoent->children.first); __archive_rb_tree_init(&(isoent->rbtree), &rb_ops); isoent->subdirs.first = NULL; isoent->subdirs.last = &(isoent->subdirs.first); isoent->extr_rec_list.first = NULL; isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); isoent->extr_rec_list.current = NULL; if (archive_entry_filetype(file->entry) == AE_IFDIR) isoent->dir = 1; return (isoent); } static inline struct isoent * isoent_clone(struct isoent *src) { return (isoent_new(src->file)); } static void _isoent_free(struct isoent *isoent) { struct extr_rec *er, *er_next; free(isoent->children_sorted); free(isoent->identifier); er = isoent->extr_rec_list.first; while (er != NULL) { er_next = er->next; free(er); er = er_next; } free(isoent); } static void isoent_free_all(struct isoent *isoent) { struct isoent *np, *np_temp; if (isoent == NULL) return; np = isoent; for (;;) { if (np->dir) { if (np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; continue; } } for (;;) { np_temp = np; if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; _isoent_free(np_temp); if (np == np_temp) return; } else { np = np->chnext; _isoent_free(np_temp); break; } } } } static struct isoent * isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname) { struct isofile *file; struct isoent *isoent; file = isofile_new(a, NULL); if (file == NULL) return (NULL); archive_entry_set_pathname(file->entry, pathname); archive_entry_unset_mtime(file->entry); archive_entry_unset_atime(file->entry); archive_entry_unset_ctime(file->entry); archive_entry_set_uid(file->entry, getuid()); archive_entry_set_gid(file->entry, getgid()); archive_entry_set_mode(file->entry, 0555 | AE_IFDIR); archive_entry_set_nlink(file->entry, 2); if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { isofile_free(file); return (NULL); } isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) return (NULL); isoent->dir = 1; isoent->virtual = 1; return (isoent); } static int isoent_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct isoent *e1 = (const struct isoent *)n1; const struct isoent *e2 = (const struct isoent *)n2; return (strcmp(e1->file->basename.s, e2->file->basename.s)); } static int isoent_cmp_key(const struct archive_rb_node *n, const void *key) { const struct isoent *e = (const struct isoent *)n; return (strcmp(e->file->basename.s, (const char *)key)); } static int isoent_add_child_head(struct isoent *parent, struct isoent *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); if ((child->chnext = parent->children.first) == NULL) parent->children.last = &(child->chnext); parent->children.first = child; parent->children.cnt++; child->parent = parent; /* Add a child to a sub-directory chain */ if (child->dir) { if ((child->drnext = parent->subdirs.first) == NULL) parent->subdirs.last = &(child->drnext); parent->subdirs.first = child; parent->subdirs.cnt++; child->parent = parent; } else child->drnext = NULL; return (1); } static int isoent_add_child_tail(struct isoent *parent, struct isoent *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); child->chnext = NULL; *parent->children.last = child; parent->children.last = &(child->chnext); parent->children.cnt++; child->parent = parent; /* Add a child to a sub-directory chain */ child->drnext = NULL; if (child->dir) { *parent->subdirs.last = child; parent->subdirs.last = &(child->drnext); parent->subdirs.cnt++; child->parent = parent; } return (1); } static void isoent_remove_child(struct isoent *parent, struct isoent *child) { struct isoent *ent; /* Remove a child entry from children chain. */ ent = parent->children.first; while (ent->chnext != child) ent = ent->chnext; if ((ent->chnext = ent->chnext->chnext) == NULL) parent->children.last = &(ent->chnext); parent->children.cnt--; if (child->dir) { /* Remove a child entry from sub-directory chain. */ ent = parent->subdirs.first; while (ent->drnext != child) ent = ent->drnext; if ((ent->drnext = ent->drnext->drnext) == NULL) parent->subdirs.last = &(ent->drnext); parent->subdirs.cnt--; } __archive_rb_tree_remove_node(&(parent->rbtree), (struct archive_rb_node *)child); } static int isoent_clone_tree(struct archive_write *a, struct isoent **nroot, struct isoent *root) { struct isoent *np, *xroot, *newent; np = root; xroot = NULL; do { newent = isoent_clone(np); if (newent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } if (xroot == NULL) { *nroot = xroot = newent; newent->parent = xroot; } else isoent_add_child_tail(xroot, newent); if (np->dir && np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; xroot = newent; continue; } while (np != np->parent) { if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; xroot = xroot->parent; } else { np = np->chnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } /* * Setup directory locations. */ static void isoent_setup_directory_location(struct iso9660 *iso9660, int location, struct vdd *vdd) { struct isoent *np; int depth; vdd->total_dir_block = 0; depth = 0; np = vdd->rootent; do { int block; np->dir_block = calculate_directory_descriptors( iso9660, vdd, np, depth); vdd->total_dir_block += np->dir_block; np->dir_location = location; location += np->dir_block; block = extra_setup_location(np, location); vdd->total_dir_block += block; location += block; if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); } static void _isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent, int *symlocation) { struct isoent **children; int n; if (isoent->children.cnt == 0) return; children = isoent->children_sorted; for (n = 0; n < isoent->children.cnt; n++) { struct isoent *np; struct isofile *file; np = children[n]; if (np->dir) continue; if (np == iso9660->el_torito.boot) continue; file = np->file; if (file->boot || file->hardlink_target != NULL) continue; if (archive_entry_filetype(file->entry) == AE_IFLNK || file->content.size == 0) { /* * Do not point a valid location. * Make sure entry is not hardlink file. */ file->content.location = (*symlocation)--; continue; } file->write_content = 1; } } /* * Setup file locations. */ static void isoent_setup_file_location(struct iso9660 *iso9660, int location) { struct isoent *isoent; struct isoent *np; struct isofile *file; size_t size; int block; int depth; int joliet; int symlocation; int total_block; iso9660->total_file_block = 0; if ((isoent = iso9660->el_torito.catalog) != NULL) { isoent->file->content.location = location; block = (int)((archive_entry_size(isoent->file->entry) + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); location += block; iso9660->total_file_block += block; } if ((isoent = iso9660->el_torito.boot) != NULL) { isoent->file->content.location = location; size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = (size_t)archive_entry_size(isoent->file->entry); block = ((int)size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS; location += block; iso9660->total_file_block += block; isoent->file->content.blocks = block; } depth = 0; symlocation = -16; if (!iso9660->opt.rr && iso9660->opt.joliet) { joliet = 1; np = iso9660->joliet.rootent; } else { joliet = 0; np = iso9660->primary.rootent; } do { _isoent_file_location(iso9660, np, &symlocation); if (np->subdirs.first != NULL && (joliet || ((iso9660->opt.rr == OPT_RR_DISABLED && depth + 2 < iso9660->primary.max_depth) || (iso9660->opt.rr && depth + 1 < iso9660->primary.max_depth)))) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); total_block = 0; for (file = iso9660->data_file_list.first; file != NULL; file = file->datanext) { if (!file->write_content) continue; file->cur_content = &(file->content); do { file->cur_content->location = location; location += file->cur_content->blocks; total_block += file->cur_content->blocks; - /* Next fragument */ + /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } iso9660->total_file_block += total_block; } static int get_path_component(char *name, size_t n, const char *fn) { char *p; size_t l; p = strchr(fn, '/'); if (p == NULL) { if ((l = strlen(fn)) == 0) return (0); } else l = p - fn; if (l > n -1) return (-1); memcpy(name, fn, l); name[l] = '\0'; return ((int)l); } /* * Add a new entry into the tree. */ static int isoent_tree(struct archive_write *a, struct isoent **isoentpp) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct iso9660 *iso9660 = a->format_data; struct isoent *dent, *isoent, *np; struct isofile *f1, *f2; const char *fn, *p; int l; isoent = *isoentpp; dent = iso9660->primary.rootent; if (isoent->file->parentdir.length > 0) fn = p = isoent->file->parentdir.s; else fn = p = ""; /* * If the path of the parent directory of `isoent' entry is * the same as the path of `cur_dirent', add isoent to * `cur_dirent'. */ if (archive_strlen(&(iso9660->cur_dirstr)) == archive_strlen(&(isoent->file->parentdir)) && strcmp(iso9660->cur_dirstr.s, fn) == 0) { if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) { np = (struct isoent *)__archive_rb_tree_find_node( &(iso9660->cur_dirent->rbtree), isoent->file->basename.s); goto same_entry; } return (ARCHIVE_OK); } for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) { np = NULL; break; } if (l < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); _isoent_free(isoent); return (ARCHIVE_FATAL); } np = isoent_find_child(dent, name); if (np == NULL || fn[0] == '\0') break; /* Find next subdirectory. */ if (!np->dir) { /* NOT Directory! */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "`%s' is not directory, we cannot insert `%s' ", archive_entry_pathname(np->file->entry), archive_entry_pathname(isoent->file->entry)); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FAILED); } fn += l; if (fn[0] == '/') fn++; dent = np; } if (np == NULL) { /* * Create virtual parent directories. */ while (fn[0] != '\0') { struct isoent *vp; struct archive_string as; archive_string_init(&as); archive_strncat(&as, p, fn - p + l); if (as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } vp = isoent_create_virtual_dir(a, iso9660, as.s); if (vp == NULL) { archive_string_free(&as); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FATAL); } archive_string_free(&as); if (vp->file->dircnt > iso9660->dircnt_max) iso9660->dircnt_max = vp->file->dircnt; isoent_add_child_tail(dent, vp); np = vp; fn += l; if (fn[0] == '/') fn++; l = get_path_component(name, sizeof(name), fn); if (l < 0) { archive_string_free(&as); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FATAL); } dent = np; } /* Found out the parent directory where isoent can be * inserted. */ iso9660->cur_dirent = dent; archive_string_empty(&(iso9660->cur_dirstr)); archive_string_ensure(&(iso9660->cur_dirstr), archive_strlen(&(dent->file->parentdir)) + archive_strlen(&(dent->file->basename)) + 2); if (archive_strlen(&(dent->file->parentdir)) + archive_strlen(&(dent->file->basename)) == 0) iso9660->cur_dirstr.s[0] = 0; else { if (archive_strlen(&(dent->file->parentdir)) > 0) { archive_string_copy(&(iso9660->cur_dirstr), &(dent->file->parentdir)); archive_strappend_char(&(iso9660->cur_dirstr), '/'); } archive_string_concat(&(iso9660->cur_dirstr), &(dent->file->basename)); } if (!isoent_add_child_tail(dent, isoent)) { np = (struct isoent *)__archive_rb_tree_find_node( &(dent->rbtree), isoent->file->basename.s); goto same_entry; } return (ARCHIVE_OK); } same_entry: /* * We have already has the entry the filename of which is * the same. */ f1 = np->file; f2 = isoent->file; /* If the file type of entries is different, * we cannot handle it. */ if (archive_entry_filetype(f1->entry) != archive_entry_filetype(f2->entry)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found duplicate entries `%s' and its file type is " "different", archive_entry_pathname(f1->entry)); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FAILED); } /* Swap file entries. */ np->file = f2; isoent->file = f1; np->virtual = 0; _isoent_free(isoent); *isoentpp = np; return (ARCHIVE_OK); } /* * Find a entry from `isoent' */ static struct isoent * isoent_find_child(struct isoent *isoent, const char *child_name) { struct isoent *np; np = (struct isoent *)__archive_rb_tree_find_node( &(isoent->rbtree), child_name); return (np); } /* * Find a entry full-path of which is specified by `fn' parameter, * in the tree. */ static struct isoent * isoent_find_entry(struct isoent *rootent, const char *fn) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct isoent *isoent, *np; int l; isoent = rootent; np = NULL; for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) break; fn += l; if (fn[0] == '/') fn++; np = isoent_find_child(isoent, name); if (np == NULL) break; if (fn[0] == '\0') break;/* We found out the entry */ /* Try sub directory. */ isoent = np; np = NULL; if (!isoent->dir) break;/* Not directory */ } return (np); } /* * Following idr_* functions are used for resolving duplicated filenames * and unreceivable filenames to generate ISO9660/Joliet Identifiers. */ static void idr_relaxed_filenames(char *map) { int i; for (i = 0x21; i <= 0x2F; i++) map[i] = 1; for (i = 0x3A; i <= 0x41; i++) map[i] = 1; for (i = 0x5B; i <= 0x5E; i++) map[i] = 1; map[0x60] = 1; for (i = 0x7B; i <= 0x7E; i++) map[i] = 1; } static void idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr) { idr->idrent_pool = NULL; idr->pool_size = 0; if (vdd->vdd_type != VDD_JOLIET) { if (iso9660->opt.iso_level <= 3) { memcpy(idr->char_map, d_characters_map, sizeof(idr->char_map)); } else { memcpy(idr->char_map, d1_characters_map, sizeof(idr->char_map)); idr_relaxed_filenames(idr->char_map); } } } static void idr_cleanup(struct idr *idr) { free(idr->idrent_pool); } static int idr_ensure_poolsize(struct archive_write *a, struct idr *idr, int cnt) { if (idr->pool_size < cnt) { void *p; const int bk = (1 << 7) - 1; int psize; psize = (cnt + bk) & ~bk; p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } idr->idrent_pool = (struct idrent *)p; idr->pool_size = psize; } return (ARCHIVE_OK); } static int idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax, int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops) { int r; (void)ffmax; /* UNUSED */ r = idr_ensure_poolsize(a, idr, cnt); if (r != ARCHIVE_OK) return (r); __archive_rb_tree_init(&(idr->rbtree), rbt_ops); idr->wait_list.first = NULL; idr->wait_list.last = &(idr->wait_list.first); idr->pool_idx = 0; idr->num_size = num_size; idr->null_size = null_size; return (ARCHIVE_OK); } static void idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff) { struct idrent *idrent, *n; idrent = &(idr->idrent_pool[idr->pool_idx++]); idrent->wnext = idrent->avail = NULL; idrent->isoent = isoent; idrent->weight = weight; idrent->noff = noff; idrent->rename_num = 0; if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) { n = (struct idrent *)__archive_rb_tree_find_node( &(idr->rbtree), idrent->isoent); if (n != NULL) { /* this `idrent' needs to rename. */ idrent->avail = n; *idr->wait_list.last = idrent; idr->wait_list.last = &(idrent->wnext); } } } static void idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize) { unsigned char *p; int wnp_ext_off; wnp_ext_off = wnp->isoent->ext_off; if (wnp->noff + numsize != wnp_ext_off) { p = (unsigned char *)wnp->isoent->identifier; /* Extend the filename; foo.c --> foo___.c */ memmove(p + wnp->noff + numsize, p + wnp_ext_off, wnp->isoent->ext_len + nullsize); wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize; wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len; } } static void idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) { struct idrent *n; unsigned char *p; for (n = idr->wait_list.first; n != NULL; n = n->wnext) { idr_extend_identifier(n, idr->num_size, idr->null_size); p = (unsigned char *)n->isoent->identifier + n->noff; do { fsetnum(p, n->avail->rename_num++); } while (!__archive_rb_tree_insert_node( &(idr->rbtree), &(n->rbnode))); } } static void idr_set_num(unsigned char *p, int num) { static const char xdig[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig); p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))]; num %= sizeof(xdig) * sizeof(xdig); p[1] = xdig[ (num / sizeof(xdig))]; num %= sizeof(xdig); p[2] = xdig[num]; } static void idr_set_num_beutf16(unsigned char *p, int num) { static const uint16_t xdig[] = { 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A }; #define XDIG_CNT (sizeof(xdig)/sizeof(xdig[0])) num %= XDIG_CNT * XDIG_CNT * XDIG_CNT; archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]); num %= XDIG_CNT * XDIG_CNT; archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]); num %= XDIG_CNT; archive_be16enc(p+4, xdig[num]); } /* * Generate ISO9660 Identifier. */ static int isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct iso9660 *iso9660; struct isoent *np; char *p; int l, r; const char *char_map; char allow_ldots, allow_multidot, allow_period, allow_vernum; int fnmax, ffmax, dnmax; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_iso9660, isoent_cmp_key_iso9660 }; if (isoent->children.cnt == 0) return (0); iso9660 = a->format_data; char_map = idr->char_map; if (iso9660->opt.iso_level <= 3) { allow_ldots = 0; allow_multidot = 0; allow_period = 1; allow_vernum = iso9660->opt.allow_vernum; if (iso9660->opt.iso_level == 1) { fnmax = 8; ffmax = 12;/* fnmax + '.' + 3 */ dnmax = 8; } else { fnmax = 30; ffmax = 31; dnmax = 31; } } else { allow_ldots = allow_multidot = 1; allow_period = allow_vernum = 0; if (iso9660->opt.rr) /* * MDR : The maximum size of Directory Record(254). * DRL : A Directory Record Length(33). * CE : A size of SUSP CE System Use Entry(28). * MDR - DRL - CE = 254 - 33 - 28 = 193. */ fnmax = ffmax = dnmax = 193; else /* * XA : CD-ROM XA System Use Extension * Information(14). * MDR - DRL - XA = 254 - 33 -14 = 207. */ fnmax = ffmax = dnmax = 207; } r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops); if (r < 0) return (r); for (np = isoent->children.first; np != NULL; np = np->chnext) { char *dot, *xdot; int ext_off, noff, weight; l = (int)np->file->basename.length; p = malloc(l+31+2+1); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(p, np->file->basename.s, l); p[l] = '\0'; np->identifier = p; dot = xdot = NULL; if (!allow_ldots) { /* * If there is a '.' character at the first byte, * it has to be replaced by '_' character. */ if (*p == '.') *p++ = '_'; } for (;*p; p++) { if (*p & 0x80) { *p = '_'; continue; } if (char_map[(unsigned char)*p]) { /* if iso-level is '4', a character '.' is * allowed by char_map. */ if (*p == '.') { xdot = dot; dot = p; } continue; } if (*p >= 'a' && *p <= 'z') { *p -= 'a' - 'A'; continue; } if (*p == '.') { xdot = dot; dot = p; if (allow_multidot) continue; } *p = '_'; } p = np->identifier; weight = -1; if (dot == NULL) { int nammax; if (np->dir) nammax = dnmax; else nammax = fnmax; if (l > nammax) { p[nammax] = '\0'; weight = nammax; ext_off = nammax; } else ext_off = l; } else { *dot = '.'; ext_off = (int)(dot - p); if (iso9660->opt.iso_level == 1) { if (dot - p <= 8) { if (strlen(dot) > 4) { /* A length of a file extension * must be less than 4 */ dot[4] = '\0'; weight = 0; } } else { p[8] = dot[0]; p[9] = dot[1]; p[10] = dot[2]; p[11] = dot[3]; p[12] = '\0'; weight = 8; ext_off = 8; } } else if (np->dir) { if (l > dnmax) { p[dnmax] = '\0'; weight = dnmax; if (ext_off > dnmax) ext_off = dnmax; } } else if (l > ffmax) { int extlen = (int)strlen(dot); int xdoff; if (xdot != NULL) xdoff = (int)(xdot - p); else xdoff = 0; if (extlen > 1 && xdoff < fnmax-1) { int off; if (extlen > ffmax) extlen = ffmax; off = ffmax - extlen; if (off == 0) { /* A dot('.') character * does't place to the first * byte of identifier. */ off ++; extlen --; } memmove(p+off, dot, extlen); p[ffmax] = '\0'; ext_off = off; weight = off; #ifdef COMPAT_MKISOFS } else if (xdoff >= fnmax-1) { /* Simulate a bug(?) of mkisofs. */ p[fnmax-1] = '\0'; ext_off = fnmax-1; weight = fnmax-1; #endif } else { p[fnmax] = '\0'; ext_off = fnmax; weight = fnmax; } } } /* Save an offset of a file name extension to sort files. */ np->ext_off = ext_off; np->ext_len = (int)strlen(&p[ext_off]); np->id_len = l = ext_off + np->ext_len; /* Make an offset of the number which is used to be set - * hexadecimal number to avoid duplicate identififier. */ + * hexadecimal number to avoid duplicate identifier. */ if (iso9660->opt.iso_level == 1) { if (ext_off >= 5) noff = 5; else noff = ext_off; } else { if (l == ffmax) noff = ext_off - 3; else if (l == ffmax-1) noff = ext_off - 2; else if (l == ffmax-2) noff = ext_off - 1; else noff = ext_off; } /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier. */ idr_resolve(idr, idr_set_num); /* Add a period and a version number to identifiers. */ for (np = isoent->children.first; np != NULL; np = np->chnext) { if (!np->dir && np->rr_child == NULL) { p = np->identifier + np->ext_off + np->ext_len; if (np->ext_len == 0 && allow_period) { *p++ = '.'; np->ext_len = 1; } if (np->ext_len == 1 && !allow_period) { *--p = '\0'; np->ext_len = 0; } np->id_len = np->ext_off + np->ext_len; if (allow_vernum) { *p++ = ';'; *p++ = '1'; np->id_len += 2; } *p = '\0'; } else np->id_len = np->ext_off + np->ext_len; np->mb_len = np->id_len; } return (ARCHIVE_OK); } /* * Generate Joliet Identifier. */ static int isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct iso9660 *iso9660; struct isoent *np; unsigned char *p; size_t l; int r; size_t ffmax, parent_len; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_joliet, isoent_cmp_key_joliet }; if (isoent->children.cnt == 0) return (0); iso9660 = a->format_data; if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) ffmax = 206; else ffmax = 128; r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); if (r < 0) return (r); parent_len = 1; for (np = isoent; np->parent != np; np = np->parent) parent_len += np->mb_len + 1; for (np = isoent->children.first; np != NULL; np = np->chnext) { unsigned char *dot; int ext_off, noff, weight; size_t lt; if ((l = np->file->basename_utf16.length) > ffmax) l = ffmax; p = malloc((l+1)*2); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(p, np->file->basename_utf16.s, l); p[l] = 0; p[l+1] = 0; np->identifier = (char *)p; lt = l; dot = p + l; weight = 0; while (lt > 0) { if (!joliet_allowed_char(p[0], p[1])) archive_be16enc(p, 0x005F); /* '_' */ else if (p[0] == 0 && p[1] == 0x2E) /* '.' */ dot = p; p += 2; lt -= 2; } ext_off = (int)(dot - (unsigned char *)np->identifier); np->ext_off = ext_off; np->ext_len = (int)l - ext_off; np->id_len = (int)l; /* * Get a length of MBS of a full-pathname. */ if (np->file->basename_utf16.length > ffmax) { if (archive_strncpy_l(&iso9660->mbs, (const char *)np->identifier, l, iso9660->sconv_from_utf16be) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, errno, "No memory"); return (ARCHIVE_FATAL); } np->mb_len = (int)iso9660->mbs.length; if (np->mb_len != (int)np->file->basename.length) weight = np->mb_len; } else np->mb_len = (int)np->file->basename.length; /* If a length of full-pathname is longer than 240 bytes, * it violates Joliet extensions regulation. */ if (parent_len > 240 || np->mb_len > 240 || parent_len + np->mb_len > 240) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "The regulation of Joliet extensions;" " A length of a full-pathname of `%s' is " "longer than 240 bytes, (p=%d, b=%d)", archive_entry_pathname(np->file->entry), (int)parent_len, (int)np->mb_len); return (ARCHIVE_FATAL); } /* Make an offset of the number which is used to be set * hexadecimal number to avoid duplicate identifier. */ if (l == ffmax) noff = ext_off - 6; else if (l == ffmax-2) noff = ext_off - 4; else if (l == ffmax-4) noff = ext_off - 2; else noff = ext_off; /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier with Joliet Volume. */ idr_resolve(idr, idr_set_num_beutf16); return (ARCHIVE_OK); } /* * This comparing rule is according to ISO9660 Standard 9.3 */ static int isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2) { const char *s1, *s2; int cmp; int l; s1 = p1->identifier; s2 = p2->identifier; /* Compare File Name */ l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } /* Compare File Name Extension */ if (p1->ext_len == 0 && p2->ext_len == 0) return (0); if (p1->ext_len == 1 && p2->ext_len == 1) return (0); if (p1->ext_len <= 1) return (-1); if (p2->ext_len <= 1) return (1); l = p1->ext_len; if (l > p2->ext_len) l = p2->ext_len; s1 = p1->identifier + p1->ext_off; s2 = p2->identifier + p2->ext_off; if (l > 1) { cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); } if (p1->ext_len < p2->ext_len) { s2 += l; l = p2->ext_len - p1->ext_len; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_len > p2->ext_len) { s1 += l; l = p1->ext_len - p2->ext_len; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } /* Compare File Version Number */ /* No operation. The File Version Number is always one. */ return (cmp); } static int isoent_cmp_node_iso9660(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct idrent *e1 = (const struct idrent *)n1; const struct idrent *e2 = (const struct idrent *)n2; return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent)); } static int isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key) { const struct isoent *isoent = (const struct isoent *)key; const struct idrent *idrent = (const struct idrent *)node; return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent)); } static int isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2) { const unsigned char *s1, *s2; int cmp; int l; s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; /* Compare File Name */ l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } /* Compare File Name Extension */ if (p1->ext_len == 0 && p2->ext_len == 0) return (0); if (p1->ext_len == 2 && p2->ext_len == 2) return (0); if (p1->ext_len <= 2) return (-1); if (p2->ext_len <= 2) return (1); l = p1->ext_len; if (l > p2->ext_len) l = p2->ext_len; s1 = (unsigned char *)(p1->identifier + p1->ext_off); s2 = (unsigned char *)(p2->identifier + p2->ext_off); if (l > 1) { cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); } if (p1->ext_len < p2->ext_len) { s2 += l; l = p2->ext_len - p1->ext_len; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_len > p2->ext_len) { s1 += l; l = p1->ext_len - p2->ext_len; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } /* Compare File Version Number */ /* No operation. The File Version Number is always one. */ return (cmp); } static int isoent_cmp_node_joliet(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct idrent *e1 = (const struct idrent *)n1; const struct idrent *e2 = (const struct idrent *)n2; return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent)); } static int isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key) { const struct isoent *isoent = (const struct isoent *)key; const struct idrent *idrent = (const struct idrent *)node; return (isoent_cmp_joliet_identifier(isoent, idrent->isoent)); } static int isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct archive_rb_node *rn; struct isoent **children; children = malloc(isoent->children.cnt * sizeof(struct isoent *)); if (children == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } isoent->children_sorted = children; ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) { struct idrent *idrent = (struct idrent *)rn; *children ++ = idrent->isoent; } return (ARCHIVE_OK); } /* * - Generate ISO9660 and Joliet identifiers from basenames. * - Sort files by each directory. */ static int isoent_traverse_tree(struct archive_write *a, struct vdd* vdd) { struct iso9660 *iso9660 = a->format_data; struct isoent *np; struct idr idr; int depth; int r; int (*genid)(struct archive_write *, struct isoent *, struct idr *); idr_init(iso9660, vdd, &idr); np = vdd->rootent; depth = 0; if (vdd->vdd_type == VDD_JOLIET) genid = isoent_gen_joliet_identifier; else genid = isoent_gen_iso9660_identifier; do { if (np->virtual && !archive_entry_mtime_is_set(np->file->entry)) { /* Set properly times to virtual directory */ archive_entry_set_mtime(np->file->entry, iso9660->birth_time, 0); archive_entry_set_atime(np->file->entry, iso9660->birth_time, 0); archive_entry_set_ctime(np->file->entry, iso9660->birth_time, 0); } if (np->children.first != NULL) { if (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth) { if (np->children.cnt > 0) iso9660->directories_too_deep = np; } else { /* Generate Identifier */ r = genid(a, np, &idr); if (r < 0) goto exit_traverse_tree; r = isoent_make_sorted_files(a, np, &idr); if (r < 0) goto exit_traverse_tree; if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } } } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); r = ARCHIVE_OK; exit_traverse_tree: idr_cleanup(&idr); return (r); } /* * Collect directory entries into path_table by a directory depth. */ static int isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth) { struct isoent *np; if (rootent == NULL) rootent = vdd->rootent; np = rootent; do { /* Register current directory to pathtable. */ path_table_add_entry(&(vdd->pathtbl[depth]), np); if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != rootent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != rootent); return (ARCHIVE_OK); } /* * The entry whose number of levels in a directory hierarchy is * large than eight relocate to rr_move directory. */ static int isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved, struct isoent *curent, struct isoent **newent) { struct iso9660 *iso9660 = a->format_data; struct isoent *rrmoved, *mvent, *np; if ((rrmoved = *rr_moved) == NULL) { struct isoent *rootent = iso9660->primary.rootent; /* There isn't rr_move entry. * Create rr_move entry and insert it into the root entry. */ rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved"); if (rrmoved == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } /* Add "rr_moved" entry to the root entry. */ isoent_add_child_head(rootent, rrmoved); archive_entry_set_nlink(rootent->file->entry, archive_entry_nlink(rootent->file->entry) + 1); /* Register "rr_moved" entry to second level pathtable. */ path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved); /* Save rr_moved. */ *rr_moved = rrmoved; } /* * Make a clone of curent which is going to be relocated * to rr_moved. */ mvent = isoent_clone(curent); if (mvent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } /* linking.. and use for creating "CL", "PL" and "RE" */ mvent->rr_parent = curent->parent; curent->rr_child = mvent; /* * Move subdirectories from the curent to mvent */ if (curent->children.first != NULL) { *mvent->children.last = curent->children.first; mvent->children.last = curent->children.last; } for (np = mvent->children.first; np != NULL; np = np->chnext) np->parent = mvent; mvent->children.cnt = curent->children.cnt; curent->children.cnt = 0; curent->children.first = NULL; curent->children.last = &curent->children.first; if (curent->subdirs.first != NULL) { *mvent->subdirs.last = curent->subdirs.first; mvent->subdirs.last = curent->subdirs.last; } mvent->subdirs.cnt = curent->subdirs.cnt; curent->subdirs.cnt = 0; curent->subdirs.first = NULL; curent->subdirs.last = &curent->subdirs.first; /* * The mvent becomes a child of the rr_moved entry. */ isoent_add_child_tail(rrmoved, mvent); archive_entry_set_nlink(rrmoved->file->entry, archive_entry_nlink(rrmoved->file->entry) + 1); /* * This entry which relocated to the rr_moved directory * has to set the flag as a file. * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry. */ curent->dir = 0; *newent = mvent; return (ARCHIVE_OK); } static int isoent_rr_move(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct path_table *pt; struct isoent *rootent, *rr_moved; struct isoent *np, *last; int r; pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]); - /* Theare aren't level 8 directories reaching a deepr level. */ + /* There aren't level 8 directories reaching a deeper level. */ if (pt->cnt == 0) return (ARCHIVE_OK); rootent = iso9660->primary.rootent; /* If "rr_moved" directory is already existing, * we have to use it. */ rr_moved = isoent_find_child(rootent, "rr_moved"); if (rr_moved != NULL && rr_moved != rootent->children.first) { /* * It's necessary that rr_move is the first entry * of the root. */ /* Remove "rr_moved" entry from children chain. */ isoent_remove_child(rootent, rr_moved); /* Add "rr_moved" entry into the head of children chain. */ isoent_add_child_head(rootent, rr_moved); } /* * Check level 8 path_table. * If find out sub directory entries, that entries move to rr_move. */ np = pt->first; while (np != NULL) { last = path_table_last_entry(pt); for (; np != NULL; np = np->ptnext) { struct isoent *mvent; struct isoent *newent; if (!np->dir) continue; for (mvent = np->subdirs.first; mvent != NULL; mvent = mvent->drnext) { r = isoent_rr_move_dir(a, &rr_moved, mvent, &newent); if (r < 0) return (r); isoent_collect_dirs(&(iso9660->primary), newent, 2); } } /* If new entries are added to level 8 path_talbe, * its sub directory entries move to rr_move too. */ np = last->ptnext; } return (ARCHIVE_OK); } /* * This comparing rule is according to ISO9660 Standard 6.9.1 */ static int _compare_path_table(const void *v1, const void *v2) { const struct isoent *p1, *p2; const char *s1, *s2; int cmp, l; p1 = *((const struct isoent **)(uintptr_t)v1); p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); - /* Compare indetifier */ + /* Compare identifier */ s1 = p1->identifier; s2 = p2->identifier; l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = strncmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } return (0); } static int _compare_path_table_joliet(const void *v1, const void *v2) { const struct isoent *p1, *p2; const unsigned char *s1, *s2; int cmp, l; p1 = *((const struct isoent **)(uintptr_t)v1); p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); - /* Compare indetifier */ + /* Compare identifier */ s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } return (0); } static inline void path_table_add_entry(struct path_table *pathtbl, struct isoent *ent) { ent->ptnext = NULL; *pathtbl->last = ent; pathtbl->last = &(ent->ptnext); pathtbl->cnt ++; } static inline struct isoent * path_table_last_entry(struct path_table *pathtbl) { if (pathtbl->first == NULL) return (NULL); return (((struct isoent *)(void *) ((char *)(pathtbl->last) - offsetof(struct isoent, ptnext)))); } /* * Sort directory entries in path_table * and assign directory number to each entries. */ static int isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd, int depth, int *dir_number) { struct isoent *np; struct isoent **enttbl; struct path_table *pt; int i; pt = &vdd->pathtbl[depth]; if (pt->cnt == 0) { pt->sorted = NULL; return (ARCHIVE_OK); } enttbl = malloc(pt->cnt * sizeof(struct isoent *)); if (enttbl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } pt->sorted = enttbl; for (np = pt->first; np != NULL; np = np->ptnext) *enttbl ++ = np; enttbl = pt->sorted; switch (vdd->vdd_type) { case VDD_PRIMARY: case VDD_ENHANCED: #ifdef __COMPAR_FN_T qsort(enttbl, pt->cnt, sizeof(struct isoent *), (__compar_fn_t)_compare_path_table); #else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table); #endif break; case VDD_JOLIET: #ifdef __COMPAR_FN_T qsort(enttbl, pt->cnt, sizeof(struct isoent *), (__compar_fn_t)_compare_path_table_joliet); #else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table_joliet); #endif break; } for (i = 0; i < pt->cnt; i++) enttbl[i]->dir_number = (*dir_number)++; return (ARCHIVE_OK); } static int isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd, int max_depth) { int i; vdd->max_depth = max_depth; vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth); if (vdd->pathtbl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } for (i = 0; i < vdd->max_depth; i++) { vdd->pathtbl[i].first = NULL; vdd->pathtbl[i].last = &(vdd->pathtbl[i].first); vdd->pathtbl[i].sorted = NULL; vdd->pathtbl[i].cnt = 0; } return (ARCHIVE_OK); } /* * Make Path Tables */ static int isoent_make_path_table(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int depth, r; int dir_number; /* * Init Path Table. */ if (iso9660->dircnt_max >= MAX_DEPTH && (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4)) r = isoent_alloc_path_table(a, &(iso9660->primary), iso9660->dircnt_max + 1); else /* The number of levels in the hierarchy cannot exceed * eight. */ r = isoent_alloc_path_table(a, &(iso9660->primary), MAX_DEPTH); if (r < 0) return (r); if (iso9660->opt.joliet) { r = isoent_alloc_path_table(a, &(iso9660->joliet), iso9660->dircnt_max + 1); if (r < 0) return (r); } /* Step 0. * - Collect directories for primary and joliet. */ isoent_collect_dirs(&(iso9660->primary), NULL, 0); if (iso9660->opt.joliet) isoent_collect_dirs(&(iso9660->joliet), NULL, 0); /* * Rockridge; move deeper depth directories to rr_moved. */ if (iso9660->opt.rr) { r = isoent_rr_move(a); if (r < 0) return (r); } /* Update nlink. */ isofile_connect_hardlink_files(iso9660); /* Step 1. * - Renew a value of the depth of that directories. * - Resolve hardlinks. * - Convert pathnames to ISO9660 name or UCS2(joliet). * - Sort files by each directory. */ r = isoent_traverse_tree(a, &(iso9660->primary)); if (r < 0) return (r); if (iso9660->opt.joliet) { r = isoent_traverse_tree(a, &(iso9660->joliet)); if (r < 0) return (r); } /* Step 2. * - Sort directories. * - Assign all directory number. */ dir_number = 1; for (depth = 0; depth < iso9660->primary.max_depth; depth++) { r = isoent_make_path_table_2(a, &(iso9660->primary), depth, &dir_number); if (r < 0) return (r); } if (iso9660->opt.joliet) { dir_number = 1; for (depth = 0; depth < iso9660->joliet.max_depth; depth++) { r = isoent_make_path_table_2(a, &(iso9660->joliet), depth, &dir_number); if (r < 0) return (r); } } if (iso9660->opt.limit_dirs && dir_number > 0xffff) { /* * Maximum number of directories is 65535(0xffff) * doe to size(16bit) of Parent Directory Number of * the Path Table. * See also ISO9660 Standard 9.4. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many directories(%d) over 65535.", dir_number); return (ARCHIVE_FATAL); } /* Get the size of the Path Table. */ calculate_path_table_size(&(iso9660->primary)); if (iso9660->opt.joliet) calculate_path_table_size(&(iso9660->joliet)); return (ARCHIVE_OK); } static int isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent) { struct iso9660 *iso9660 = a->format_data; /* Find a isoent of the boot file. */ iso9660->el_torito.boot = isoent_find_entry(rootent, iso9660->el_torito.boot_filename.s); if (iso9660->el_torito.boot == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't find the boot image file ``%s''", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } iso9660->el_torito.boot->file->boot = BOOT_IMAGE; return (ARCHIVE_OK); } static int isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; struct isoent *isoent; struct archive_entry *entry; (void)rootent; /* UNUSED */ /* * Create the entry which is the "boot.catalog" file. */ file = isofile_new(a, NULL); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } archive_entry_set_pathname(file->entry, iso9660->el_torito.catalog_filename.s); archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE); archive_entry_set_mtime(file->entry, iso9660->birth_time, 0); archive_entry_set_atime(file->entry, iso9660->birth_time, 0); archive_entry_set_ctime(file->entry, iso9660->birth_time, 0); archive_entry_set_uid(file->entry, getuid()); archive_entry_set_gid(file->entry, getgid()); archive_entry_set_mode(file->entry, AE_IFREG | 0444); archive_entry_set_nlink(file->entry, 1); if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { isofile_free(file); return (ARCHIVE_FATAL); } file->boot = BOOT_CATALOG; file->content.size = LOGICAL_BLOCK_SIZE; isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } isoent->virtual = 1; /* Add the "boot.catalog" entry into tree */ if (isoent_tree(a, &isoent) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->el_torito.catalog = isoent; /* * Get a boot medai type. */ switch (iso9660->opt.boot_type) { default: case OPT_BOOT_TYPE_AUTO: /* Try detecting a media type of the boot image. */ entry = iso9660->el_torito.boot->file->entry; if (archive_entry_size(entry) == FD_1_2M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_2M_DISKETTE; else if (archive_entry_size(entry) == FD_1_44M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_44M_DISKETTE; else if (archive_entry_size(entry) == FD_2_88M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_2_88M_DISKETTE; else /* We cannot decide whether the boot image is * hard-disk. */ iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; break; case OPT_BOOT_TYPE_NO_EMU: iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; break; case OPT_BOOT_TYPE_HARD_DISK: iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK; break; case OPT_BOOT_TYPE_FD: entry = iso9660->el_torito.boot->file->entry; if (archive_entry_size(entry) <= FD_1_2M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_2M_DISKETTE; else if (archive_entry_size(entry) <= FD_1_44M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_44M_DISKETTE; else if (archive_entry_size(entry) <= FD_2_88M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_2_88M_DISKETTE; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Boot image file(``%s'') size is too big " "for fd type.", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } break; } /* * Get a system type. * TODO: `El Torito' specification says "A copy of byte 5 from the * Partition Table found in the boot image". */ iso9660->el_torito.system_type = 0; /* * Get an ID. */ if (iso9660->opt.publisher) archive_string_copy(&(iso9660->el_torito.id), &(iso9660->publisher_identifier)); return (ARCHIVE_OK); } /* * If a media type is floppy, return its image size. * otherwise return 0. */ static size_t fd_boot_image_size(int media_type) { switch (media_type) { case BOOT_MEDIA_1_2M_DISKETTE: return (FD_1_2M_SIZE); case BOOT_MEDIA_1_44M_DISKETTE: return (FD_1_44M_SIZE); case BOOT_MEDIA_2_88M_DISKETTE: return (FD_2_88M_SIZE); default: return (0); } } /* * Make a boot catalog image data. */ static int make_boot_catalog(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; unsigned char *block; unsigned char *p; uint16_t sum, *wp; block = wb_buffptr(a); memset(block, 0, LOGICAL_BLOCK_SIZE); p = block; /* * Validation Entry */ /* Header ID */ p[0] = 1; /* Platform ID */ p[1] = iso9660->el_torito.platform_id; /* Reserved */ p[2] = p[3] = 0; /* ID */ if (archive_strlen(&(iso9660->el_torito.id)) > 0) strncpy((char *)p+4, iso9660->el_torito.id.s, 23); p[27] = 0; /* Checksum */ p[28] = p[29] = 0; /* Key */ p[30] = 0x55; p[31] = 0xAA; sum = 0; wp = (uint16_t *)block; while (wp < (uint16_t *)&block[32]) sum += archive_le16dec(wp++); set_num_721(&block[28], (~sum) + 1); /* * Initial/Default Entry */ p = &block[32]; /* Boot Indicator */ p[0] = 0x88; /* Boot media type */ p[1] = iso9660->el_torito.media_type; /* Load Segment */ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) set_num_721(&p[2], iso9660->el_torito.boot_load_seg); else set_num_721(&p[2], 0); /* System Type */ p[4] = iso9660->el_torito.system_type; /* Unused */ p[5] = 0; /* Sector Count */ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) set_num_721(&p[6], iso9660->el_torito.boot_load_size); else set_num_721(&p[6], 1); /* Load RBA */ set_num_731(&p[8], iso9660->el_torito.boot->file->content.location); /* Unused */ memset(&p[12], 0, 20); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int setup_boot_information(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isoent *np; int64_t size; uint32_t sum; unsigned char buff[4096]; np = iso9660->el_torito.boot; lseek(iso9660->temp_fd, np->file->content.offset_of_temp + 64, SEEK_SET); size = archive_entry_size(np->file->entry) - 64; if (size <= 0) { archive_set_error(&a->archive, errno, "Boot file(%jd) is too small", (intmax_t)size + 64); return (ARCHIVE_FATAL); } sum = 0; while (size > 0) { size_t rsize; ssize_t i, rs; if (size > (int64_t)sizeof(buff)) rsize = sizeof(buff); else rsize = (size_t)size; rs = read(iso9660->temp_fd, buff, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } for (i = 0; i < rs; i += 4) sum += archive_le32dec(buff + i); size -= rs; } /* Set the location of Primary Volume Descriptor. */ set_num_731(buff, SYSTEM_AREA_BLOCK); /* Set the location of the boot file. */ set_num_731(buff+4, np->file->content.location); /* Set the size of the boot file. */ size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = archive_entry_size(np->file->entry); set_num_731(buff+8, (uint32_t)size); /* Set the sum of the boot file. */ set_num_731(buff+12, sum); /* Clear reserved bytes. */ memset(buff+16, 0, 40); /* Overwrite the boot file. */ lseek(iso9660->temp_fd, np->file->content.offset_of_temp + 8, SEEK_SET); return (write_to_temp(a, buff, 56)); } #ifdef HAVE_ZLIB_H static int zisofs_init_zstream(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int r; iso9660->zisofs.stream.next_in = NULL; iso9660->zisofs.stream.avail_in = 0; iso9660->zisofs.stream.total_in = 0; iso9660->zisofs.stream.total_out = 0; if (iso9660->zisofs.stream_valid) r = deflateReset(&(iso9660->zisofs.stream)); else { r = deflateInit(&(iso9660->zisofs.stream), iso9660->zisofs.compression_level); iso9660->zisofs.stream_valid = 1; } switch (r) { case Z_OK: break; default: case Z_STREAM_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: invalid setup parameter"); return (ARCHIVE_FATAL); case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Internal error initializing " "compression library"); return (ARCHIVE_FATAL); case Z_VERSION_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: invalid library version"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ static int zisofs_init(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660 = a->format_data; #ifdef HAVE_ZLIB_H uint64_t tsize; size_t _ceil, bpsize; int r; #endif iso9660->zisofs.detect_magic = 0; iso9660->zisofs.making = 0; if (!iso9660->opt.rr || !iso9660->opt.zisofs) return (ARCHIVE_OK); if (archive_entry_size(file->entry) >= 24 && archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) { /* Acceptable file size for zisofs. */ iso9660->zisofs.detect_magic = 1; iso9660->zisofs.magic_cnt = 0; } if (!iso9660->zisofs.detect_magic) return (ARCHIVE_OK); #ifdef HAVE_ZLIB_H /* The number of Logical Blocks which uncompressed data * will use in iso-image file is the same as the number of * Logical Blocks which zisofs(compressed) data will use * in ISO-image file. It won't reduce iso-image file size. */ if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE) return (ARCHIVE_OK); /* Initialize compression library */ r = zisofs_init_zstream(a); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Mark file->zisofs to create RRIP 'ZF' Use Entry. */ file->zisofs.header_size = ZF_HEADER_SIZE >> 2; file->zisofs.log2_bs = ZF_LOG2_BS; file->zisofs.uncompressed_size = (uint32_t)archive_entry_size(file->entry); /* Calculate a size of Block Pointers of zisofs. */ _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) >> file->zisofs.log2_bs; iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1; iso9660->zisofs.block_pointers_idx = 0; /* Ensure a buffer size used for Block Pointers */ bpsize = iso9660->zisofs.block_pointers_cnt * sizeof(iso9660->zisofs.block_pointers[0]); if (iso9660->zisofs.block_pointers_allocated < bpsize) { free(iso9660->zisofs.block_pointers); iso9660->zisofs.block_pointers = malloc(bpsize); if (iso9660->zisofs.block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } iso9660->zisofs.block_pointers_allocated = bpsize; } /* * Skip zisofs header and Block Pointers, which we will write * after all compressed data of a file written to the temporary * file. */ tsize = ZF_HEADER_SIZE + bpsize; if (write_null(a, (size_t)tsize) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Initialize some variables to make zisofs. */ archive_le32enc(&(iso9660->zisofs.block_pointers[0]), (uint32_t)tsize); iso9660->zisofs.remaining = file->zisofs.uncompressed_size; iso9660->zisofs.making = 1; iso9660->zisofs.allzero = 1; iso9660->zisofs.block_offset = tsize; iso9660->zisofs.total_size = tsize; iso9660->cur_file->cur_content->size = tsize; #endif return (ARCHIVE_OK); } static void zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; const unsigned char *p, *endp; const unsigned char *magic_buff; uint32_t uncompressed_size; unsigned char header_size; unsigned char log2_bs; size_t _ceil, doff; uint32_t bst, bed; int magic_max; int64_t entry_size; entry_size = archive_entry_size(file->entry); if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size) magic_max = (int)entry_size; else magic_max = sizeof(iso9660->zisofs.magic_buffer); if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max) /* It's unnecessary we copy buffer. */ magic_buff = buff; else { if (iso9660->zisofs.magic_cnt < magic_max) { size_t l; l = sizeof(iso9660->zisofs.magic_buffer) - iso9660->zisofs.magic_cnt; if (l > s) l = s; memcpy(iso9660->zisofs.magic_buffer + iso9660->zisofs.magic_cnt, buff, l); iso9660->zisofs.magic_cnt += (int)l; if (iso9660->zisofs.magic_cnt < magic_max) return; } magic_buff = iso9660->zisofs.magic_buffer; } iso9660->zisofs.detect_magic = 0; p = magic_buff; /* Check the magic code of zisofs. */ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) /* This is not zisofs file which made by mkzftree. */ return; p += sizeof(zisofs_magic); /* Read a zisofs header. */ uncompressed_size = archive_le32dec(p); header_size = p[4]; log2_bs = p[5]; if (uncompressed_size < 24 || header_size != 4 || log2_bs > 30 || log2_bs < 7) return;/* Invalid or not supported header. */ /* Calculate a size of Block Pointers of zisofs. */ _ceil = (uncompressed_size + (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs; doff = (_ceil + 1) * 4 + 16; if (entry_size < (int64_t)doff) return;/* Invalid data. */ /* Check every Block Pointer has valid value. */ p = magic_buff + 16; endp = magic_buff + magic_max; while (_ceil && p + 8 <= endp) { bst = archive_le32dec(p); if (bst != doff) return;/* Invalid data. */ p += 4; bed = archive_le32dec(p); if (bed < bst || bed > entry_size) return;/* Invalid data. */ doff += bed - bst; _ceil--; } file->zisofs.uncompressed_size = uncompressed_size; file->zisofs.header_size = header_size; file->zisofs.log2_bs = log2_bs; /* Disable making a zisofs image. */ iso9660->zisofs.making = 0; } #ifdef HAVE_ZLIB_H /* * Compress data and write it to a temporary file. */ static int zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; const unsigned char *b; z_stream *zstrm; size_t avail, csize; int flush, r; zstrm = &(iso9660->zisofs.stream); zstrm->next_out = wb_buffptr(a); zstrm->avail_out = (uInt)wb_remaining(a); b = (const unsigned char *)buff; do { avail = ZF_BLOCK_SIZE - zstrm->total_in; if (s < avail) { avail = s; flush = Z_NO_FLUSH; } else flush = Z_FINISH; iso9660->zisofs.remaining -= avail; if (iso9660->zisofs.remaining <= 0) flush = Z_FINISH; zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b; zstrm->avail_in = (uInt)avail; /* * Check if current data block are all zero. */ if (iso9660->zisofs.allzero) { const unsigned char *nonzero = b; const unsigned char *nonzeroend = b + avail; while (nonzero < nonzeroend) if (*nonzero++) { iso9660->zisofs.allzero = 0; break; } } b += avail; s -= avail; /* * If current data block are all zero, we do not use * compressed data. */ if (flush == Z_FINISH && iso9660->zisofs.allzero && avail + zstrm->total_in == ZF_BLOCK_SIZE) { if (iso9660->zisofs.block_offset != file->cur_content->size) { int64_t diff; r = wb_set_offset(a, file->cur_content->offset_of_temp + iso9660->zisofs.block_offset); if (r != ARCHIVE_OK) return (r); diff = file->cur_content->size - iso9660->zisofs.block_offset; file->cur_content->size -= diff; iso9660->zisofs.total_size -= diff; } zstrm->avail_in = 0; } /* * Compress file data. */ while (zstrm->avail_in > 0) { csize = zstrm->total_out; r = deflate(zstrm, flush); switch (r) { case Z_OK: case Z_STREAM_END: csize = zstrm->total_out - csize; if (wb_consume(a, csize) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->zisofs.total_size += csize; iso9660->cur_file->cur_content->size += csize; zstrm->next_out = wb_buffptr(a); zstrm->avail_out = (uInt)wb_remaining(a); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Compression failed:" " deflate() call returned status %d", r); return (ARCHIVE_FATAL); } } if (flush == Z_FINISH) { /* * Save the information of one zisofs block. */ iso9660->zisofs.block_pointers_idx ++; archive_le32enc(&(iso9660->zisofs.block_pointers[ iso9660->zisofs.block_pointers_idx]), (uint32_t)iso9660->zisofs.total_size); r = zisofs_init_zstream(a); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->zisofs.allzero = 1; iso9660->zisofs.block_offset = file->cur_content->size; } } while (s); return (ARCHIVE_OK); } static int zisofs_finish_entry(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; unsigned char buff[16]; size_t s; int64_t tail; /* Direct temp file stream to zisofs temp file stream. */ archive_entry_set_size(file->entry, iso9660->zisofs.total_size); /* * Save a file pointer which points the end of current zisofs data. */ tail = wb_offset(a); /* * Make a header. * * +-----------------+----------------+-----------------+ * | Header 16 bytes | Block Pointers | Compressed data | * +-----------------+----------------+-----------------+ * 0 16 +X * Block Pointers : * 4 * (((Uncompressed file size + block_size -1) / block_size) + 1) * * Write zisofs header. * Magic number * +----+----+----+----+----+----+----+----+ * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 | * +----+----+----+----+----+----+----+----+ * 0 1 2 3 4 5 6 7 8 * * +------------------------+------------------+ * | Uncompressed file size | header_size >> 2 | * +------------------------+------------------+ * 8 12 13 * * +-----------------+----------------+ * | log2 block_size | Reserved(0000) | * +-----------------+----------------+ * 13 14 16 */ memcpy(buff, zisofs_magic, 8); set_num_731(buff+8, file->zisofs.uncompressed_size); buff[12] = file->zisofs.header_size; buff[13] = file->zisofs.log2_bs; buff[14] = buff[15] = 0;/* Reserved */ /* Move to the right position to write the header. */ wb_set_offset(a, file->content.offset_of_temp); /* Write the header. */ if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Write zisofs Block Pointers. */ s = iso9660->zisofs.block_pointers_cnt * sizeof(iso9660->zisofs.block_pointers[0]); if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Set a file pointer back to the end of the temporary file. */ wb_set_offset(a, tail); return (ARCHIVE_OK); } static int zisofs_free(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int ret = ARCHIVE_OK; free(iso9660->zisofs.block_pointers); if (iso9660->zisofs.stream_valid && deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } iso9660->zisofs.block_pointers = NULL; iso9660->zisofs.stream_valid = 0; return (ret); } struct zisofs_extract { int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; size_t uncompressed_buffer_size; int initialized:1; int header_passed:1; uint32_t pz_offset; unsigned char *block_pointers; size_t block_pointers_size; size_t block_pointers_avail; size_t block_off; uint32_t block_avail; z_stream stream; int stream_valid; }; static ssize_t zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail = bytes; size_t _ceil, xsize; /* Allocate block pointers buffer. */ _ceil = (size_t)((zisofs->pz_uncompressed_size + (((int64_t)1) << zisofs->pz_log2_bs) - 1) >> zisofs->pz_log2_bs); xsize = (_ceil + 1) * 4; if (zisofs->block_pointers == NULL) { size_t alloc = ((xsize >> 10) + 1) << 10; zisofs->block_pointers = malloc(alloc); if (zisofs->block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for zisofs decompression"); return (ARCHIVE_FATAL); } } zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs; /* * Read the file header, and check the magic code of zisofs. */ if (!zisofs->header_passed) { int err = 0; if (avail < 16) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) err = 1; else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size) err = 1; else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs) err = 1; if (err) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } avail -= 16; p += 16; zisofs->header_passed = 1; } /* * Read block pointers. */ if (zisofs->header_passed && zisofs->block_pointers_avail < zisofs->block_pointers_size) { xsize = zisofs->block_pointers_size - zisofs->block_pointers_avail; if (avail < xsize) xsize = avail; memcpy(zisofs->block_pointers + zisofs->block_pointers_avail, p, xsize); zisofs->block_pointers_avail += xsize; avail -= xsize; if (zisofs->block_pointers_avail == zisofs->block_pointers_size) { /* We've got all block pointers and initialize * related variables. */ zisofs->block_off = 0; zisofs->block_avail = 0; /* Complete a initialization */ zisofs->initialized = 1; } } return ((ssize_t)avail); } static ssize_t zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail; int r; if (!zisofs->initialized) { ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes); if (rs < 0) return (rs); if (!zisofs->initialized) { /* We need more data. */ zisofs->pz_offset += (uint32_t)bytes; return (bytes); } avail = rs; p += bytes - avail; } else avail = bytes; /* * Get block offsets from block pointers. */ if (zisofs->block_avail == 0) { uint32_t bst, bed; if (zisofs->block_off + 4 >= zisofs->block_pointers_size) { /* There isn't a pair of offsets. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } bst = archive_le32dec( zisofs->block_pointers + zisofs->block_off); if (bst != zisofs->pz_offset + (bytes - avail)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers(cannot seek)"); return (ARCHIVE_FATAL); } bed = archive_le32dec( zisofs->block_pointers + zisofs->block_off + 4); if (bed < bst) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } zisofs->block_avail = bed - bst; zisofs->block_off += 4; /* Initialize compression library for new block. */ if (zisofs->stream_valid) r = inflateReset(&zisofs->stream); else r = inflateInit(&zisofs->stream); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize zisofs decompression."); return (ARCHIVE_FATAL); } zisofs->stream_valid = 1; zisofs->stream.total_in = 0; zisofs->stream.total_out = 0; } /* * Make uncompressed data. */ if (zisofs->block_avail == 0) { /* * It's basically 32K bytes NUL data. */ unsigned char *wb; size_t size, wsize; size = zisofs->uncompressed_buffer_size; while (size) { wb = wb_buffptr(a); if (size > wb_remaining(a)) wsize = wb_remaining(a); else wsize = size; memset(wb, 0, wsize); r = wb_consume(a, wsize); if (r < 0) return (r); size -= wsize; } } else { zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; if (avail > zisofs->block_avail) zisofs->stream.avail_in = zisofs->block_avail; else zisofs->stream.avail_in = (uInt)avail; zisofs->stream.next_out = wb_buffptr(a); zisofs->stream.avail_out = (uInt)wb_remaining(a); r = inflate(&zisofs->stream, 0); switch (r) { case Z_OK: /* Decompressor made some progress.*/ case Z_STREAM_END: /* Found end of stream. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zisofs decompression failed (%d)", r); return (ARCHIVE_FATAL); } avail -= zisofs->stream.next_in - p; zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out); if (r < 0) return (r); } zisofs->pz_offset += (uint32_t)bytes; return (bytes - avail); } static int zisofs_rewind_boot_file(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; unsigned char *rbuff; ssize_t r; size_t remaining, rbuff_size; struct zisofs_extract zext; int64_t read_offset, write_offset, new_offset; int fd, ret = ARCHIVE_OK; file = iso9660->el_torito.boot->file; /* * There is nothing to do if this boot file does not have * zisofs header. */ if (file->zisofs.header_size == 0) return (ARCHIVE_OK); /* * Uncompress the zisofs'ed file contents. */ memset(&zext, 0, sizeof(zext)); zext.pz_uncompressed_size = file->zisofs.uncompressed_size; zext.pz_log2_bs = file->zisofs.log2_bs; fd = iso9660->temp_fd; new_offset = wb_offset(a); read_offset = file->content.offset_of_temp; remaining = (size_t)file->content.size; if (remaining > 1024 * 32) rbuff_size = 1024 * 32; else rbuff_size = remaining; rbuff = malloc(rbuff_size); if (rbuff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } while (remaining) { size_t rsize; ssize_t rs; /* Get the current file pointer. */ write_offset = lseek(fd, 0, SEEK_CUR); /* Change the file pointer to read. */ lseek(fd, read_offset, SEEK_SET); rsize = rbuff_size; if (rsize > remaining) rsize = remaining; rs = read(iso9660->temp_fd, rbuff, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); ret = ARCHIVE_FATAL; break; } remaining -= rs; read_offset += rs; /* Put the file pointer back to write. */ lseek(fd, write_offset, SEEK_SET); r = zisofs_extract(a, &zext, rbuff, rs); if (r < 0) { ret = (int)r; break; } } if (ret == ARCHIVE_OK) { /* * Change the boot file content from zisofs'ed data * to plain data. */ file->content.offset_of_temp = new_offset; file->content.size = file->zisofs.uncompressed_size; archive_entry_set_size(file->entry, file->content.size); /* Set to be no zisofs. */ file->zisofs.header_size = 0; file->zisofs.log2_bs = 0; file->zisofs.uncompressed_size = 0; r = wb_write_padding_to_temp(a, file->content.size); if (r < 0) ret = ARCHIVE_FATAL; } /* * Free the resource we used in this function only. */ free(rbuff); free(zext.block_pointers); if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } return (ret); } #else static int zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { (void)buff; /* UNUSED */ (void)s; /* UNUSED */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programing error"); return (ARCHIVE_FATAL); } static int zisofs_rewind_boot_file(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; if (iso9660->el_torito.boot->file->zisofs.header_size != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "We cannot extract the zisofs imaged boot file;" " this may not boot in being zisofs imaged"); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int zisofs_finish_entry(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int zisofs_free(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ Index: vendor/libarchive/dist/libarchive/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/libarchive/test/CMakeLists.txt (revision 310621) +++ vendor/libarchive/dist/libarchive/test/CMakeLists.txt (revision 310622) @@ -1,309 +1,310 @@ ############################################ # # How to build libarchive_test # ############################################ IF(ENABLE_TEST) SET(libarchive_test_SOURCES ../../test_utils/test_utils.c main.c read_open_memory.c test.h test_acl_freebsd_nfs4.c test_acl_freebsd_posix1e.c test_acl_nfs4.c test_acl_pax.c test_acl_posix1e.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_owner.c test_archive_match_path.c test_archive_match_time.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_pax_sparse.c test_compat_solaris_tar_acl.c test_compat_star_acl_posix1e.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_failure.c test_open_fd.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_bzip2_rpm.c test_read_format_cpio_svr4_gzip.c test_read_format_cpio_svr4_gzip_rpm.c test_read_format_cpio_svr4c_Z.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_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_isorr_rr_moved.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_ustar_filename_encoding.c test_ustar_filenames.c test_warn_missing_hardlink_target.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_zip_filename_encoding.c ) # # Register target # ADD_EXECUTABLE(libarchive_test ${libarchive_test_SOURCES}) TARGET_LINK_LIBRARIES(libarchive_test archive_static ${ADDITIONAL_LIBS}) SET_PROPERTY(TARGET libarchive_test PROPERTY COMPILE_DEFINITIONS LIBARCHIVE_STATIC LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${libarchive_test_SOURCES}) SET_PROPERTY(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) # list.h has a line DEFINE_TEST(testname) for every # test. We can use that to define the tests for cmake by # defining a DEFINE_TEST macro and reading list.h in. MACRO (DEFINE_TEST _testname) ADD_TEST( NAME libarchive_${_testname} COMMAND libarchive_test -vv -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) # Experimental new test handling ADD_CUSTOM_TARGET(run_libarchive_test COMMAND libarchive_test -r ${CMAKE_CURRENT_SOURCE_DIR} -vv) ADD_DEPENDENCIES(run_all_tests run_libarchive_test) ENDIF(ENABLE_TEST) Index: vendor/libarchive/dist/libarchive/test/test_read_disk_directory_traversals.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_read_disk_directory_traversals.c (revision 310621) +++ vendor/libarchive/dist/libarchive/test/test_read_disk_directory_traversals.c (revision 310622) @@ -1,1580 +1,1580 @@ /*- * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #include #if defined(_WIN32) && !defined(__CYGWIN__) # if !defined(__BORLANDC__) # define getcwd _getcwd # endif #endif /* * Test if the current filesystem is mounted with noatime option. */ static int atimeIsUpdated(void) { const char *fn = "fs_noatime"; struct stat st; if (!assertMakeFile(fn, 0666, "a")) return (0); if (!assertUtimes(fn, 1, 0, 1, 0)) return (0); /* Test the file contents in order to update its atime. */ if (!assertTextFileContents("a", fn)) return (0); if (stat(fn, &st) != 0) return (0); /* Is atime updated? */ if (st.st_atime > 1) return (1); return (0); } static void test_basic(void) { struct archive *a; struct archive_entry *ae; const void *p; char *initial_cwd, *cwd; size_t size; int64_t offset; int file_count; #if defined(_WIN32) && !defined(__CYGWIN__) wchar_t *wcwd, *wp, *fullpath; #endif assertMakeDir("dir1", 0755); assertMakeFile("dir1/file1", 0644, "0123456789"); assertMakeFile("dir1/file2", 0644, "hello world"); assertMakeDir("dir1/sub1", 0755); assertMakeFile("dir1/sub1/file1", 0644, "0123456789"); assertMakeDir("dir1/sub2", 0755); assertMakeFile("dir1/sub2/file1", 0644, "0123456789"); assertMakeFile("dir1/sub2/file2", 0644, "0123456789"); assertMakeDir("dir1/sub2/sub1", 0755); assertMakeDir("dir1/sub2/sub2", 0755); assertMakeDir("dir1/sub2/sub3", 0755); assertMakeFile("dir1/sub2/sub3/file", 0644, "xyz"); file_count = 12; assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1")); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub3/file") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 3); assertEqualInt((int)offset, 0); assertEqualMem(p, "xyz", 3); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 3); assertEqualInt(0, archive_read_disk_can_descend(a)); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test that call archive_read_disk_open_w, wchar_t version. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, L"dir1")); file_count = 12; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (wcscmp(archive_entry_pathname_w(ae), L"dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub3/file") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 3); assertEqualInt((int)offset, 0); assertEqualMem(p, "xyz", 3); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 3); assertEqualInt(0, archive_read_disk_can_descend(a)); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test that call archive_read_disk_open with a regular file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1/file1")); /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); #if defined(_WIN32) && !defined(__CYGWIN__) /* * Test for wildcard '*' or '?' */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1/*1")); /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* dir1/sub1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(1, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/sub1"); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); /* dir1/sub1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/sub1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test for a full-path beginning with "//?/" */ wcwd = _wgetcwd(NULL, 0); fullpath = malloc(sizeof(wchar_t) * (wcslen(wcwd) + 32)); wcscpy(fullpath, L"//?/"); wcscat(fullpath, wcwd); wcscat(fullpath, L"/dir1/file1"); free(wcwd); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, fullpath)); while ((wcwd = wcschr(fullpath, L'\\')) != NULL) *wcwd = L'/'; /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); free(fullpath); /* * Test for wild card '*' or '?' with "//?/" prefix. */ wcwd = _wgetcwd(NULL, 0); fullpath = malloc(sizeof(wchar_t) * (wcslen(wcwd) + 32)); wcscpy(fullpath, L"//?/"); wcscat(fullpath, wcwd); wcscat(fullpath, L"/dir1/*1"); free(wcwd); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, fullpath)); while ((wcwd = wcschr(fullpath, L'\\')) != NULL) *wcwd = L'/'; /* dir1/file1 */ wp = wcsrchr(fullpath, L'/'); wcscpy(wp+1, L"file1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* dir1/sub1 */ wcscpy(wp+1, L"sub1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(1, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); /* dir1/sub1/file1 */ wcscpy(wp+1, L"sub1/file1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); free(fullpath); #endif /* * We should be on the initial directory where we performed - * archive_read_disk_new() after we perfome archive_read_free() - * even if we broke off the directory traversals. + * archive_read_disk_new() after we perform archive_read_free() + * even if we broke off the directory traversals. */ /* Save current working directory. */ #ifdef PATH_MAX initial_cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else initial_cwd = getcwd(NULL, 0); #endif assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1")); /* Step in a deep directory. */ file_count = 12; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "dir1/sub1/file1") == 0) /* * We are on an another directory at this time. */ break; if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* We should be on the initial working directory. */ failure( "Current working directory does not return to the initial" "directory"); #ifdef PATH_MAX cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else cwd = getcwd(NULL, 0); #endif assertEqualString(initial_cwd, cwd); free(initial_cwd); free(cwd); archive_entry_free(ae); } static void test_symlink_hybrid(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("h", 0755); assertChdir("h"); assertMakeDir("d1", 0755); assertMakeSymlink("ld1", "d1"); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); assertMakeSymlink("d1/link1", "file1"); assertMakeSymlink("d1/linkX", "fileX"); assertMakeSymlink("link2", "d1/file2"); assertMakeSymlink("linkY", "d1/fileY"); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_hybrid(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "h/ld1")); file_count = 5; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "h/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Specified file is a directory and it has symbolic files. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "h")); file_count = 9; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "h") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/d1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/d1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/d1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/d1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/link2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/linkY") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_symlink_logical(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("l", 0755); assertChdir("l"); assertMakeDir("d1", 0755); assertMakeSymlink("ld1", "d1"); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); assertMakeSymlink("d1/link1", "file1"); assertMakeSymlink("d1/linkX", "fileX"); assertMakeSymlink("link2", "d1/file2"); assertMakeSymlink("linkY", "d1/fileY"); assertChdir(".."); /* Note: this test uses archive_read_next_header() instead of archive_read_next_header2() */ assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_logical(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l/ld1")); file_count = 5; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); if (strcmp(archive_entry_pathname(ae), "l/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Specified file is a directory and it has symbolic files. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l")); file_count = 13; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); if (strcmp(archive_entry_pathname(ae), "l") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/d1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l/link2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/linkY") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_symlink_logical_loop(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("l2", 0755); assertChdir("l2"); assertMakeDir("d1", 0755); assertMakeDir("d1/d2", 0755); assertMakeDir("d1/d2/d3", 0755); assertMakeDir("d2", 0755); assertMakeFile("d2/file1", 0644, "d2/file1"); assertMakeSymlink("d1/d2/ld1", "../../d1"); assertMakeSymlink("d1/d2/ld2", "../../d2"); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_logical(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l2/d1")); file_count = 6; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "l2/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/d3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d2/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_restore_atime(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!atimeIsUpdated()) { skipping("Can't test restoring atime on this filesystem"); return; } assertMakeDir("at", 0755); assertMakeFile("at/f1", 0644, "0123456789"); assertMakeFile("at/f2", 0644, "hello world"); assertMakeFile("at/fe", 0644, NULL); assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* * Test1: Traversals without archive_read_disk_set_atime_restored(). */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* On FreeBSD (and likely other systems), atime on dirs does not change when it is read. */ /* failure("Atime should be restored"); */ /* assertFileAtimeRecent("at"); */ failure("Atime should be restored"); assertFileAtimeRecent("at/f1"); failure("Atime should be restored"); assertFileAtimeRecent("at/f2"); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test2: Traversals with archive_read_disk_set_atime_restored(). */ assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test3: Traversals with archive_read_disk_set_atime_restored() but * no data read as a listing. */ assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); if (!canNodump()) { /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); skipping("Can't test atime with nodump on this filesystem"); return; } /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test4: Traversals with archive_read_disk_set_atime_restored() and * archive_read_disk_honor_nodump(). */ assertNodump("at/f1"); assertNodump("at/f2"); assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 2; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, ARCHIVE_READDISK_RESTORE_ATIME | ARCHIVE_READDISK_HONOR_NODUMP)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static int metadata_filter(struct archive *a, void *data, struct archive_entry *ae) { (void)data; /* UNUSED */ failure("CTime should be set"); assertEqualInt(8, archive_entry_ctime_is_set(ae)); failure("MTime should be set"); assertEqualInt(16, archive_entry_mtime_is_set(ae)); if (archive_entry_mtime(ae) < 886611) return (0); if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ failure("archive_read_disk_can_descend should work" " in metadata filter"); assertEqualIntA(a, 1, archive_read_disk_can_descend(a)); failure("archive_read_disk_descend should work" " in metadata filter"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } return (1); } static void test_callbacks(void) { struct archive *a; struct archive *m; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; assertMakeDir("cb", 0755); assertMakeFile("cb/f1", 0644, "0123456789"); assertMakeFile("cb/f2", 0644, "hello world"); assertMakeFile("cb/fe", 0644, NULL); assertUtimes("cb/f1", 886600, 0, 886600, 0); assertUtimes("cb/f2", 886611, 0, 886611, 0); assertUtimes("cb/fe", 886611, 0, 886611, 0); assertUtimes("cb", 886622, 0, 886622, 0); assert((ae = archive_entry_new()) != NULL); if (assert((a = archive_read_disk_new()) != NULL)) { archive_entry_free(ae); return; } if (assert((m = archive_match_new()) != NULL)) { archive_entry_free(ae); archive_read_free(a); archive_match_free(m); return; } /* * Test1: Traversals with a name filter. */ file_count = 3; assertEqualIntA(m, ARCHIVE_OK, archive_match_exclude_pattern(m, "cb/f2")); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_matching(a, m, NULL, NULL)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "cb")); while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'cb/f2' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "cb/f2") != 0); if (strcmp(archive_entry_pathname(ae), "cb") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "cb/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "cb/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test2: Traversals with a metadata filter. */ assertUtimes("cb/f1", 886600, 0, 886600, 0); assertUtimes("cb/f2", 886611, 0, 886611, 0); assertUtimes("cb/fe", 886611, 0, 886611, 0); assertUtimes("cb", 886622, 0, 886622, 0); file_count = 3; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_metadata_filter_callback(a, metadata_filter, NULL)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "cb")); while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'cb/f1' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "cb/f1") != 0); if (strcmp(archive_entry_pathname(ae), "cb") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "cb/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "cb/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualInt(ARCHIVE_OK, archive_match_free(m)); archive_entry_free(ae); } static void test_nodump(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canNodump()) { skipping("Can't test nodump on this filesystem"); return; } assertMakeDir("nd", 0755); assertMakeFile("nd/f1", 0644, "0123456789"); assertMakeFile("nd/f2", 0644, "hello world"); assertMakeFile("nd/fe", 0644, NULL); assertNodump("nd/f2"); assertUtimes("nd/f1", 886600, 0, 886600, 0); assertUtimes("nd/f2", 886611, 0, 886611, 0); assertUtimes("nd/fe", 886611, 0, 886611, 0); assertUtimes("nd", 886622, 0, 886622, 0); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* * Test1: Traversals without archive_read_disk_honor_nodump(). */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd")); file_count = 4; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "nd") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "nd/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "nd/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test2: Traversals with archive_read_disk_honor_nodump(). */ assertUtimes("nd/f1", 886600, 0, 886600, 0); assertUtimes("nd/f2", 886611, 0, 886611, 0); assertUtimes("nd/fe", 886611, 0, 886611, 0); assertUtimes("nd", 886622, 0, 886622, 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, ARCHIVE_READDISK_RESTORE_ATIME | ARCHIVE_READDISK_HONOR_NODUMP)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd")); file_count = 3; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'nd/f2' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "nd/f2") != 0); if (strcmp(archive_entry_pathname(ae), "nd") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "nd/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("nd/f2", 886611, 0); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } DEFINE_TEST(test_read_disk_directory_traversals) { /* Basic test. */ test_basic(); - /* Test hybird mode; follow symlink initially, then not. */ + /* Test hybrid mode; follow symlink initially, then not. */ test_symlink_hybrid(); - /* Test logcal mode; follow all symlinks. */ + /* Test logical mode; follow all symlinks. */ test_symlink_logical(); - /* Test logcal mode; prevent loop in symlinks. */ + /* Test logical mode; prevent loop in symlinks. */ test_symlink_logical_loop(); /* Test to restore atime. */ test_restore_atime(); /* Test callbacks. */ test_callbacks(); /* Test nodump. */ test_nodump(); } Index: vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.c (nonexistent) +++ vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.c (revision 310622) @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2016 Peter Wu + * 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$"); + +/* + * Issue 822: jar files have an empty External File Attributes field which + * is misinterpreted as regular file type due to OS MS-DOS. + */ + +DEFINE_TEST(test_read_format_zip_jar) +{ + const char *refname = "test_read_format_zip_jar.jar"; + char *p; + size_t s; + struct archive *a; + struct archive_entry *ae; + char data[16]; + + extract_reference_file(refname); + p = slurpfile(&s, refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("somedir/", archive_entry_pathname(ae)); + assertEqualInt(AE_IFDIR | 0775, archive_entry_mode(ae)); + assertEqualInt(0, archive_entry_size(ae)); + assertEqualIntA(a, 0, archive_read_data(a, data, 16)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); + free(p); +} Property changes on: vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.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: vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.jar.uu =================================================================== --- vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.jar.uu (nonexistent) +++ vendor/libarchive/dist/libarchive/test/test_read_format_zip_jar.jar.uu (revision 310622) @@ -0,0 +1,6 @@ +begin 640 test_read_format_zip_jar.jar +M4$L#! H @ $AQETD ( 0 #endif #ifdef HAVE_SYS_XATTR_H #include #endif #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) // // The test ACL used here is sometimes assigned to the 'Guest' user // This changes the text and breaks the test. This function simply // strips the 'Guest' information from the string to help ensure // consistent results on different machines. // static char _acl_temp[256]; static const char * clean_acl(const char *acl) { char *p, *q; if (strlen(acl) >= sizeof(_acl_temp)) return acl; strcpy(_acl_temp, acl); p = strstr(_acl_temp, ":Guest:"); if (p != NULL) { fprintf(stderr, "Shortening: %s\n", p + 1); memmove(p + 1, p + 6, strlen(p + 6) + 1); q = strstr(p + 2, ":"); fprintf(stderr, "Shortening: %s\n", q); memmove(p + 2, q, strlen(q) + 1); return _acl_temp; } return _acl_temp; } static int has_xattr(const char *filename, const char *xattrname) { char *nl, *nlp; ssize_t r; int exisiting; r = listxattr(filename, NULL, 0, XATTR_SHOWCOMPRESSION); if (r < 0) return (0); if (r == 0) return (0); assert((nl = malloc(r)) != NULL); if (nl == NULL) return (0); r = listxattr(filename, nl, r, XATTR_SHOWCOMPRESSION); if (r < 0) { free(nl); return (0); } exisiting = 0; for (nlp = nl; nlp < nl + r; nlp += strlen(nlp) + 1) { if (strcmp(nlp, xattrname) == 0) { exisiting = 1; break; } } free(nl); return (exisiting); } #endif /* * Exercise HFS+ Compression. */ DEFINE_TEST(test_write_disk_appledouble) { #if !defined(__APPLE__) || !defined(UF_COMPRESSED) || !defined(HAVE_SYS_XATTR_H)\ || !defined(HAVE_ZLIB_H) skipping("MacOS-specific AppleDouble test"); #else const char *refname = "test_write_disk_appledouble.cpio.gz"; struct archive *ad, *a; struct archive_entry *ae; struct stat st; acl_t acl; extract_reference_file(refname); /* * Extract an archive to disk with HFS+ Compression. */ assert((ad = archive_write_disk_new()) != NULL); assertEqualIntA(ad, ARCHIVE_OK, archive_write_disk_set_standard_lookup(ad)); assertEqualIntA(ad, ARCHIVE_OK, archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 512 * 20)); assertMakeDir("hfscmp", 0755); assertChdir("hfscmp"); /* Skip "." */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(".", archive_entry_pathname(ae)); /* Extract file3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./file3", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad)); - /* Extract ._file3 which will be merged into file3 as medtadata. */ + /* Extract ._file3 which will be merged into file3 as metadata. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./._file3", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad)); /* Test file3. */ assertEqualInt(0, stat("file3", &st)); assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED); assertFileSize("file3", 8); failure("'%s' should not have Resource Fork", "file3"); assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork")); failure("'%s' should have decompfs xattr", "file3"); assertEqualInt(1, has_xattr("file3", "com.apple.decmpfs")); assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED))); assertEqualString(clean_acl(acl_to_text(acl, NULL)), "!#acl 1\n" "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:::deny:read\n" "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n" ); if (acl) acl_free(acl); /* Test ._file3. */ failure("'file3' should be merged and removed"); assertFileNotExists("._file3"); assertChdir(".."); /* * Extract an archive to disk without HFS+ Compression. */ assert((ad = archive_write_disk_new()) != NULL); assertEqualIntA(ad, ARCHIVE_OK, archive_write_disk_set_standard_lookup(ad)); assertEqualIntA(ad, ARCHIVE_OK, archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 512 * 20)); assertMakeDir("nocmp", 0755); assertChdir("nocmp"); /* Skip "." */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(".", archive_entry_pathname(ae)); /* Extract file3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./file3", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad)); - /* Extract ._file3 which will be merged into file3 as medtadata. */ + /* Extract ._file3 which will be merged into file3 as metadata. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./._file3", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad)); /* Test file3. */ assertEqualInt(0, stat("file3", &st)); assertEqualInt(0, st.st_flags & UF_COMPRESSED); assertFileSize("file3", 8); failure("'%s' should not have Resource Fork", "file3"); assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork")); failure("'%s' should not have decmpfs", "file3"); assertEqualInt(0, has_xattr("file3", "com.apple.decmpfs")); assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED))); assertEqualString(clean_acl(acl_to_text(acl, NULL)), "!#acl 1\n" "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:::deny:read\n" "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n" ); if (acl) acl_free(acl); /* Test ._file3. */ failure("'file3' should be merged and removed"); assertFileNotExists("._file3"); assertChdir(".."); assertEqualFile("hfscmp/file3", "nocmp/file3"); #endif } Index: vendor/libarchive/dist/libarchive/test/test_write_format_xar_empty.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_write_format_xar_empty.c (revision 310621) +++ vendor/libarchive/dist/libarchive/test/test_write_format_xar_empty.c (revision 310622) @@ -1,120 +1,120 @@ /*- * Copyright (c) 2010-2011 Michihiro NAKAJIMA * Copyright (c) 2008 Anselm Strauss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Development supported by Google Summer of Code 2008. */ #include "test.h" __FBSDID("$FreeBSD$"); DEFINE_TEST(test_write_format_xar_empty) { struct archive *a; struct archive_entry *ae; char buff[256]; size_t used; /* Xar format: Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); if (archive_write_set_format_xar(a) != ARCHIVE_OK) { skipping("xar is not supported on this platform"); assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); return; } assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff), &used)); /* Add "." entry which must be ignored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_atime(ae, 2, 0); archive_entry_set_ctime(ae, 4, 0); archive_entry_set_mtime(ae, 5, 0); archive_entry_copy_pathname(ae, "."); archive_entry_set_mode(ae, S_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Add ".." entry which must be ignored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_atime(ae, 2, 0); archive_entry_set_ctime(ae, 4, 0); archive_entry_set_mtime(ae, 5, 0); archive_entry_copy_pathname(ae, ".."); archive_entry_set_mode(ae, S_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Add "/" entry which must be ignored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_atime(ae, 2, 0); archive_entry_set_ctime(ae, 4, 0); archive_entry_set_mtime(ae, 5, 0); archive_entry_copy_pathname(ae, "/"); archive_entry_set_mode(ae, S_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Add "../" entry which must be ignored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_atime(ae, 2, 0); archive_entry_set_ctime(ae, 4, 0); archive_entry_set_mtime(ae, 5, 0); archive_entry_copy_pathname(ae, "../"); archive_entry_set_mode(ae, S_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Add "../../." entry which must be ignored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_atime(ae, 2, 0); archive_entry_set_ctime(ae, 4, 0); archive_entry_set_mtime(ae, 5, 0); archive_entry_copy_pathname(ae, "../../."); archive_entry_set_mode(ae, S_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Add "..//.././" entry which must be ignored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_atime(ae, 2, 0); archive_entry_set_ctime(ae, 4, 0); archive_entry_set_mtime(ae, 5, 0); archive_entry_copy_pathname(ae, "..//.././"); archive_entry_set_mode(ae, S_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive without writing anything. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); - /* Verify the correct format for an empy Xar archive. */ + /* Verify the correct format for an empty Xar archive. */ assertEqualInt(used, 0); } Index: vendor/libarchive/dist/libarchive/test/test_write_format_zip_empty.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_write_format_zip_empty.c (revision 310621) +++ vendor/libarchive/dist/libarchive/test/test_write_format_zip_empty.c (revision 310622) @@ -1,78 +1,78 @@ /*- * Copyright (c) 2008 Anselm Strauss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Development supported by Google Summer of Code 2008. */ #include "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_format_zip_empty.c 201247 2009-12-30 05:59:21Z kientzle $"); DEFINE_TEST(test_write_format_zip_empty) { struct archive *a; struct archive_entry *ae; char buff[256]; size_t used; /* Zip format: Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff), &used)); /* Close out the archive without writing anything. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); - /* Verify the correct format for an empy Zip archive. */ + /* Verify the correct format for an empty Zip archive. */ assertEqualInt(used, 22); assertEqualMem(buff, "PK\005\006\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 22); /* Verify that we read this kind of empty archive correctly. */ /* Try with the standard memory reader, and with the test memory reader with and without seek support. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, 22)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, buff, 22, 1)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, 22, 22)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); } Index: vendor/libarchive/dist/libarchive/test/test_write_format_zip_empty_zip64.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_write_format_zip_empty_zip64.c (revision 310621) +++ vendor/libarchive/dist/libarchive/test/test_write_format_zip_empty_zip64.c (revision 310622) @@ -1,103 +1,103 @@ /*- * Copyright (c) 2008 Anselm Strauss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Development supported by Google Summer of Code 2008. */ #include "test.h" __FBSDID("$FreeBSD$"); DEFINE_TEST(test_write_format_zip_empty_zip64) { struct archive *a; struct archive_entry *ae; char buff[256]; size_t used; /* Zip format: Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); /* Force zip writer to use Zip64 extensions. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_option(a, "zip", "zip64", "1")); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff), &used)); /* Close out the archive without writing anything. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); - /* Verify the correct format for an empy Zip archive with Zip64 extensions forced. */ + /* Verify the correct format for an empty Zip archive with Zip64 extensions forced. */ assertEqualInt(used, 98); assertEqualMem(buff, "PK\006\006" /* Zip64 end-of-central-directory record */ "\x2c\0\0\0\0\0\0\0" /* 44 bytes long */ "\x2d\0" /* Created by Zip 4.5 */ "\x2d\0" /* Extract with Zip 4.5 or later */ "\0\0\0\0" /* This is disk #0 */ "\0\0\0\0" /* Central dir starts on disk #0 */ "\0\0\0\0\0\0\0\0" /* There are 0 entries on this disk ... */ "\0\0\0\0\0\0\0\0" /* ... out of 0 entries total ... */ "\0\0\0\0\0\0\0\0" /* ... requiring a total of 0 bytes. */ "\0\0\0\0\0\0\0\0" /* Directory starts at offset 0 */ "PK\006\007" /* Zip64 end-of-central-directory locator */ "\0\0\0\0" /* Zip64 EOCD record is on disk #0 .. */ "\0\0\0\0\0\0\0\0" /* .. at offset 0 .. */ "\1\0\0\0" /* .. of 1 total disks. */ "PK\005\006" /* Regular Zip end-of-central-directory record */ "\0\0" /* This is disk #0 */ "\0\0" /* Central dir is on disk #0 */ "\0\0" /* There are 0 entries on this disk ... */ "\0\0" /* ... out of 0 total entries ... */ "\0\0\0\0" /* ... requiring a total of 0 bytes. */ "\0\0\0\0" /* Directory starts at offset 0. */ "\0\0" /* File comment is zero bytes long. */, 98); /* Verify that we read this kind of empty archive correctly. */ /* Try with the standard memory reader, and with the test memory reader with and without seek support. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, 98)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, buff, 98, 1)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, 98, 98)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); } Index: vendor/libarchive/dist/tar/read.c =================================================================== --- vendor/libarchive/dist/tar/read.c (revision 310621) +++ vendor/libarchive/dist/tar/read.c (revision 310622) @@ -1,420 +1,419 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "bsdtar_platform.h" __FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "bsdtar.h" #include "err.h" struct progress_data { struct bsdtar *bsdtar; struct archive *archive; struct archive_entry *entry; }; static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *); static int unmatched_inclusions_warn(struct archive *matching, const char *); void tar_mode_t(struct bsdtar *bsdtar) { read_archive(bsdtar, 't', NULL); if (unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0) bsdtar->return_value = 1; } void tar_mode_x(struct bsdtar *bsdtar) { struct archive *writer; writer = archive_write_disk_new(); if (writer == NULL) lafe_errc(1, ENOMEM, "Cannot allocate disk writer object"); if (!bsdtar->option_numeric_owner) archive_write_disk_set_standard_lookup(writer); archive_write_disk_set_options(writer, bsdtar->extract_flags); read_archive(bsdtar, 'x', writer); if (unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0) bsdtar->return_value = 1; archive_write_free(writer); } static void progress_func(void *cookie) { struct progress_data *progress_data = (struct progress_data *)cookie; struct bsdtar *bsdtar = progress_data->bsdtar; struct archive *a = progress_data->archive; struct archive_entry *entry = progress_data->entry; uint64_t comp, uncomp; int compression; if (!need_report()) return; if (bsdtar->verbose) fprintf(stderr, "\n"); if (a != NULL) { comp = archive_filter_bytes(a, -1); uncomp = archive_filter_bytes(a, 0); if (comp > uncomp) compression = 0; else compression = (int)((uncomp - comp) * 100 / uncomp); fprintf(stderr, "In: %s bytes, compression %d%%;", tar_i64toa(comp), compression); fprintf(stderr, " Out: %d files, %s bytes\n", archive_file_count(a), tar_i64toa(uncomp)); } if (entry != NULL) { safe_fprintf(stderr, "Current: %s", archive_entry_pathname(entry)); fprintf(stderr, " (%s bytes)\n", tar_i64toa(archive_entry_size(entry))); } } /* * Handle 'x' and 't' modes. */ static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) { struct progress_data progress_data; FILE *out; struct archive *a; struct archive_entry *entry; const char *reader_options; int r; while (*bsdtar->argv) { if (archive_match_include_pattern(bsdtar->matching, *bsdtar->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) if (archive_match_include_pattern_from_file( bsdtar->matching, bsdtar->names_from_file, bsdtar->option_null) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); a = archive_read_new(); if (cset_read_support_filter_program(bsdtar->cset, a) == 0) archive_read_support_filter_all(a); archive_read_support_format_all(a); reader_options = getenv(ENV_READER_OPTIONS); if (reader_options != NULL) { + size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; + size_t opt_len = strlen(reader_options) + 1; char *p; /* Set default read options. */ - p = (char *)malloc(sizeof(IGNORE_WRONG_MODULE_NAME) - + strlen(reader_options) + 1); - if (p == NULL) + if ((p = malloc(module_len + opt_len)) == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or modules which are not added to * the archive read object. */ - strncpy(p, IGNORE_WRONG_MODULE_NAME, - sizeof(IGNORE_WRONG_MODULE_NAME) -1); - strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options); + memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); + memcpy(p + module_len, reader_options, opt_len); r = archive_read_set_options(a, p); free(p); if (r == ARCHIVE_FATAL) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); if (bsdtar->option_ignore_zeros) if (archive_read_set_options(a, "read_concatenated_archives") != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (bsdtar->passphrase != NULL) r = archive_read_add_passphrase(a, bsdtar->passphrase); else r = archive_read_set_passphrase_callback(a, bsdtar, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, bsdtar->filename, bsdtar->bytes_per_block)) lafe_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); if (mode == 'x') { /* Set an extract callback so that we can handle SIGINFO. */ progress_data.bsdtar = bsdtar; progress_data.archive = a; archive_read_extract_set_progress_callback(a, progress_func, &progress_data); } if (mode == 'x' && bsdtar->option_chroot) { #if HAVE_CHROOT if (chroot(".") != 0) lafe_errc(1, errno, "Can't chroot to \".\""); #else lafe_errc(1, 0, "chroot isn't supported on this platform"); #endif } #if defined(_WIN32) && !defined(__CYGWIN__) if (mode == 'x' && bsdtar->option_stdout) { _setmode(1, _O_BINARY); } #endif for (;;) { /* Support --fast-read option */ const char *p; if (bsdtar->option_fast_read && archive_match_path_unmatched_inclusions(bsdtar->matching) == 0) break; r = archive_read_next_header(a, &entry); progress_data.entry = entry; if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ lafe_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) break; p = archive_entry_pathname(entry); if (p == NULL || p[0] == '\0') { lafe_warnc(0, "Archive entry has empty or unreadable filename ... skipping."); bsdtar->return_value = 1; continue; } if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); archive_entry_set_uname(entry, NULL); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); archive_entry_set_gname(entry, NULL); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more * control over exclusions, since rewrites always lose * information. (For example, consider a rewrite * s/foo[0-9]/foo/. If we check exclusions after the * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ if (archive_match_excluded(bsdtar->matching, entry)) continue; /* Excluded by a pattern test. */ if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ out = bsdtar->option_stdout ? stderr : stdout; /* * TODO: Provide some reasonable way to * preview rewrites. gtar always displays * the unedited path in -t output, which means * you cannot easily preview rewrites. */ if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); else list_item_verbose(bsdtar, out, entry); fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } fprintf(out, "\n"); } else { /* Note: some rewrite failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) continue; /* Excluded by a rewrite failure. */ if (bsdtar->option_interactive && !yes("extract '%s'", archive_entry_pathname(entry))) continue; if (bsdtar->verbose > 1) { /* GNU tar uses -tv format with -xvv */ safe_fprintf(stderr, "x "); list_item_verbose(bsdtar, stderr, entry); fflush(stderr); } else if (bsdtar->verbose > 0) { /* Format follows SUSv2, including the * deferred '\n'. */ safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); } /* TODO siginfo_printinfo(bsdtar, 0); */ if (bsdtar->option_stdout) r = archive_read_data_into_fd(a, 1); else r = archive_read_extract2(a, entry, writer); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) fprintf(stderr, "\n"); bsdtar->return_value = 1; } if (bsdtar->verbose) fprintf(stderr, "\n"); if (r == ARCHIVE_FATAL) break; } } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_filter_name(a, 0)); archive_read_free(a); } static int unmatched_inclusions_warn(struct archive *matching, const char *msg) { const char *p; int r; if (matching == NULL) return (0); while ((r = archive_match_path_unmatched_inclusions_next( matching, &p)) == ARCHIVE_OK) lafe_warnc(0, "%s: %s", p, msg); if (r == ARCHIVE_FATAL) lafe_errc(1, errno, "Out of memory"); return (archive_match_path_unmatched_inclusions(matching)); } Index: vendor/libarchive/dist/tar/write.c =================================================================== --- vendor/libarchive/dist/tar/write.c (revision 310621) +++ vendor/libarchive/dist/tar/write.c (revision 310622) @@ -1,1053 +1,1052 @@ /*- * 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 "bsdtar_platform.h" __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_LIBGEN_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_PWD_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 #include "bsdtar.h" #include "err.h" #include "line_reader.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct archive_dir_entry { struct archive_dir_entry *next; time_t mtime_sec; int mtime_nsec; char *name; }; struct archive_dir { struct archive_dir_entry *head, *tail; }; static int append_archive(struct bsdtar *, struct archive *, struct archive *ina); static int append_archive_filename(struct bsdtar *, struct archive *, const char *fname); static void archive_names_from_file(struct bsdtar *bsdtar, struct archive *a); static int copy_file_data_block(struct bsdtar *, struct archive *a, struct archive *, struct archive_entry *); static void excluded_callback(struct archive *, void *, struct archive_entry *); static void report_write(struct bsdtar *, struct archive *, struct archive_entry *, int64_t progress); static void test_for_append(struct bsdtar *); static int metadata_filter(struct archive *, void *, struct archive_entry *); static void write_archive(struct archive *, struct bsdtar *); static void write_entry(struct bsdtar *, struct archive *, struct archive_entry *); static void write_file(struct bsdtar *, struct archive *, struct archive_entry *); static void write_hierarchy(struct bsdtar *, struct archive *, const char *); #if defined(_WIN32) && !defined(__CYGWIN__) /* Not a full lseek() emulation, but enough for our needs here. */ static int seek_file(int fd, int64_t offset, int whence) { LARGE_INTEGER distance; (void)whence; /* UNUSED */ distance.QuadPart = offset; return (SetFilePointerEx((HANDLE)_get_osfhandle(fd), distance, NULL, FILE_BEGIN) ? 1 : -1); } #define open _open #define close _close #define read _read #ifdef lseek #undef lseek #endif #define lseek seek_file #endif static void set_writer_options(struct bsdtar *bsdtar, struct archive *a) { const char *writer_options; int r; writer_options = getenv(ENV_WRITER_OPTIONS); if (writer_options != NULL) { + size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; + size_t opt_len = strlen(writer_options) + 1; char *p; /* Set default write options. */ - p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) - + strlen(writer_options) + 1); - if (p == NULL) + if ((p = malloc(module_len + opt_len)) == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or filters which are not added to * the archive write object. */ - strncpy(p, IGNORE_WRONG_MODULE_NAME, - sizeof(IGNORE_WRONG_MODULE_NAME) -1); - strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, writer_options); + memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); + memcpy(p, writer_options, opt_len); r = archive_write_set_options(a, p); free(p); if (r < ARCHIVE_WARN) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); } static void set_reader_options(struct bsdtar *bsdtar, struct archive *a) { const char *reader_options; int r; (void)bsdtar; /* UNUSED */ reader_options = getenv(ENV_READER_OPTIONS); if (reader_options != NULL) { + size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; + size_t opt_len = strlen(reader_options) + 1; char *p; /* Set default write options. */ - p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) - + strlen(reader_options) + 1); + if ((p = malloc(module_len + opt_len)) == NULL) if (p == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or filters which are not added to * the archive write object. */ - strncpy(p, IGNORE_WRONG_MODULE_NAME, - sizeof(IGNORE_WRONG_MODULE_NAME) -1); - strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options); + memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); + memcpy(p, reader_options, opt_len); r = archive_read_set_options(a, p); free(p); if (r < ARCHIVE_WARN) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } } void tar_mode_c(struct bsdtar *bsdtar) { struct archive *a; const void *filter_name; int r; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) lafe_errc(1, 0, "no files or directories specified"); a = archive_write_new(); /* Support any format that the library supports. */ if (cset_get_format(bsdtar->cset) == NULL) { r = archive_write_set_format_pax_restricted(a); cset_set_format(bsdtar->cset, "pax restricted"); } else { r = archive_write_set_format_by_name(a, cset_get_format(bsdtar->cset)); } if (r != ARCHIVE_OK) { fprintf(stderr, "Can't use format %s: %s\n", cset_get_format(bsdtar->cset), archive_error_string(a)); usage(); } archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block); r = cset_write_add_filters(bsdtar->cset, a, &filter_name); if (r < ARCHIVE_WARN) { lafe_errc(1, 0, "Unsupported compression option --%s", (const char *)filter_name); } set_writer_options(bsdtar, a); if (bsdtar->passphrase != NULL) r = archive_write_set_passphrase(a, bsdtar->passphrase); else r = archive_write_set_passphrase_callback(a, bsdtar, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_filename(a, bsdtar->filename)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); } /* * Same as 'c', except we only support tar or empty formats in * uncompressed files on disk. */ void tar_mode_r(struct bsdtar *bsdtar) { int64_t end_offset; int format; struct archive *a; struct archive_entry *entry; int r; /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; #if defined(__BORLANDC__) bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY); #else bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY, 0666); #endif if (bsdtar->fd < 0) lafe_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); archive_read_support_filter_all(a); archive_read_support_format_empty(a); archive_read_support_format_tar(a); archive_read_support_format_gnutar(a); set_reader_options(bsdtar, a); r = archive_read_open_fd(a, bsdtar->fd, 10240); if (r != ARCHIVE_OK) lafe_errc(1, archive_errno(a), "Can't read archive %s: %s", bsdtar->filename, archive_error_string(a)); while (0 == archive_read_next_header(a, &entry)) { if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { archive_read_free(a); close(bsdtar->fd); lafe_errc(1, 0, "Cannot append to compressed archive."); } /* Keep going until we hit end-of-archive */ format = archive_format(a); } end_offset = archive_read_header_position(a); archive_read_free(a); /* Re-open archive for writing */ a = archive_write_new(); /* * Set the format to be used for writing. To allow people to * extend empty files, we need to allow them to specify the format, * which opens the possibility that they will specify a format that * doesn't match the existing format. Hence, the following bit * of arcane ugliness. */ if (cset_get_format(bsdtar->cset) != NULL) { /* If the user requested a format, use that, but ... */ archive_write_set_format_by_name(a, cset_get_format(bsdtar->cset)); /* ... complain if it's not compatible. */ format &= ARCHIVE_FORMAT_BASE_MASK; if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK) && format != ARCHIVE_FORMAT_EMPTY) { lafe_errc(1, 0, "Format %s is incompatible with the archive %s.", cset_get_format(bsdtar->cset), bsdtar->filename); } } else { /* * Just preserve the current format, with a little care * for formats that libarchive can't write. */ if (format == ARCHIVE_FORMAT_EMPTY) format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; archive_write_set_format(a, format); } if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) lafe_errc(1, errno, "Could not seek to archive end"); set_writer_options(bsdtar, a); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); /* XXX check return val XXX */ close(bsdtar->fd); bsdtar->fd = -1; } void tar_mode_u(struct bsdtar *bsdtar) { int64_t end_offset; struct archive *a; struct archive_entry *entry; int format; struct archive_dir_entry *p; struct archive_dir archive_dir; bsdtar->archive_dir = &archive_dir; memset(&archive_dir, 0, sizeof(archive_dir)); format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); bsdtar->fd = open(bsdtar->filename, O_RDWR | O_BINARY); if (bsdtar->fd < 0) lafe_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); archive_read_support_filter_all(a); archive_read_support_format_tar(a); archive_read_support_format_gnutar(a); set_reader_options(bsdtar, a); if (archive_read_open_fd(a, bsdtar->fd, bsdtar->bytes_per_block) != ARCHIVE_OK) { lafe_errc(1, 0, "Can't open %s: %s", bsdtar->filename, archive_error_string(a)); } /* Build a list of all entries and their recorded mod times. */ while (0 == archive_read_next_header(a, &entry)) { if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { archive_read_free(a); close(bsdtar->fd); lafe_errc(1, 0, "Cannot append to compressed archive."); } if (archive_match_exclude_entry(bsdtar->matching, ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER | ARCHIVE_MATCH_EQUAL, entry) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); /* Record the last format determination we see */ format = archive_format(a); /* Keep going until we hit end-of-archive */ } end_offset = archive_read_header_position(a); archive_read_free(a); /* Re-open archive for writing. */ a = archive_write_new(); /* * Set format to same one auto-detected above. */ archive_write_set_format(a, format); archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block); if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) lafe_errc(1, errno, "Could not seek to archive end"); set_writer_options(bsdtar, a); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); close(bsdtar->fd); bsdtar->fd = -1; while (bsdtar->archive_dir->head != NULL) { p = bsdtar->archive_dir->head->next; free(bsdtar->archive_dir->head->name); free(bsdtar->archive_dir->head); bsdtar->archive_dir->head = p; } bsdtar->archive_dir->tail = NULL; } /* * Write user-specified files/dirs to opened archive. */ static void write_archive(struct archive *a, struct bsdtar *bsdtar) { const char *arg; struct archive_entry *entry, *sparse_entry; /* Choose a suitable copy buffer size */ bsdtar->buff_size = 64 * 1024; while (bsdtar->buff_size < (size_t)bsdtar->bytes_per_block) bsdtar->buff_size *= 2; /* Try to compensate for space we'll lose to alignment. */ bsdtar->buff_size += 16 * 1024; /* Allocate a buffer for file data. */ if ((bsdtar->buff = malloc(bsdtar->buff_size)) == NULL) lafe_errc(1, 0, "cannot allocate memory"); if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) lafe_errc(1, 0, "cannot create link resolver"); archive_entry_linkresolver_set_strategy(bsdtar->resolver, archive_format(a)); /* Create a read_disk object. */ if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) lafe_errc(1, 0, "Cannot create read_disk object"); /* Tell the read_disk how handle symlink. */ switch (bsdtar->symlink_mode) { case 'H': archive_read_disk_set_symlink_hybrid(bsdtar->diskreader); break; case 'L': archive_read_disk_set_symlink_logical(bsdtar->diskreader); break; default: archive_read_disk_set_symlink_physical(bsdtar->diskreader); break; } /* Register entry filters. */ archive_read_disk_set_matching(bsdtar->diskreader, bsdtar->matching, excluded_callback, bsdtar); archive_read_disk_set_metadata_filter_callback( bsdtar->diskreader, metadata_filter, bsdtar); /* Set the behavior of archive_read_disk. */ archive_read_disk_set_behavior(bsdtar->diskreader, bsdtar->readdisk_flags); archive_read_disk_set_standard_lookup(bsdtar->diskreader); if (bsdtar->names_from_file != NULL) archive_names_from_file(bsdtar, a); while (*bsdtar->argv) { arg = *bsdtar->argv; if (arg[0] == '-' && arg[1] == 'C') { arg += 2; if (*arg == '\0') { bsdtar->argv++; arg = *bsdtar->argv; if (arg == NULL) { lafe_warnc(0, "%s", "Missing argument for -C"); bsdtar->return_value = 1; goto cleanup; } if (*arg == '\0') { lafe_warnc(0, "Meaningless argument for -C: ''"); bsdtar->return_value = 1; goto cleanup; } } set_chdir(bsdtar, arg); } else { if (*arg != '/' && (arg[0] != '@' || arg[1] != '/')) do_chdir(bsdtar); /* Handle a deferred -C */ if (*arg == '@') { if (append_archive_filename(bsdtar, a, arg + 1) != 0) break; } else write_hierarchy(bsdtar, a, arg); } bsdtar->argv++; } archive_read_disk_set_matching(bsdtar->diskreader, NULL, NULL, NULL); archive_read_disk_set_metadata_filter_callback( bsdtar->diskreader, NULL, NULL); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); while (entry != NULL) { int r; struct archive_entry *entry2; struct archive *disk = bsdtar->diskreader; /* * This tricky code here is to correctly read the contents * of the entry because the disk reader bsdtar->diskreader * is pointing at does not have any information about the * entry by this time and using archive_read_data_block() * with the disk reader consequently must fail. And we * have to re-open the entry to read the contents. */ /* TODO: Work with -C option as well. */ r = archive_read_disk_open(disk, archive_entry_sourcepath(entry)); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); bsdtar->return_value = 1; archive_entry_free(entry); continue; } /* * Invoke archive_read_next_header2() to work * archive_read_data_block(), which is called via write_file(), * without failure. */ entry2 = archive_entry_new(); r = archive_read_next_header2(disk, entry2); archive_entry_free(entry2); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); if (r == ARCHIVE_FATAL) bsdtar->return_value = 1; else archive_read_close(disk); archive_entry_free(entry); continue; } write_file(bsdtar, a, entry); archive_entry_free(entry); archive_read_close(disk); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); } if (archive_write_close(a)) { lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; } cleanup: /* Free file data buffer. */ free(bsdtar->buff); archive_entry_linkresolver_free(bsdtar->resolver); bsdtar->resolver = NULL; archive_read_free(bsdtar->diskreader); bsdtar->diskreader = NULL; if (bsdtar->option_totals) { fprintf(stderr, "Total bytes written: %s\n", tar_i64toa(archive_filter_bytes(a, -1))); } archive_write_free(a); } /* * Archive names specified in file. * * Unless --null was specified, a line containing exactly "-C" will * cause the next line to be a directory to pass to chdir(). If * --null is specified, then a line "-C" is just another filename. */ static void archive_names_from_file(struct bsdtar *bsdtar, struct archive *a) { struct lafe_line_reader *lr; const char *line; bsdtar->next_line_is_dir = 0; lr = lafe_line_reader(bsdtar->names_from_file, bsdtar->option_null); while ((line = lafe_line_reader_next(lr)) != NULL) { if (bsdtar->next_line_is_dir) { if (*line != '\0') set_chdir(bsdtar, line); else { lafe_warnc(0, "Meaningless argument for -C: ''"); bsdtar->return_value = 1; } bsdtar->next_line_is_dir = 0; } else if (!bsdtar->option_null && strcmp(line, "-C") == 0) bsdtar->next_line_is_dir = 1; else { if (*line != '/') do_chdir(bsdtar); /* Handle a deferred -C */ write_hierarchy(bsdtar, a, line); } } lafe_line_reader_free(lr); if (bsdtar->next_line_is_dir) lafe_errc(1, errno, "Unexpected end of filename list; " "directory expected after -C"); } /* * Copy from specified archive to current archive. Returns non-zero * for write errors (which force us to terminate the entire archiving * operation). If there are errors reading the input archive, we set * bsdtar->return_value but return zero, so the overall archiving * operation will complete and return non-zero. */ static int append_archive_filename(struct bsdtar *bsdtar, struct archive *a, const char *raw_filename) { struct archive *ina; const char *filename = raw_filename; int rc; if (strcmp(filename, "-") == 0) filename = NULL; /* Library uses NULL for stdio. */ ina = archive_read_new(); archive_read_support_format_all(ina); archive_read_support_filter_all(ina); set_reader_options(bsdtar, ina); archive_read_set_options(ina, "mtree:checkfs"); if (bsdtar->passphrase != NULL) rc = archive_read_add_passphrase(a, bsdtar->passphrase); else rc = archive_read_set_passphrase_callback(ina, bsdtar, &passphrase_callback); if (rc != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(ina, filename, bsdtar->bytes_per_block)) { lafe_warnc(0, "%s", archive_error_string(ina)); bsdtar->return_value = 1; return (0); } rc = append_archive(bsdtar, a, ina); if (rc != ARCHIVE_OK) { lafe_warnc(0, "Error reading archive %s: %s", raw_filename, archive_error_string(ina)); bsdtar->return_value = 1; } archive_read_free(ina); return (rc); } static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { struct archive_entry *in_entry; int e; while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) { if (archive_match_excluded(bsdtar->matching, in_entry)) continue; if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, in_entry); } else if (bsdtar->verbose > 0) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); if (need_report()) report_write(bsdtar, a, in_entry, 0); e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) lafe_warnc(0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); if (e >= ARCHIVE_WARN) { if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); else if (copy_file_data_block(bsdtar, a, ina, in_entry)) exit(1); } if (bsdtar->verbose) fprintf(stderr, "\n"); } return (e == ARCHIVE_EOF ? ARCHIVE_OK : e); } /* Helper function to copy file to archive. */ static int copy_file_data_block(struct bsdtar *bsdtar, struct archive *a, struct archive *in_a, struct archive_entry *entry) { size_t bytes_read; ssize_t bytes_written; int64_t offset, progress = 0; char *null_buff = NULL; const void *buff; int r; while ((r = archive_read_data_block(in_a, &buff, &bytes_read, &offset)) == ARCHIVE_OK) { if (need_report()) report_write(bsdtar, a, entry, progress); if (offset > progress) { int64_t sparse = offset - progress; size_t ns; if (null_buff == NULL) { null_buff = bsdtar->buff; memset(null_buff, 0, bsdtar->buff_size); } while (sparse > 0) { if (sparse > (int64_t)bsdtar->buff_size) ns = bsdtar->buff_size; else ns = (size_t)sparse; bytes_written = archive_write_data(a, null_buff, ns); if (bytes_written < 0) { /* Write failed; this is bad */ lafe_warnc(0, "%s", archive_error_string(a)); return (-1); } if ((size_t)bytes_written < ns) { /* Write was truncated; warn but * continue. */ lafe_warnc(0, "%s: Truncated write; file may " "have grown while being archived.", archive_entry_pathname(entry)); return (0); } progress += bytes_written; sparse -= bytes_written; } } bytes_written = archive_write_data(a, buff, bytes_read); if (bytes_written < 0) { /* Write failed; this is bad */ lafe_warnc(0, "%s", archive_error_string(a)); return (-1); } if ((size_t)bytes_written < bytes_read) { /* Write was truncated; warn but continue. */ lafe_warnc(0, "%s: Truncated write; file may have grown " "while being archived.", archive_entry_pathname(entry)); return (0); } progress += bytes_written; } if (r < ARCHIVE_WARN) { lafe_warnc(archive_errno(a), "%s", archive_error_string(a)); return (-1); } return (0); } static void excluded_callback(struct archive *a, void *_data, struct archive_entry *entry) { struct bsdtar *bsdtar = (struct bsdtar *)_data; if (bsdtar->option_no_subdirs) return; if (!archive_read_disk_can_descend(a)) return; if (bsdtar->option_interactive && !yes("add '%s'", archive_entry_pathname(entry))) return; archive_read_disk_descend(a); } static int metadata_filter(struct archive *a, void *_data, struct archive_entry *entry) { struct bsdtar *bsdtar = (struct bsdtar *)_data; /* XXX TODO: check whether this filesystem is * synthetic and/or local. Add a new * --local-only option to skip non-local * filesystems. Skip synthetic filesystems * regardless. * * The results should be cached, since * tree.c doesn't usually visit a directory * and the directory contents together. A simple * move-to-front list should perform quite well. * * Use archive_read_disk_current_filesystem_is_remote(). */ /* * If the user vetoes this file/directory, skip it. * We want this to be fairly late; if some other * check would veto this file, we shouldn't bother * the user with it. */ if (bsdtar->option_interactive && !yes("add '%s'", archive_entry_pathname(entry))) return (0); /* Note: if user vetoes, we won't descend. */ if (!bsdtar->option_no_subdirs && archive_read_disk_can_descend(a)) archive_read_disk_descend(a); return (1); } /* * Add the file or dir hierarchy named by 'path' to the archive */ static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { struct archive *disk = bsdtar->diskreader; struct archive_entry *entry = NULL, *spare_entry = NULL; int r; r = archive_read_disk_open(disk, path); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); bsdtar->return_value = 1; return; } bsdtar->first_fs = -1; for (;;) { archive_entry_free(entry); entry = archive_entry_new(); r = archive_read_next_header2(disk, entry); if (r == ARCHIVE_EOF) break; else if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); if (r == ARCHIVE_FATAL || r == ARCHIVE_FAILED) { bsdtar->return_value = 1; archive_entry_free(entry); archive_read_close(disk); return; } else if (r < ARCHIVE_WARN) continue; } if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); if (!bsdtar->uname) archive_entry_set_uname(entry, archive_read_disk_uname(bsdtar->diskreader, bsdtar->uid)); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); if (!bsdtar->gname) archive_entry_set_gname(entry, archive_read_disk_gname(bsdtar->diskreader, bsdtar->gid)); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Rewrite the pathname to be archived. If rewrite * fails, skip the entry. */ if (edit_pathname(bsdtar, entry)) continue; /* Display entry as we process it. */ if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, entry); } else if (bsdtar->verbose > 0) { /* This format is required by SUSv2. */ safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); } /* Non-regular files get archived with zero size. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); while (entry != NULL) { write_file(bsdtar, a, entry); archive_entry_free(entry); entry = spare_entry; spare_entry = NULL; } if (bsdtar->verbose) fprintf(stderr, "\n"); } archive_entry_free(entry); archive_read_close(disk); } /* * Write a single file (or directory or other filesystem object) to * the archive. */ static void write_file(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry) { write_entry(bsdtar, a, entry); } /* * Write a single entry to the archive. */ static void write_entry(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry) { int e; e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, entry); lafe_warnc(0, ": %s", archive_error_string(a)); } else if (bsdtar->verbose > 0) { lafe_warnc(0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); } else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); /* * If we opened a file earlier, write it out now. Note that * the format handler might have reset the size field to zero * to inform us that the archive body won't get stored. In * that case, just skip the write. */ if (e >= ARCHIVE_WARN && archive_entry_size(entry) > 0) { if (copy_file_data_block(bsdtar, a, bsdtar->diskreader, entry)) exit(1); } } static void report_write(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry, int64_t progress) { uint64_t comp, uncomp; int compression; if (bsdtar->verbose) fprintf(stderr, "\n"); comp = archive_filter_bytes(a, -1); uncomp = archive_filter_bytes(a, 0); fprintf(stderr, "In: %d files, %s bytes;", archive_file_count(a), tar_i64toa(uncomp)); if (comp > uncomp) compression = 0; else compression = (int)((uncomp - comp) * 100 / uncomp); fprintf(stderr, " Out: %s bytes, compression %d%%\n", tar_i64toa(comp), compression); /* Can't have two calls to tar_i64toa() pending, so split the output. */ safe_fprintf(stderr, "Current: %s (%s", archive_entry_pathname(entry), tar_i64toa(progress)); fprintf(stderr, "/%s bytes)\n", tar_i64toa(archive_entry_size(entry))); } static void test_for_append(struct bsdtar *bsdtar) { struct stat s; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) lafe_errc(1, 0, "no files or directories specified"); if (bsdtar->filename == NULL) lafe_errc(1, 0, "Cannot append to stdout."); if (stat(bsdtar->filename, &s) != 0) return; if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) lafe_errc(1, 0, "Cannot append to %s: not a regular file.", bsdtar->filename); /* Is this an appropriate check here on Windows? */ /* if (GetFileType(handle) != FILE_TYPE_DISK) lafe_errc(1, 0, "Cannot append"); */ }