Index: vendor/libarchive/dist/Makefile.am =================================================================== --- vendor/libarchive/dist/Makefile.am (revision 302002) +++ vendor/libarchive/dist/Makefile.am (revision 302003) @@ -1,1243 +1,1246 @@ ## 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 \ 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_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_solaris_tar_acl.c \ libarchive/test/test_compat_solaris_pax_sparse.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_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_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_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_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_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_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_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_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_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.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_length_at_end.zip.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/cat/test/main.c =================================================================== --- vendor/libarchive/dist/cat/test/main.c (revision 302002) +++ vendor/libarchive/dist/cat/test/main.c (revision 302003) @@ -1,2969 +1,2992 @@ /* * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include "test_utils.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include /* * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. * TODO: Move this into a separate configuration header, have all test * suites share one copy of this file. */ #define KNOWNREF "test_expand.Z.uu" #define ENVBASE "BSDCAT" /* Prefix for environment variables. */ #define PROGRAM "bsdcat" /* Name of program being tested. */ #define PROGRAM_ALIAS "cat" /* Generic alias for program */ #undef LIBRARY /* Not testing a library. */ #undef EXTRA_DUMP /* How to dump extra data */ #undef EXTRA_ERRNO /* How to dump errno */ /* How to generate extra version info. */ #define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, flags); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalide sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not leagal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %d bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n", fn); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %lld.%09lld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)pathname; /* UNUSED */ (void)contents; /* UNUSED */ assertion_count(file, line); /* Windows sort-of has real symlinks, but they're only usable * by privileged users and are crippled even then, so there's * really not much point in bothering with this. */ return (0); #else char buff[300]; struct stat st; ssize_t linklen; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff)); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents) { if (is_symlink(file, line, path, contents)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) return (1); #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); return (0); } } close(fd); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Create a symlink and report any failures. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto) { #if defined(_WIN32) && !defined(__CYGWIN__) int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Set nodump, report failures. */ int assertion_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 -d -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int canNodump(void) { const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int canNodump(void) { const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); close(fd); if (flags & EXT2_NODUMP_FL) return (1); return (0); } #else int canNodump() { return (0); } #endif /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { char workdir[1024]; char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { - char tried[512] = { '\0' }; - char buff[128]; - char *pwd, *p; + size_t tried_size, buff_size; + char *buff, *tried, *pwd = NULL, *p = NULL; +#ifdef PATH_MAX + buff_size = PATH_MAX; +#else + buff_size = 8192; +#endif + buff = calloc(buff_size, 1); + if (buff == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + + /* Allocate a buffer to hold the various directories we checked. */ + tried_size = buff_size * 2; + tried = calloc(tried_size, 1); + if (tried == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; - snprintf(buff, sizeof(buff), "%s", d); + snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ - snprintf(buff, sizeof(buff), "%s", pwd); + snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd); + snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); + snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM_ALIAS); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, sizeof(buff), "%s", pwd + 8); + snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd + 8); + snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); - return strdup(buff); + free(tried); + + /* Copy result into a fresh buffer to reduce memory usage. */ + p = strdup(buff); + free(buff); + return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: vendor/libarchive/dist/configure.ac =================================================================== --- vendor/libarchive/dist/configure.ac (revision 302002) +++ vendor/libarchive/dist/configure.ac (revision 302003) @@ -1,877 +1,878 @@ dnl Process this file with autoconf to produce a configure script. dnl First, define all of the version numbers up front. dnl In particular, this allows the version macro to be used in AC_INIT dnl These first two version numbers are updated automatically on each release. m4_define([LIBARCHIVE_VERSION_S],[3.2.0]) m4_define([LIBARCHIVE_VERSION_N],[3002000]) dnl bsdtar and bsdcpio versioning tracks libarchive m4_define([BSDTAR_VERSION_S],LIBARCHIVE_VERSION_S()) m4_define([BSDCPIO_VERSION_S],LIBARCHIVE_VERSION_S()) m4_define([BSDCAT_VERSION_S],LIBARCHIVE_VERSION_S()) AC_PREREQ([2.69]) # # Now starts the "real" configure script. # AC_INIT([libarchive],[LIBARCHIVE_VERSION_S()],[libarchive-discuss@googlegroups.com]) # Make sure the srcdir contains "libarchive" directory AC_CONFIG_SRCDIR([libarchive]) # Use auxiliary subscripts from this subdirectory (cleans up root) AC_CONFIG_AUX_DIR([build/autoconf]) # M4 scripts AC_CONFIG_MACRO_DIR([build/autoconf]) # Must follow AC_CONFIG macros above... AM_INIT_AUTOMAKE() m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) # Libtool's "interface version" can be computed from the libarchive version. # Libtool interface version bumps on any API change, so increments # whenever libarchive minor version does. ARCHIVE_MINOR=$(( (LIBARCHIVE_VERSION_N() / 1000) % 1000 )) # Libarchive 2.7 == libtool interface 9 = 2 + 7 # Libarchive 2.8 == libtool interface 10 = 2 + 8 # Libarchive 2.9 == libtool interface 11 = 2 + 8 # Libarchive 3.0 == libtool interface 12 # Libarchive 3.1 == libtool interface 13 ARCHIVE_INTERFACE=`echo $((13 + ${ARCHIVE_MINOR}))` # Libarchive revision is bumped on any source change === libtool revision ARCHIVE_REVISION=$(( LIBARCHIVE_VERSION_N() % 1000 )) # Libarchive minor is bumped on any interface addition === libtool age ARCHIVE_LIBTOOL_VERSION=$ARCHIVE_INTERFACE:$ARCHIVE_REVISION:$ARCHIVE_MINOR # Stick the version numbers into config.h AC_DEFINE([LIBARCHIVE_VERSION_STRING],"LIBARCHIVE_VERSION_S()", [Version number of libarchive]) AC_DEFINE_UNQUOTED([LIBARCHIVE_VERSION_NUMBER],"LIBARCHIVE_VERSION_N()", [Version number of libarchive as a single integer]) AC_DEFINE([BSDCPIO_VERSION_STRING],"BSDCPIO_VERSION_S()", [Version number of bsdcpio]) AC_DEFINE([BSDTAR_VERSION_STRING],"BSDTAR_VERSION_S()", [Version number of bsdtar]) AC_DEFINE([BSDCAT_VERSION_STRING],"BSDTAR_VERSION_S()", [Version number of bsdcat]) # The shell variables here must be the same as the AC_SUBST() variables # below, but the shell variable names apparently cannot be the same as # the m4 macro names above. Why? Ask autoconf. BSDCPIO_VERSION_STRING=BSDCPIO_VERSION_S() BSDTAR_VERSION_STRING=BSDTAR_VERSION_S() BSDCAT_VERSION_STRING=BSDCAT_VERSION_S() LIBARCHIVE_VERSION_STRING=LIBARCHIVE_VERSION_S() LIBARCHIVE_VERSION_NUMBER=LIBARCHIVE_VERSION_N() # Substitute the above version numbers into the various files below. # Yes, I believe this is the fourth time we define what are essentially # the same symbols. Why? Ask autoconf. AC_SUBST(ARCHIVE_LIBTOOL_VERSION) AC_SUBST(BSDCPIO_VERSION_STRING) AC_SUBST(BSDTAR_VERSION_STRING) AC_SUBST(BSDCAT_VERSION_STRING) AC_SUBST(LIBARCHIVE_VERSION_STRING) AC_SUBST(LIBARCHIVE_VERSION_NUMBER) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([build/pkgconfig/libarchive.pc]) # Check for host type AC_CANONICAL_HOST dnl Compilation on mingw and Cygwin needs special Makefile rules inc_windows_files=no inc_cygwin_files=no case "$host_os" in *mingw* ) inc_windows_files=yes ;; *cygwin*) inc_cygwin_files=yes ;; esac AM_CONDITIONAL([INC_WINDOWS_FILES], [test $inc_windows_files = yes]) AM_CONDITIONAL([INC_CYGWIN_FILES], [test $inc_cygwin_files = yes]) dnl Defines that are required for specific platforms (e.g. -D_POSIX_SOURCE, etc) PLATFORMCPPFLAGS= case "$host_os" in *mingw* ) PLATFORMCPPFLAGS=-D__USE_MINGW_ANSI_STDIO ;; esac AC_SUBST(PLATFORMCPPFLAGS) # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AC_USE_SYSTEM_EXTENSIONS AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL AC_CHECK_TOOL([STRIP],[strip]) AC_PROG_MKDIR_P # # Options for building bsdtar. # # Default is to build bsdtar, but allow people to override that. # AC_ARG_ENABLE([bsdtar], [AS_HELP_STRING([--enable-bsdtar], [enable build of bsdtar (default)]) AS_HELP_STRING([--enable-bsdtar=static], [force static build of bsdtar]) AS_HELP_STRING([--enable-bsdtar=shared], [force dynamic build of bsdtar]) AS_HELP_STRING([--disable-bsdtar], [disable build of bsdtar])], [], [enable_bsdtar=yes]) case "$enable_bsdtar" in yes) if test "$enable_static" = "no"; then static_bsdtar=no else static_bsdtar=yes fi build_bsdtar=yes ;; dynamic|shared) if test "$enable_shared" = "no"; then AC_MSG_FAILURE([Shared linking of bsdtar requires shared libarchive]) fi build_bsdtar=yes static_bsdtar=no ;; static) build_bsdtar=yes static_bsdtar=yes ;; no) build_bsdtar=no static_bsdtar=no ;; *) AC_MSG_FAILURE([Unsupported value for --enable-bsdtar]) ;; esac AM_CONDITIONAL([BUILD_BSDTAR], [ test "$build_bsdtar" = yes ]) AM_CONDITIONAL([STATIC_BSDTAR], [ test "$static_bsdtar" = yes ]) # # Options for building bsdcat. # # Default is to build bsdcat, but allow people to override that. # AC_ARG_ENABLE([bsdcat], [AS_HELP_STRING([--enable-bsdcat], [enable build of bsdcat (default)]) AS_HELP_STRING([--enable-bsdcat=static], [force static build of bsdcat]) AS_HELP_STRING([--enable-bsdcat=shared], [force dynamic build of bsdcat]) AS_HELP_STRING([--disable-bsdcat], [disable build of bsdcat])], [], [enable_bsdcat=yes]) case "$enable_bsdcat" in yes) if test "$enable_static" = "no"; then static_bsdcat=no else static_bsdcat=yes fi build_bsdcat=yes ;; dynamic|shared) if test "$enable_shared" = "no"; then AC_MSG_FAILURE([Shared linking of bsdcat requires shared libarchive]) fi build_bsdcat=yes static_bsdcat=no ;; static) build_bsdcat=yes static_bsdcat=yes ;; no) build_bsdcat=no static_bsdcat=no ;; *) AC_MSG_FAILURE([Unsupported value for --enable-bsdcat]) ;; esac AM_CONDITIONAL([BUILD_BSDCAT], [ test "$build_bsdcat" = yes ]) AM_CONDITIONAL([STATIC_BSDCAT], [ test "$static_bsdcat" = yes ]) # # Options for building bsdcpio. # # Default is not to build bsdcpio, but that can be overridden. # AC_ARG_ENABLE([bsdcpio], [AS_HELP_STRING([--enable-bsdcpio], [enable build of bsdcpio (default)]) AS_HELP_STRING([--enable-bsdcpio=static], [static build of bsdcpio]) AS_HELP_STRING([--enable-bsdcpio=shared], [dynamic build of bsdcpio]) AS_HELP_STRING([--disable-bsdcpio], [disable build of bsdcpio])], [], [enable_bsdcpio=yes]) case "$enable_bsdcpio" in yes) if test "$enable_static" = "no"; then static_bsdcpio=no else static_bsdcpio=yes fi build_bsdcpio=yes ;; dynamic|shared) if test "$enabled_shared" = "no"; then AC_MSG_FAILURE([Shared linking of bsdcpio requires shared libarchive]) fi build_bsdcpio=yes ;; static) build_bsdcpio=yes static_bsdcpio=yes ;; no) build_bsdcpio=no static_bsdcpio=no ;; *) AC_MSG_FAILURE([Unsupported value for --enable-bsdcpio]) ;; esac AM_CONDITIONAL([BUILD_BSDCPIO], [ test "$build_bsdcpio" = yes ]) AM_CONDITIONAL([STATIC_BSDCPIO], [ test "$static_bsdcpio" = yes ]) # Set up defines needed before including any headers case $host in *mingw* | *cygwin* ) AC_DEFINE([_WIN32_WINNT], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) AC_DEFINE([WINVER], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) AC_DEFINE([NTDDI_VERSION], 0x05020000, [Define to '0x05020000' for Windows Server 2003 APIs.]) ;; esac # Checks for header files. AC_HEADER_DIRENT AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([acl/libacl.h attr/xattr.h copyfile.h ctype.h]) +AC_CHECK_HEADERS([copyfile.h ctype.h]) AC_CHECK_HEADERS([errno.h ext2fs/ext2_fs.h fcntl.h grp.h]) AC_CACHE_CHECK([whether EXT2_IOC_GETFLAGS is usable], [ac_cv_have_decl_EXT2_IOC_GETFLAGS], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include @%:@include ], [int x = EXT2_IOC_GETFLAGS])], [AS_VAR_SET([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes])], [AS_VAR_SET([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [no])])]) AS_VAR_IF([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes], [AC_DEFINE_UNQUOTED([HAVE_WORKING_EXT2_IOC_GETFLAGS], [1], [Define to 1 if you have a working EXT2_IOC_GETFLAGS])]) AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h]) AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h linux/types.h]) AC_CHECK_HEADERS([locale.h paths.h poll.h pthread.h pwd.h]) AC_CHECK_HEADERS([readpassphrase.h signal.h spawn.h]) AC_CHECK_HEADERS([stdarg.h stdint.h stdlib.h string.h]) -AC_CHECK_HEADERS([sys/acl.h sys/cdefs.h sys/extattr.h]) +AC_CHECK_HEADERS([sys/cdefs.h sys/extattr.h]) AC_CHECK_HEADERS([sys/ioctl.h sys/mkdev.h sys/mount.h]) AC_CHECK_HEADERS([sys/param.h sys/poll.h sys/select.h sys/statfs.h sys/statvfs.h]) AC_CHECK_HEADERS([sys/time.h sys/utime.h sys/utsname.h sys/vfs.h]) AC_CHECK_HEADERS([time.h unistd.h utime.h wchar.h wctype.h]) AC_CHECK_HEADERS([windows.h]) AC_CHECK_HEADERS([Bcrypt.h]) # check windows.h first; the other headers require it. AC_CHECK_HEADERS([wincrypt.h winioctl.h],[],[], [[#ifdef HAVE_WINDOWS_H # include #endif ]]) # Checks for libraries. AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib], [Don't build support for gzip through zlib])) if test "x$with_zlib" != "xno"; then AC_CHECK_HEADERS([zlib.h]) AC_CHECK_LIB(z,inflate) fi AC_ARG_WITH([bz2lib], AS_HELP_STRING([--without-bz2lib], [Don't build support for bzip2 through bz2lib])) if test "x$with_bz2lib" != "xno"; then AC_CHECK_HEADERS([bzlib.h]) case "$host_os" in *mingw* | *cygwin*) dnl AC_CHECK_LIB cannot be used on the Windows port of libbz2, therefore dnl use AC_LINK_IFELSE. AC_MSG_CHECKING([for BZ2_bzDecompressInit in -lbz2]) old_LIBS="$LIBS" LIBS="-lbz2 $LIBS" AC_LINK_IFELSE( [AC_LANG_SOURCE(#include int main() { return BZ2_bzDecompressInit(NULL, 0, 0); })], [ac_cv_lib_bz2_BZ2_bzDecompressInit=yes], [ac_cv_lib_bz2_BZ2_bzDecompressInit=no]) LIBS="$old_LIBS" AC_MSG_RESULT($ac_cv_lib_bz2_BZ2_bzDecompressInit) if test "x$ac_cv_lib_bz2_BZ2_bzDecompressInit" = xyes; then AC_DEFINE([HAVE_LIBBZ2], [1], [Define to 1 if you have the `bz2' library (-lbz2).]) LIBS="-lbz2 $LIBS" fi ;; *) AC_CHECK_LIB(bz2,BZ2_bzDecompressInit) ;; esac fi AC_ARG_WITH([lzmadec], AS_HELP_STRING([--without-lzmadec], [Don't build support for lzma through lzmadec])) if test "x$with_lzmadec" != "xno"; then AC_CHECK_HEADERS([lzmadec.h]) AC_CHECK_LIB(lzmadec,lzmadec_decode) fi AC_ARG_WITH([iconv], AS_HELP_STRING([--without-iconv], [Don't try to link against iconv])) if test "x$with_iconv" != "xno"; then AM_ICONV AC_CHECK_HEADERS([iconv.h],[],[],[#include ]) if test "x$am_cv_func_iconv" = "xyes"; then AC_CHECK_HEADERS([localcharset.h]) am_save_LIBS="$LIBS" LIBS="${LIBS} ${LIBICONV}" AC_CHECK_FUNCS([locale_charset]) LIBS="${am_save_LIBS}" if test "x$ac_cv_func_locale_charset" != "xyes"; then # If locale_charset() is not in libiconv, we have to find libcharset. AC_CHECK_LIB(charset,locale_charset) fi fi fi AC_ARG_WITH([lz4], AS_HELP_STRING([--without-lz4], [Don't build support for lz4 through liblz4])) if test "x$with_lz4" != "xno"; then AC_CHECK_HEADERS([lz4.h lz4hc.h]) AC_CHECK_LIB(lz4,LZ4_decompress_safe) fi AC_ARG_WITH([lzma], AS_HELP_STRING([--without-lzma], [Don't build support for xz through lzma])) if test "x$with_lzma" != "xno"; then AC_CHECK_HEADERS([lzma.h]) AC_CHECK_LIB(lzma,lzma_stream_decoder) # Some pre-release (but widely distributed) versions of liblzma # included a disabled version of lzma_stream_encoder_mt that # fools a naive AC_CHECK_LIB or AC_CHECK_FUNC, so we need # to do something more complex here: AC_CACHE_CHECK( [whether we have multithread support in lzma], ac_cv_lzma_has_mt, [AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[lzma_stream_encoder_mt(0, 0);]])], [ac_cv_lzma_has_mt=yes], [ac_cv_lzma_has_mt=no])]) if test "x$ac_cv_lzma_has_mt" != xno; then AC_DEFINE([HAVE_LZMA_STREAM_ENCODER_MT], [1], [Define to 1 if you have the `lzma_stream_encoder_mt' function.]) fi fi AC_ARG_WITH([lzo2], AS_HELP_STRING([--without-lzo2], [Don't build support for lzop through liblzo2])) if test "x$with_lzo2" != "xno"; then AC_CHECK_HEADERS([lzo/lzoconf.h lzo/lzo1x.h]) AC_CHECK_LIB(lzo2,lzo1x_decompress_safe) fi AC_ARG_WITH([nettle], AS_HELP_STRING([--without-nettle], [Don't build with crypto support from Nettle])) AC_ARG_WITH([openssl], AS_HELP_STRING([--without-openssl], [Don't build support for mtree and xar hashes through openssl])) case "$host_os" in *darwin* ) with_openssl=no ;; esac AC_ARG_WITH([xml2], AS_HELP_STRING([--without-xml2], [Don't build support for xar through libxml2])) AC_ARG_WITH([expat], AS_HELP_STRING([--without-expat], [Don't build support for xar through expat])) if test "x$with_xml2" != "xno"; then PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(LIBXML2_PC, [libxml-2.0], [ CPPFLAGS="${CPPFLAGS} ${LIBXML2_PC_CFLAGS}" LIBS="${LIBS} ${LIBXML2_PC_LIBS}" AC_CHECK_LIB(xml2,xmlInitParser,[true],AC_MSG_FAILURE(Missing xml2 library)) ], [ AC_CHECK_LIB(xml2,xmlInitParser) ]) AC_CHECK_HEADERS([libxml/xmlreader.h libxml/xmlwriter.h]) fi if test "x$ac_cv_header_libxml_xmlreader_h" != "xyes"; then if test "x$with_expat" != "xno"; then AC_CHECK_HEADERS([expat.h]) AC_CHECK_LIB(expat,XML_ParserCreate) fi fi AC_ARG_ENABLE([posix-regex-lib], [AS_HELP_STRING([--enable-posix-regex-lib], [choose what library to use for POSIX regular expression support (default: auto)]) AS_HELP_STRING([--enable-posix-regex-lib=libc], [use libc POSIX regular expression support]) AS_HELP_STRING([--enable-posix-regex-lib=libregex], [use libregex POSIX regular expression support]) AS_HELP_STRING([--enable-posix-regex-lib=libpcreposix], [use libpcreposix POSIX regular expression support]) AS_HELP_STRING([--disable-posix-regex-lib], [don't enable POSIX regular expression support])], [], [enable_posix_regex_lib=auto]) posix_regex_lib_found= if test "$enable_posix_regex_lib" = "auto" || test "$enable_posix_regex_lib" = "libc" || test "$enable_posix_regex_lib" = "libregex"; then AC_CHECK_HEADERS([regex.h]) if test "x$ac_cv_header_regex_h" != "xno"; then AC_CHECK_FUNC(regcomp) if test "x$ac_cv_func_regcomp" = xyes; then posix_regex_lib_found=1 else AC_CHECK_LIB(regex,regcomp) if test "x$ac_cv_lib_regex_regcomp" = xyes; then posix_regex_lib_found=1 fi fi fi fi if test -z $posix_regex_lib_found && (test "$enable_posix_regex_lib" = "auto" || test "$enable_posix_regex_lib" = "libpcreposix"); then AC_CHECK_HEADERS([pcreposix.h]) AC_CHECK_LIB(pcreposix,regcomp) if test "x$ac_cv_lib_pcreposix_regcomp" != xyes; then AC_MSG_NOTICE(trying libpcreposix check again with libpcre) unset ac_cv_lib_pcreposix_regcomp AC_CHECK_LIB(pcre,pcre_exec) AC_CHECK_LIB(pcreposix,regcomp) if test "x$ac_cv_lib_pcre_pcre_exec" = xyes && test "x$ac_cv_lib_pcreposix_regcomp" = xyes; then AC_MSG_CHECKING(if PCRE_STATIC needs to be defined) AC_LINK_IFELSE( [AC_LANG_SOURCE(#include int main() { return regcomp(NULL, NULL, 0); })], [without_pcre_static=yes], [without_pcre_static=no]) AC_LINK_IFELSE( [AC_LANG_SOURCE(#define PCRE_STATIC #include int main() { return regcomp(NULL, NULL, 0); })], [with_pcre_static=yes], [with_pcre_static=no]) if test "x$without_pcre_static" != xyes && test "x$with_pcre_static" = xyes; then AC_MSG_RESULT(yes) AC_DEFINE([PCRE_STATIC], [1], [Define to 1 if PCRE_STATIC needs to be defined.]) elif test "x$without_pcre_static" = xyes || test "x$with_pcre_static" = xyes; then AC_MSG_RESULT(no) fi posix_regex_lib_found=1 fi else posix_regex_lib_found=1 fi fi # TODO: Give the user the option of using a pre-existing system # libarchive. This will define HAVE_LIBARCHIVE which will cause # bsdtar_platform.h to use #include <...> for the libarchive headers. # Need to include Makefile.am magic to link against system # -larchive in that case. #AC_CHECK_LIB(archive,archive_version) # Checks for supported compiler flags AX_APPEND_COMPILE_FLAGS([-Wall -Wformat -Wformat-security]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST # la_TYPE_UID_T defaults to "int", which is incorrect for MinGW # and MSVC. Use a customized version. la_TYPE_UID_T AC_TYPE_MODE_T # AC_TYPE_OFF_T defaults to "long", which limits us to 4GB files on # most systems... default to "long long" instead. AC_CHECK_TYPE(off_t, [long long]) AC_TYPE_SIZE_T AC_CHECK_TYPE(id_t, [unsigned long]) AC_CHECK_TYPE(uintptr_t, [unsigned int]) # Check for tm_gmtoff in struct tm AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.__tm_gmtoff],,, [ #include ]) # Check for f_namemax in struct statfs AC_CHECK_MEMBERS([struct statfs.f_namemax],,, [ #include #include ]) # Check for f_iosize in struct statvfs AC_CHECK_MEMBERS([struct statvfs.f_iosize],,, [ #include ]) # Check for birthtime in struct stat AC_CHECK_MEMBERS([struct stat.st_birthtime]) # Check for high-resolution timestamps in struct stat AC_CHECK_MEMBERS([struct stat.st_birthtimespec.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtimespec.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtime_n]) # AIX AC_CHECK_MEMBERS([struct stat.st_umtime]) # Tru64 AC_CHECK_MEMBERS([struct stat.st_mtime_usec]) # Hurd # Check for block size support in struct stat AC_CHECK_MEMBERS([struct stat.st_blksize]) # Check for st_flags in struct stat (BSD fflags) AC_CHECK_MEMBERS([struct stat.st_flags]) # If you have uintmax_t, we assume printf supports %ju # If you have unsigned long long, we assume printf supports %llu # TODO: Check for %ju and %llu support directly. AC_CHECK_TYPES([uintmax_t, unsigned long long]) # We use C99-style integer types # Declare them if the local platform doesn't already do so. AC_TYPE_INTMAX_T AC_TYPE_UINTMAX_T AC_TYPE_INT64_T AC_TYPE_UINT64_T AC_TYPE_INT32_T AC_TYPE_UINT32_T AC_TYPE_INT16_T AC_TYPE_UINT16_T AC_TYPE_UINT8_T AC_CHECK_DECLS([SIZE_MAX, INT32_MAX, INT32_MIN]) AC_CHECK_DECLS([INT64_MAX, INT64_MIN, UINT64_MAX, UINT32_MAX]) AC_CHECK_DECLS([INTMAX_MAX, INTMAX_MIN, UINTMAX_MAX]) AC_CHECK_DECL([SSIZE_MAX], [AC_DEFINE(HAVE_DECL_SSIZE_MAX, 1, [Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you don't.])], [], [#include ]) AC_CHECK_DECL([EFTYPE], [AC_DEFINE(HAVE_EFTYPE, 1, [A possible errno value for invalid file format errors])], [], [#include ]) AC_CHECK_DECL([EILSEQ], [AC_DEFINE(HAVE_EILSEQ, 1, [A possible errno value for invalid file format errors])], [], [#include ]) AC_CHECK_TYPE([wchar_t], [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]wchar_t), 1, [Define to 1 if the system has the type `wchar_t'.])dnl AC_CHECK_SIZEOF([wchar_t])], []) AC_HEADER_TIME # Checks for library functions. AC_PROG_GCC_TRADITIONAL AC_HEADER_MAJOR AC_FUNC_FSEEKO AC_FUNC_MEMCMP AC_FUNC_LSTAT AC_FUNC_STAT AC_FUNC_STRERROR_R AC_FUNC_STRFTIME AC_FUNC_VPRINTF # check for: # CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES) # To avoid necessity for including windows.h or special forward declaration # workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *' AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *]) AC_CHECK_FUNCS([arc4random_buf chflags chown chroot ctime_r dirfd]) AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fork]) AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate]) AC_CHECK_FUNCS([futimens futimes futimesat]) AC_CHECK_FUNCS([geteuid getpid getgrgid_r getgrnam_r]) AC_CHECK_FUNCS([getpwnam_r getpwuid_r getvfsbyname gmtime_r]) AC_CHECK_FUNCS([lchflags lchmod lchown link localtime_r lstat lutimes]) AC_CHECK_FUNCS([mbrtowc memmove memset]) AC_CHECK_FUNCS([mkdir mkfifo mknod mkstemp]) AC_CHECK_FUNCS([nl_langinfo openat pipe poll posix_spawnp readlink readlinkat]) AC_CHECK_FUNCS([readpassphrase]) AC_CHECK_FUNCS([select setenv setlocale sigaction statfs statvfs]) AC_CHECK_FUNCS([strchr strdup strerror strncpy_s strrchr symlink timegm]) AC_CHECK_FUNCS([tzset unsetenv utime utimensat utimes vfork]) AC_CHECK_FUNCS([wcrtomb wcscmp wcscpy wcslen wctomb wmemcmp wmemcpy wmemmove]) AC_CHECK_FUNCS([_ctime64_s _fseeki64]) AC_CHECK_FUNCS([_get_timezone _localtime64_s _mkgmtime64]) # detects cygwin-1.7, as opposed to older versions AC_CHECK_FUNCS([cygwin_conv_path]) # There are several variants of readdir_r around; we only # accept the POSIX-compliant version. AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[DIR *dir; struct dirent e, *r; return(readdir_r(dir, &e, &r));]])], [AC_DEFINE(HAVE_READDIR_R,1,[Define to 1 if you have a POSIX compatible readdir_r])] ) # FreeBSD's nl_langinfo supports an option to specify whether the # current locale uses month/day or day/month ordering. It makes the # output a little prettier... AC_CHECK_DECL([D_MD_ORDER], [AC_DEFINE(HAVE_D_MD_ORDER, 1, [Define to 1 if nl_langinfo supports D_MD_ORDER])], [], [#if HAVE_LANGINFO_H #include #endif ]) # Check for dirent.d_namlen field explicitly # (This is a bit more straightforward than, if not quite as portable as, # the recipe given by the autoconf maintainers.) AC_CHECK_MEMBER(struct dirent.d_namlen,,, [#if HAVE_DIRENT_H #include #endif ]) # Check for Extended Attributes support AC_ARG_ENABLE([xattr], AS_HELP_STRING([--disable-xattr], - [Enable Extended Attributes support (default: check)])) + [Disable Extended Attributes support (default: check)])) if test "x$enable_xattr" != "xno"; then AC_CHECK_HEADERS([attr/xattr.h]) AC_CHECK_HEADERS([sys/xattr.h sys/ea.h]) AC_SEARCH_LIBS([setxattr], [attr]) AC_CHECK_FUNCS([extattr_get_file extattr_list_file]) AC_CHECK_FUNCS([extattr_set_fd extattr_set_file]) AC_CHECK_FUNCS([fgetxattr flistxattr fsetxattr getxattr]) AC_CHECK_FUNCS([lgetxattr listxattr llistxattr lsetxattr]) AC_CHECK_FUNCS([fgetea flistea fsetea getea]) AC_CHECK_FUNCS([lgetea listea llistea lsetea]) AC_CHECK_DECLS([EXTATTR_NAMESPACE_USER], [], [], [#include #include ]) fi # Check for ACL support # # The ACL support in libarchive is written against the POSIX1e draft, # which was never officially approved and varies quite a bit across # platforms. Worse, some systems have completely non-POSIX acl functions, # which makes the following checks rather more complex than I would like. # AC_ARG_ENABLE([acl], AS_HELP_STRING([--disable-acl], - [Enable ACL support (default: check)])) + [Disable ACL support (default: check)])) if test "x$enable_acl" != "xno"; then + AC_CHECK_HEADERS([acl/libacl.h]) AC_CHECK_HEADERS([sys/acl.h]) AC_CHECK_LIB([acl],[acl_get_file]) AC_CHECK_FUNCS([acl_create_entry acl_init acl_set_fd acl_set_fd_np acl_set_file]) AC_CHECK_TYPES(acl_permset_t,,, [#if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_ACL_H #include #endif ]) # The "acl_get_perm()" function was omitted from the POSIX draft. # (It's a pretty obvious oversight; otherwise, there's no way to # test for specific permissions in a permset.) Linux uses the obvious # name, FreeBSD adds _np to mark it as "non-Posix extension." # Test for both as a double-check that we really have POSIX-style ACL support. AC_CHECK_FUNCS(acl_get_perm_np acl_get_perm acl_get_link acl_get_link_np,,, [#if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_ACL_H #include #endif ]) # MacOS has an acl.h that isn't POSIX. It can be detected by # checking for ACL_USER AC_CHECK_DECL([ACL_USER], [AC_DEFINE(HAVE_ACL_USER, 1, [True for systems with POSIX ACL support])], [], [#include ]) fi # Additional requirements AC_SYS_LARGEFILE dnl NOTE: Crypto checks must run last. AC_DEFUN([CRYPTO_CHECK], [ if test "$found_$1" != yes; then saved_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I. -I$srcdir -I$srcdir/libarchive" touch "check_crypto_md.h" AC_MSG_CHECKING([support for ARCHIVE_CRYPTO_$1_$2]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #define ARCHIVE_$1_COMPILE_TEST #define ARCHIVE_CRYPTO_$1_$2 #define PLATFORM_CONFIG_H "check_crypto_md.h" $(cat "$srcdir/libarchive/archive_digest.c") int main(int argc, char **argv) { archive_$3_ctx ctx; archive_$3_init(&ctx); archive_$3_update(&ctx, *argv, argc); archive_$3_final(&ctx, NULL); return 0; } ])], [ AC_MSG_RESULT([yes]) found_$1=yes found_$2=yes AC_DEFINE(ARCHIVE_CRYPTO_$1_$2, 1, [ $1 via ARCHIVE_CRYPTO_$1_$2 supported.]) ], [ AC_MSG_RESULT([no])]) CPPFLAGS="$saved_CPPFLAGS" rm "check_crypto_md.h" fi ]) AC_DEFUN([CRYPTO_CHECK_WIN], [ if test "$found_$1" != yes; then AC_MSG_CHECKING([support for ARCHIVE_CRYPTO_$1_WIN]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #define ARCHIVE_$1_COMPILE_TEST #include #include int main(int argc, char **argv) { (void)argc; (void)argv; return ($2); } ])], [ AC_MSG_RESULT([yes]) found_$1=yes found_WIN=yes AC_DEFINE(ARCHIVE_CRYPTO_$1_WIN, 1, [ $1 via ARCHIVE_CRYPTO_$1_WIN supported.]) ], [ AC_MSG_RESULT([no])]) fi ]) case "$host_os" in *mingw* | *cygwin*) ;; *) CRYPTO_CHECK(MD5, LIBC, md5) CRYPTO_CHECK(MD5, LIBSYSTEM, md5) CRYPTO_CHECK(RMD160, LIBC, rmd160) CRYPTO_CHECK(SHA1, LIBC, sha1) CRYPTO_CHECK(SHA1, LIBSYSTEM, sha1) CRYPTO_CHECK(SHA256, LIBC, sha256) CRYPTO_CHECK(SHA256, LIBC2, sha256) CRYPTO_CHECK(SHA256, LIBC3, sha256) CRYPTO_CHECK(SHA256, LIBSYSTEM, sha256) CRYPTO_CHECK(SHA384, LIBC, sha384) CRYPTO_CHECK(SHA384, LIBC2, sha384) CRYPTO_CHECK(SHA384, LIBC3, sha384) CRYPTO_CHECK(SHA384, LIBSYSTEM, sha384) CRYPTO_CHECK(SHA512, LIBC, sha512) CRYPTO_CHECK(SHA512, LIBC2, sha512) CRYPTO_CHECK(SHA512, LIBC3, sha512) CRYPTO_CHECK(SHA512, LIBSYSTEM, sha512) ;; esac if test "x$with_nettle" != "xno"; then AC_CHECK_HEADERS([nettle/md5.h nettle/ripemd160.h nettle/sha.h]) AC_CHECK_HEADERS([nettle/pbkdf2.h nettle/aes.h nettle/hmac.h]) saved_LIBS=$LIBS AC_CHECK_LIB(nettle,nettle_sha1_init) CRYPTO_CHECK(MD5, NETTLE, md5) CRYPTO_CHECK(RMD160, NETTLE, rmd160) CRYPTO_CHECK(SHA1, NETTLE, sha1) CRYPTO_CHECK(SHA256, NETTLE, sha256) CRYPTO_CHECK(SHA384, NETTLE, sha384) CRYPTO_CHECK(SHA512, NETTLE, sha512) if test "x$found_NETTLE" != "xyes"; then LIBS=$saved_LIBS fi fi if test "x$with_openssl" != "xno"; then AC_CHECK_HEADERS([openssl/evp.h]) saved_LIBS=$LIBS case "$host_os" in *mingw* | *cygwin*) case "$host_cpu" in x86_64) AC_CHECK_LIB(eay64,OPENSSL_config) if test "x$ac_cv_lib_eay64_main" != "xyes"; then AC_CHECK_LIB(eay32,OPENSSL_config) fi ;; *) AC_CHECK_LIB(eay32,OPENSSL_config) ;; esac ;; *) AC_CHECK_LIB(crypto,OPENSSL_config) ;; esac CRYPTO_CHECK(MD5, OPENSSL, md5) CRYPTO_CHECK(RMD160, OPENSSL, rmd160) CRYPTO_CHECK(SHA1, OPENSSL, sha1) CRYPTO_CHECK(SHA256, OPENSSL, sha256) CRYPTO_CHECK(SHA384, OPENSSL, sha384) CRYPTO_CHECK(SHA512, OPENSSL, sha512) if test "x$found_OPENSSL" != "xyes"; then LIBS=$saved_LIBS else AC_CHECK_FUNCS([PKCS5_PBKDF2_HMAC_SHA1]) fi fi # Probe libmd AFTER OpenSSL/libcrypto. # The two are incompatible and OpenSSL is more complete. AC_CHECK_HEADERS([md5.h ripemd.h sha.h sha256.h sha512.h]) saved_LIBS=$LIBS AC_CHECK_LIB(md,MD5Init) CRYPTO_CHECK(MD5, LIBMD, md5) CRYPTO_CHECK(RMD160, LIBMD, rmd160) CRYPTO_CHECK(SHA1, LIBMD, sha1) CRYPTO_CHECK(SHA256, LIBMD, sha256) CRYPTO_CHECK(SHA512, LIBMD, sha512) if test "x$found_LIBMD" != "xyes"; then LIBS=$saved_LIBS fi case "$host_os" in *mingw* | *cygwin*) CRYPTO_CHECK_WIN(MD5, CALG_MD5) CRYPTO_CHECK_WIN(SHA1, CALG_SHA1) CRYPTO_CHECK_WIN(SHA256, CALG_SHA_256) CRYPTO_CHECK_WIN(SHA384, CALG_SHA_384) CRYPTO_CHECK_WIN(SHA512, CALG_SHA_512) ;; esac # Ensure test directories are present if building out-of-tree AC_CONFIG_COMMANDS([mkdirs], [mkdir -p libarchive/test tar/test cat/test cpio/test]) AC_OUTPUT Index: vendor/libarchive/dist/cpio/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/cpio/test/CMakeLists.txt (revision 302002) +++ vendor/libarchive/dist/cpio/test/CMakeLists.txt (revision 302003) @@ -1,97 +1,98 @@ ############################################ # # How to build bsdcpio_test # ############################################ IF(ENABLE_CPIO AND ENABLE_TEST) SET(bsdcpio_test_SOURCES ../cmdline.c ../../libarchive_fe/err.c ../../test_utils/test_utils.c main.c test.h test_0.c test_basic.c test_cmdline.c test_extract_cpio_Z test_extract_cpio_bz2 test_extract_cpio_grz test_extract_cpio_gz test_extract_cpio_lrz test_extract_cpio_lz test_extract_cpio_lz4 test_extract_cpio_lzma test_extract_cpio_lzo test_extract_cpio_xz test_format_newc.c test_gcpio_compat.c + test_missing_file.c test_option_0.c test_option_B_upper.c test_option_C_upper.c test_option_J_upper.c test_option_L_upper.c test_option_Z_upper.c test_option_a.c test_option_b64encode.c test_option_c.c test_option_d.c test_option_f.c test_option_grzip.c test_option_help.c test_option_l.c test_option_lrzip.c test_option_lz4.c test_option_lzma.c test_option_lzop.c test_option_m.c test_option_passphrase.c test_option_t.c test_option_u.c test_option_uuencode.c test_option_version.c test_option_xz.c test_option_y.c test_option_z.c test_owner_parse.c test_passthrough_dotdot.c test_passthrough_reverse.c ) # # Register target # ADD_EXECUTABLE(bsdcpio_test ${bsdcpio_test_SOURCES}) SET_PROPERTY(TARGET bsdcpio_test PROPERTY COMPILE_DEFINITIONS LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${bsdcpio_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 bsdcpio_${_testname} COMMAND bsdcpio_test -vv -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) # Experimental new test handling ADD_CUSTOM_TARGET(run_bsdcpio_test COMMAND bsdcpio_test -p $ -r ${CMAKE_CURRENT_SOURCE_DIR}) ADD_DEPENDENCIES(run_bsdcpio_test bsdcpio) ADD_DEPENDENCIES(run_all_tests run_bsdcpio_test) ENDIF(ENABLE_CPIO AND ENABLE_TEST) Index: vendor/libarchive/dist/cpio/test/main.c =================================================================== --- vendor/libarchive/dist/cpio/test/main.c (revision 302002) +++ vendor/libarchive/dist/cpio/test/main.c (revision 302003) @@ -1,2970 +1,2993 @@ /* * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include "test_utils.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include /* * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. * TODO: Move this into a separate configuration header, have all test * suites share one copy of this file. */ __FBSDID("$FreeBSD: src/usr.bin/cpio/test/main.c,v 1.3 2008/08/24 04:58:22 kientzle Exp $"); #define KNOWNREF "test_option_f.cpio.uu" #define ENVBASE "BSDCPIO" /* Prefix for environment variables. */ #define PROGRAM "bsdcpio" /* Name of program being tested. */ #define PROGRAM_ALIAS "cpio" /* Generic alias for program */ #undef LIBRARY /* Not testing a library. */ #undef EXTRA_DUMP /* How to dump extra data */ #undef EXTRA_ERRNO /* How to dump errno */ /* How to generate extra version info. */ #define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, flags); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalide sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not leagal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %d bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n", fn); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %lld.%09lld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)pathname; /* UNUSED */ (void)contents; /* UNUSED */ assertion_count(file, line); /* Windows sort-of has real symlinks, but they're only usable * by privileged users and are crippled even then, so there's * really not much point in bothering with this. */ return (0); #else char buff[300]; struct stat st; ssize_t linklen; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff)); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents) { if (is_symlink(file, line, path, contents)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) return (1); #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); return (0); } } close(fd); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Create a symlink and report any failures. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto) { #if defined(_WIN32) && !defined(__CYGWIN__) int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Set nodump, report failures. */ int assertion_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 -d -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int canNodump(void) { const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int canNodump(void) { const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); close(fd); if (flags & EXT2_NODUMP_FL) return (1); return (0); } #else int canNodump() { return (0); } #endif /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { char workdir[1024]; char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { - char tried[512] = { '\0' }; - char buff[128]; - char *pwd, *p; + size_t tried_size, buff_size; + char *buff, *tried, *pwd = NULL, *p = NULL; +#ifdef PATH_MAX + buff_size = PATH_MAX; +#else + buff_size = 8192; +#endif + buff = calloc(buff_size, 1); + if (buff == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + + /* Allocate a buffer to hold the various directories we checked. */ + tried_size = buff_size * 2; + tried = calloc(tried_size, 1); + if (tried == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; - snprintf(buff, sizeof(buff), "%s", d); + snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ - snprintf(buff, sizeof(buff), "%s", pwd); + snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd); + snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); + snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM_ALIAS); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, sizeof(buff), "%s", pwd + 8); + snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd + 8); + snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); - return strdup(buff); + free(tried); + + /* Copy result into a fresh buffer to reduce memory usage. */ + p = strdup(buff); + free(buff); + return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: vendor/libarchive/dist/cpio/test/test_missing_file.c =================================================================== --- vendor/libarchive/dist/cpio/test/test_missing_file.c (nonexistent) +++ vendor/libarchive/dist/cpio/test/test_missing_file.c (revision 302003) @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2016 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_missing_file) +{ + int r; + + assertMakeFile("file1", 0644, "file1"); + assertMakeFile("file2", 0644, "file2"); + + assertMakeFile("filelist1", 0644, "file1\nfile2\n"); + r = systemf("%s -o stdout1 2>stderr1", testprog); + assertEqualInt(r, 0); + assertTextFileContents("1 block\n", "stderr1"); + + assertMakeFile("filelist2", 0644, "file1\nfile2\nfile3\n"); + r = systemf("%s -o stdout2 2>stderr2", testprog); + assert(r != 0); + + assertMakeFile("filelist3", 0644, ""); + r = systemf("%s -o stdout3 2>stderr3", testprog); + assertEqualInt(r, 0); + assertTextFileContents("1 block\n", "stderr3"); + + assertMakeFile("filelist4", 0644, "file3\n"); + r = systemf("%s -o stdout4 2>stderr4", testprog); + assert(r != 0); +} Property changes on: vendor/libarchive/dist/cpio/test/test_missing_file.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/archive_entry_xattr.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_entry_xattr.c (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_entry_xattr.c (revision 302003) @@ -1,161 +1,156 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_xattr.c 201096 2009-12-28 02:41:27Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* for Linux file flags */ #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_entry_private.h" /* * extended attribute handling */ void archive_entry_xattr_clear(struct archive_entry *entry) { struct ae_xattr *xp; while (entry->xattr_head != NULL) { xp = entry->xattr_head->next; free(entry->xattr_head->name); free(entry->xattr_head->value); free(entry->xattr_head); entry->xattr_head = xp; } entry->xattr_head = NULL; } void archive_entry_xattr_add_entry(struct archive_entry *entry, const char *name, const void *value, size_t size) { struct ae_xattr *xp; - for (xp = entry->xattr_head; xp != NULL; xp = xp->next) - ; - if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL) - /* XXX Error XXX */ - return; + __archive_errx(1, "Out of memory"); if ((xp->name = strdup(name)) == NULL) - /* XXX Error XXX */ - return; + __archive_errx(1, "Out of memory"); if ((xp->value = malloc(size)) != NULL) { memcpy(xp->value, value, size); xp->size = size; } else xp->size = 0; xp->next = entry->xattr_head; entry->xattr_head = xp; } /* * returns number of the extended attribute entries */ int archive_entry_xattr_count(struct archive_entry *entry) { struct ae_xattr *xp; int count = 0; for (xp = entry->xattr_head; xp != NULL; xp = xp->next) count++; return count; } int archive_entry_xattr_reset(struct archive_entry * entry) { entry->xattr_p = entry->xattr_head; return archive_entry_xattr_count(entry); } int archive_entry_xattr_next(struct archive_entry * entry, const char **name, const void **value, size_t *size) { if (entry->xattr_p) { *name = entry->xattr_p->name; *value = entry->xattr_p->value; *size = entry->xattr_p->size; entry->xattr_p = entry->xattr_p->next; return (ARCHIVE_OK); } else { *name = NULL; *value = NULL; *size = (size_t)0; return (ARCHIVE_WARN); } } /* * end of xattr handling */ Index: vendor/libarchive/dist/libarchive/archive_read_support_format_mtree.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_mtree.c (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_mtree.c (revision 302003) @@ -1,1993 +1,1993 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #include /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" #include "archive_pack_dev.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #define MTREE_HAS_DEVICE 0x0001 #define MTREE_HAS_FFLAGS 0x0002 #define MTREE_HAS_GID 0x0004 #define MTREE_HAS_GNAME 0x0008 #define MTREE_HAS_MTIME 0x0010 #define MTREE_HAS_NLINK 0x0020 #define MTREE_HAS_PERM 0x0040 #define MTREE_HAS_SIZE 0x0080 #define MTREE_HAS_TYPE 0x0100 #define MTREE_HAS_UID 0x0200 #define MTREE_HAS_UNAME 0x0400 #define MTREE_HAS_OPTIONAL 0x0800 #define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ struct mtree_option { struct mtree_option *next; char *value; }; struct mtree_entry { struct mtree_entry *next; struct mtree_option *options; char *name; char full; char used; }; struct mtree { struct archive_string line; size_t buffsize; char *buff; int64_t offset; int fd; int archive_format; const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; struct archive_string current_dir; struct archive_string contents_name; struct archive_entry_linkresolver *resolver; int64_t cur_size; char checkfs; }; static int bid_keycmp(const char *, const char *, ssize_t); static int cleanup(struct archive_read *); static int detect_form(struct archive_read *, int *); static int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static void parse_escapes(char *, struct mtree_entry *); static int parse_line(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static int parse_keyword(struct archive_read *, struct mtree *, struct archive_entry *, struct mtree_option *, int *); static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); static int skip(struct archive_read *a); static int read_header(struct archive_read *, struct archive_entry *); static int64_t mtree_atol10(char **); static int64_t mtree_atol8(char **); static int64_t mtree_atol(char **); /* * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them * here. TODO: Move this to configure time, but be careful * about cross-compile environments. */ static int64_t get_time_t_max(void) { #if defined(TIME_T_MAX) return TIME_T_MAX; #else /* ISO C allows time_t to be a floating-point type, but POSIX requires an integer type. The following should work on any system that follows the POSIX conventions. */ if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (~(time_t)0); } else { /* Time_t is signed. */ /* Assume it's the same as int64_t or int32_t */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MAX; } else { return (time_t)INT32_MAX; } } #endif } static int64_t get_time_t_min(void) { #if defined(TIME_T_MIN) return TIME_T_MIN; #else if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (time_t)0; } else { /* Time_t is signed. */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MIN; } else { return (time_t)INT32_MIN; } } #endif } static int archive_read_format_mtree_options(struct archive_read *a, const char *key, const char *val) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (strcmp(key, "checkfs") == 0) { /* Allows to read information missing from the mtree from the file system */ if (val == NULL || val[0] == 0) { mtree->checkfs = 0; } else { mtree->checkfs = 1; } return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static void free_options(struct mtree_option *head) { struct mtree_option *next; for (; head != NULL; head = next) { next = head->next; free(head->value); free(head); } } int archive_read_support_format_mtree(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct mtree *mtree; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_mtree"); mtree = (struct mtree *)malloc(sizeof(*mtree)); if (mtree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } memset(mtree, 0, sizeof(*mtree)); mtree->fd = -1; r = __archive_read_register_format(a, mtree, "mtree", mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(mtree); return (ARCHIVE_OK); } static int cleanup(struct archive_read *a) { struct mtree *mtree; struct mtree_entry *p, *q; mtree = (struct mtree *)(a->format->data); p = mtree->entries; while (p != NULL) { q = p->next; free(p->name); free_options(p->options); free(p); p = q; } archive_string_free(&mtree->line); archive_string_free(&mtree->current_dir); archive_string_free(&mtree->contents_name); archive_entry_linkresolver_free(mtree->resolver); free(mtree->buff); free(mtree); (a->format->data) = NULL; return (ARCHIVE_OK); } static ssize_t get_line_size(const char *b, ssize_t avail, ssize_t *nlsize) { ssize_t len; len = 0; while (len < avail) { switch (*b) { case '\0':/* Non-ascii character or control character. */ if (nlsize != NULL) *nlsize = 0; return (-1); case '\r': if (avail-len > 1 && b[1] == '\n') { if (nlsize != NULL) *nlsize = 2; return (len+2); } /* FALL THROUGH */ case '\n': if (nlsize != NULL) *nlsize = 1; return (len+1); default: b++; len++; break; } } if (nlsize != NULL) *nlsize = 0; return (avail); } static ssize_t next_line(struct archive_read *a, const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl) { ssize_t len; int quit; quit = 0; if (*avail == 0) { *nl = 0; len = 0; } else len = get_line_size(*b, *avail, nl); /* * Read bytes more while it does not reach the end of line. */ while (*nl == 0 && len == *avail && !quit) { ssize_t diff = *ravail - *avail; size_t nbytes_req = (*ravail+1023) & ~1023U; ssize_t tested; /* Increase reading bytes if it is not enough to at least * new two lines. */ if (nbytes_req < (size_t)*ravail + 160) nbytes_req <<= 1; *b = __archive_read_ahead(a, nbytes_req, avail); if (*b == NULL) { if (*ravail >= *avail) return (0); /* Reading bytes reaches the end of file. */ *b = __archive_read_ahead(a, *avail, avail); quit = 1; } *ravail = *avail; *b += diff; *avail -= diff; tested = len;/* Skip some bytes we already determinated. */ len = get_line_size(*b, *avail, nl); if (len >= 0) len += tested; } return (len); } /* * Compare characters with a mtree keyword. * Returns the length of a mtree keyword if matched. * Returns 0 if not matched. */ static int bid_keycmp(const char *p, const char *key, ssize_t len) { int match_len = 0; while (len > 0 && *p && *key) { if (*p == *key) { --len; ++p; ++key; ++match_len; continue; } return (0);/* Not match */ } if (*key != '\0') return (0);/* Not match */ /* A following character should be specified characters */ if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' || p[0] == '\n' || p[0] == '\r' || (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))) return (match_len); return (0);/* Not match */ } /* * Test whether the characters 'p' has is mtree keyword. * Returns the length of a detected keyword. * Returns 0 if any keywords were not found. */ static int bid_keyword(const char *p, ssize_t len) { static const char *keys_c[] = { "content", "contents", "cksum", NULL }; static const char *keys_df[] = { "device", "flags", NULL }; static const char *keys_g[] = { "gid", "gname", NULL }; static const char *keys_il[] = { "ignore", "inode", "link", NULL }; static const char *keys_m[] = { "md5", "md5digest", "mode", NULL }; static const char *keys_no[] = { "nlink", "nochange", "optional", NULL }; static const char *keys_r[] = { "resdevice", "rmd160", "rmd160digest", NULL }; static const char *keys_s[] = { "sha1", "sha1digest", "sha256", "sha256digest", "sha384", "sha384digest", "sha512", "sha512digest", "size", NULL }; static const char *keys_t[] = { "tags", "time", "type", NULL }; static const char *keys_u[] = { "uid", "uname", NULL }; const char **keys; int i; switch (*p) { case 'c': keys = keys_c; break; case 'd': case 'f': keys = keys_df; break; case 'g': keys = keys_g; break; case 'i': case 'l': keys = keys_il; break; case 'm': keys = keys_m; break; case 'n': case 'o': keys = keys_no; break; case 'r': keys = keys_r; break; case 's': keys = keys_s; break; case 't': keys = keys_t; break; case 'u': keys = keys_u; break; default: return (0);/* Unknown key */ } for (i = 0; keys[i] != NULL; i++) { int l = bid_keycmp(p, keys[i], len); if (l > 0) return (l); } return (0);/* Unknown key */ } /* * Test whether there is a set of mtree keywords. * Returns the number of keyword. * Returns -1 if we got incorrect sequence. * This function expects a set of "keyword=value". * When "unset" is specified, expects a set of "keyword". */ static int bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path) { int l; int keycnt = 0; while (len > 0 && *p) { int blank = 0; /* Test whether there are blank characters in the line. */ while (len >0 && (*p == ' ' || *p == '\t')) { ++p; --len; blank = 1; } if (*p == '\n' || *p == '\r') break; if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')) break; if (!blank && !last_is_path) /* No blank character. */ return (-1); if (last_is_path && len == 0) return (keycnt); if (unset) { l = bid_keycmp(p, "all", len); if (l > 0) return (1); } /* Test whether there is a correct key in the line. */ l = bid_keyword(p, len); if (l == 0) return (-1);/* Unknown keyword was found. */ p += l; len -= l; keycnt++; /* Skip value */ if (*p == '=') { int value = 0; ++p; --len; while (len > 0 && *p != ' ' && *p != '\t') { ++p; --len; value = 1; } /* A keyword should have a its value unless * "/unset" operation. */ if (!unset && value == 0) return (-1); } } return (keycnt); } static int bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path) { int f = 0; static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ /* !"$%&'()*+,-./ EXCLUSION:( )(#) */ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ /* 0123456789:;<>? EXCLUSION:(=) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ /* PQRSTUVWXYZ[\]^_ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; ssize_t ll; const char *pp = p; const char * const pp_end = pp + len; *last_is_path = 0; /* * Skip the path-name which is quoted. */ for (;pp < pp_end; ++pp) { if (!safe_char[*(const unsigned char *)pp]) { if (*pp != ' ' && *pp != '\t' && *pp != '\r' && *pp != '\n') f = 0; break; } f = 1; } ll = pp_end - pp; /* If a path-name was not found at the first, try to check * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates, * which places the path-name at the last. */ if (f == 0) { const char *pb = p + len - nl; int name_len = 0; int slash; /* The form D accepts only a single line for an entry. */ if (pb-2 >= p && pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) return (-1); if (pb-1 >= p && pb[-1] == '\\') return (-1); slash = 0; while (p <= --pb && *pb != ' ' && *pb != '\t') { if (!safe_char[*(const unsigned char *)pb]) return (-1); name_len++; /* The pathname should have a slash in this * format. */ if (*pb == '/') slash = 1; } if (name_len == 0 || slash == 0) return (-1); /* If '/' is placed at the first in this field, this is not * a valid filename. */ if (pb[1] == '/') return (-1); ll = len - nl - name_len; pp = p; *last_is_path = 1; } return (bid_keyword_list(pp, ll, 0, *last_is_path)); } #define MAX_BID_ENTRY 3 static int mtree_bid(struct archive_read *a, int best_bid) { const char *signature = "#mtree"; const char *p; (void)best_bid; /* UNUSED */ /* Now let's look at the actual header and see if it matches. */ p = __archive_read_ahead(a, strlen(signature), NULL); if (p == NULL) return (-1); if (memcmp(p, signature, strlen(signature)) == 0) return (8 * (int)strlen(signature)); /* * There is not a mtree signature. Let's try to detect mtree format. */ return (detect_form(a, NULL)); } static int detect_form(struct archive_read *a, int *is_form_d) { const char *p; ssize_t avail, ravail; ssize_t detected_bytes = 0, len, nl; int entry_cnt = 0, multiline = 0; int form_D = 0;/* The archive is generated by `NetBSD mtree -D' * (In this source we call it `form D') . */ if (is_form_d != NULL) *is_form_d = 0; p = __archive_read_ahead(a, 1, &avail); if (p == NULL) return (-1); ravail = avail; for (;;) { len = next_line(a, &p, &avail, &ravail, &nl); /* The terminal character of the line should be * a new line character, '\r\n' or '\n'. */ if (len <= 0 || nl == 0) break; if (!multiline) { /* Leading whitespace is never significant, * ignore it. */ while (len > 0 && (*p == ' ' || *p == '\t')) { ++p; --avail; --len; } /* Skip comment or empty line. */ if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') { p += len; avail -= len; continue; } } else { /* A continuance line; the terminal * character of previous line was '\' character. */ if (bid_keyword_list(p, len, 0, 0) <= 0) break; if (multiline == 1) detected_bytes += len; if (p[len-nl-1] != '\\') { if (multiline == 1 && ++entry_cnt >= MAX_BID_ENTRY) break; multiline = 0; } p += len; avail -= len; continue; } if (p[0] != '/') { int last_is_path, keywords; keywords = bid_entry(p, len, nl, &last_is_path); if (keywords >= 0) { detected_bytes += len; if (form_D == 0) { if (last_is_path) form_D = 1; else if (keywords > 0) /* This line is not `form D'. */ form_D = -1; } else if (form_D == 1) { if (!last_is_path && keywords > 0) /* This this is not `form D' * and We cannot accept mixed * format. */ break; } if (!last_is_path && p[len-nl-1] == '\\') /* This line continues. */ multiline = 1; else { /* We've got plenty of correct lines * to assume that this file is a mtree * format. */ if (++entry_cnt >= MAX_BID_ENTRY) break; } } else break; } else if (strncmp(p, "/set", 4) == 0) { if (bid_keyword_list(p+4, len-4, 0, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else if (strncmp(p, "/unset", 6) == 0) { if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else break; /* Test next line. */ p += len; avail -= len; } if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) { if (is_form_d != NULL) { if (form_D == 1) *is_form_d = 1; } return (32); } return (0); } /* * The extended mtree format permits multiple lines specifying * attributes for each file. For those entries, only the last line * is actually used. Practically speaking, that means we have * to read the entire mtree file into memory up front. * * The parsing is done in two steps. First, it is decided if a line * changes the global defaults and if it is, processed accordingly. * Otherwise, the options of the line are merged with the current * global options. */ static int add_option(struct archive_read *a, struct mtree_option **global, const char *value, size_t len) { struct mtree_option *opt; if ((opt = malloc(sizeof(*opt))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } if ((opt->value = malloc(len + 1)) == NULL) { free(opt); archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(opt->value, value, len); opt->value[len] = '\0'; opt->next = *global; *global = opt; return (ARCHIVE_OK); } static void remove_option(struct mtree_option **global, const char *value, size_t len) { struct mtree_option *iter, *last; last = NULL; for (iter = *global; iter != NULL; last = iter, iter = iter->next) { if (strncmp(iter->value, value, len) == 0 && (iter->value[len] == '\0' || iter->value[len] == '=')) break; } if (iter == NULL) return; if (last == NULL) *global = iter->next; else last->next = iter->next; free(iter->value); free(iter); } static int process_global_set(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next, *eq; size_t len; int r; line += 4; for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq > next) len = next - line; else len = eq - line; remove_option(global, line, len); r = add_option(a, global, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int process_global_unset(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next; size_t len; line += 6; if (strchr(line, '=') != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "/unset shall not contain `='"); return ARCHIVE_FATAL; } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; len = strcspn(line, " \t\r\n"); if (len == 3 && strncmp(line, "all", 3) == 0) { free_options(*global); *global = NULL; } else { remove_option(global, line, len); } line += len; } } static int process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_option **global, const char *line, ssize_t line_len, struct mtree_entry **last_entry, int is_form_d) { struct mtree_entry *entry; struct mtree_option *iter; const char *next, *eq, *name, *end; size_t name_len, len; int r, i; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } entry->next = NULL; entry->options = NULL; entry->name = NULL; entry->used = 0; entry->full = 0; /* Add this entry to list. */ if (*last_entry == NULL) mtree->entries = entry; else (*last_entry)->next = entry; *last_entry = entry; if (is_form_d) { /* Filename is last item on line. */ /* Adjust line_len to trim trailing whitespace */ while (line_len > 0) { char last_character = line[line_len - 1]; if (last_character == '\r' || last_character == '\n' || last_character == '\t' || last_character == ' ') { line_len--; } else { break; } } /* Name starts after the last whitespace separator */ name = line; for (i = 0; i < line_len; i++) { if (line[i] == '\r' || line[i] == '\n' || line[i] == '\t' || line[i] == ' ') { name = line + i + 1; } } name_len = line + line_len - name; end = name; } else { /* Filename is first item on line */ name_len = strcspn(line, " \t\r\n"); name = line; line += name_len; end = line + line_len; } /* name/name_len is the name within the line. */ /* line..end brackets the entire line except the name */ if ((entry->name = malloc(name_len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(entry->name, name, name_len); entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); for (iter = *global; iter != NULL; iter = iter->next) { r = add_option(a, &entry->options, iter->value, strlen(iter->value)); if (r != ARCHIVE_OK) return (r); } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); if (next >= end) return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq == NULL || eq > next) len = next - line; else len = eq - line; remove_option(&entry->options, line, len); r = add_option(a, &entry->options, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; uintmax_t counter; char *p; struct mtree_option *global; struct mtree_entry *last_entry; int r, is_form_d; mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; global = NULL; last_entry = NULL; (void)detect_form(a, &is_form_d); for (counter = 1; ; ++counter) { len = readline(a, mtree, &p, 65536); if (len == 0) { mtree->this_entry = mtree->entries; free_options(global); return (ARCHIVE_OK); } if (len < 0) { free_options(global); return ((int)len); } /* Leading whitespace is never significant, ignore it. */ while (*p == ' ' || *p == '\t') { ++p; --len; } /* Skip content lines and blank lines. */ if (*p == '#') continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); } else if (strncmp(p, "/set", 4) == 0) { if (p[4] != ' ' && p[4] != '\t') break; r = process_global_set(a, &global, p); } else if (strncmp(p, "/unset", 6) == 0) { if (p[6] != ' ' && p[6] != '\t') break; r = process_global_unset(a, &global, p); } else break; if (r != ARCHIVE_OK) { free_options(global); return r; } } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't parse line %ju", counter); free_options(global); return (ARCHIVE_FATAL); } /* * Read in the entire mtree file into memory on the first request. * Then use the next unused file to satisfy each header request. */ static int read_header(struct archive_read *a, struct archive_entry *entry) { struct mtree *mtree; char *p; int r, use_next; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } if (mtree->entries == NULL) { mtree->resolver = archive_entry_linkresolver_new(); if (mtree->resolver == NULL) return ARCHIVE_FATAL; archive_entry_linkresolver_set_strategy(mtree->resolver, ARCHIVE_FORMAT_MTREE); r = read_mtree(a, mtree); if (r != ARCHIVE_OK) return (r); } a->archive.archive_format = mtree->archive_format; a->archive.archive_format_name = mtree->archive_format_name; for (;;) { if (mtree->this_entry == NULL) return (ARCHIVE_EOF); if (strcmp(mtree->this_entry->name, "..") == 0) { mtree->this_entry->used = 1; if (archive_strlen(&mtree->current_dir) > 0) { /* Roll back current path. */ p = mtree->current_dir.s + mtree->current_dir.length - 1; while (p >= mtree->current_dir.s && *p != '/') --p; if (p >= mtree->current_dir.s) --p; mtree->current_dir.length = p - mtree->current_dir.s + 1; } } if (!mtree->this_entry->used) { use_next = 0; r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); if (use_next == 0) return (r); } mtree->this_entry = mtree->this_entry->next; } } /* * A single file can have multiple lines contribute specifications. * Parse as many lines as necessary, then pull additional information * from a backing file on disk as necessary. */ static int parse_file(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mentry, int *use_next) { const char *path; struct stat st_storage, *st; struct mtree_entry *mp; struct archive_entry *sparse_entry; int r = ARCHIVE_OK, r1, parsed_kws; mentry->used = 1; /* Initialize reasonable defaults. */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_size(entry, 0); archive_string_empty(&mtree->contents_name); /* Parse options from this line. */ parsed_kws = 0; r = parse_line(a, entry, mtree, mentry, &parsed_kws); if (mentry->full) { archive_entry_copy_pathname(entry, mentry->name); /* * "Full" entries are allowed to have multiple lines * and those lines aren't required to be adjacent. We * don't support multiple lines for "relative" entries * nor do we make any attempt to merge data from * separate "relative" and "full" entries. (Merging * "relative" and "full" entries would require dealing * with pathname canonicalization, which is a very * tricky subject.) */ for (mp = mentry->next; mp != NULL; mp = mp->next) { if (mp->full && !mp->used && strcmp(mentry->name, mp->name) == 0) { /* Later lines override earlier ones. */ mp->used = 1; r1 = parse_line(a, entry, mtree, mp, &parsed_kws); if (r1 < r) r = r1; } } } else { /* * Relative entries require us to construct * the full path and possibly update the * current directory. */ size_t n = archive_strlen(&mtree->current_dir); if (n > 0) archive_strcat(&mtree->current_dir, "/"); archive_strcat(&mtree->current_dir, mentry->name); archive_entry_copy_pathname(entry, mtree->current_dir.s); if (archive_entry_filetype(entry) != AE_IFDIR) mtree->current_dir.length = n; } if (mtree->checkfs) { /* * Try to open and stat the file to get the real size * and other file info. It would be nice to avoid * this here so that getting a listing of an mtree * wouldn't require opening every referenced contents * file. But then we wouldn't know the actual * contents size, so I don't see a really viable way * around this. (Also, we may want to someday pull * other unspecified info from the contents file on * disk.) */ mtree->fd = -1; if (archive_strlen(&mtree->contents_name) > 0) path = mtree->contents_name.s; else path = archive_entry_pathname(entry); if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(mtree->fd); if (mtree->fd == -1 && (errno != ENOENT || archive_strlen(&mtree->contents_name) > 0)) { archive_set_error(&a->archive, errno, "Can't open %s", path); r = ARCHIVE_WARN; } } st = &st_storage; if (mtree->fd >= 0) { if (fstat(mtree->fd, st) == -1) { archive_set_error(&a->archive, errno, "Could not fstat %s", path); r = ARCHIVE_WARN; /* If we can't stat it, don't keep it open. */ close(mtree->fd); mtree->fd = -1; st = NULL; } } else if (lstat(path, st) == -1) { st = NULL; } /* * Check for a mismatch between the type in the specification * and the type of the contents object on disk. */ if (st != NULL) { if (((st->st_mode & S_IFMT) == S_IFREG && archive_entry_filetype(entry) == AE_IFREG) #ifdef S_IFLNK ||((st->st_mode & S_IFMT) == S_IFLNK && archive_entry_filetype(entry) == AE_IFLNK) #endif #ifdef S_IFSOCK ||((st->st_mode & S_IFSOCK) == S_IFSOCK && archive_entry_filetype(entry) == AE_IFSOCK) #endif #ifdef S_IFCHR ||((st->st_mode & S_IFMT) == S_IFCHR && archive_entry_filetype(entry) == AE_IFCHR) #endif #ifdef S_IFBLK ||((st->st_mode & S_IFMT) == S_IFBLK && archive_entry_filetype(entry) == AE_IFBLK) #endif ||((st->st_mode & S_IFMT) == S_IFDIR && archive_entry_filetype(entry) == AE_IFDIR) #ifdef S_IFIFO ||((st->st_mode & S_IFMT) == S_IFIFO && archive_entry_filetype(entry) == AE_IFIFO) #endif ) { /* Types match. */ } else { /* Types don't match; bail out gracefully. */ if (mtree->fd >= 0) close(mtree->fd); mtree->fd = -1; if (parsed_kws & MTREE_HAS_OPTIONAL) { /* It's not an error for an optional * entry to not match disk. */ *use_next = 1; } else if (r == ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "mtree specification has different" " type for %s", archive_entry_pathname(entry)); r = ARCHIVE_WARN; } return (r); } } /* * If there is a contents file on disk, pick some of the * metadata from that file. For most of these, we only * set it from the contents if it wasn't already parsed * from the specification. */ if (st != NULL) { if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) && (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK)) archive_entry_set_rdev(entry, st->st_rdev); if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_gid(entry, st->st_gid); if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_uid(entry, st->st_uid); if ((parsed_kws & MTREE_HAS_MTIME) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime*1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec*1000); #else archive_entry_set_mtime(entry, st->st_mtime, 0); #endif } if ((parsed_kws & MTREE_HAS_NLINK) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_nlink(entry, st->st_nlink); if ((parsed_kws & MTREE_HAS_PERM) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_perm(entry, st->st_mode); if ((parsed_kws & MTREE_HAS_SIZE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_size(entry, st->st_size); archive_entry_set_ino(entry, st->st_ino); archive_entry_set_dev(entry, st->st_dev); archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); } else if (parsed_kws & MTREE_HAS_OPTIONAL) { /* * Couldn't open the entry, stat it or the on-disk type * didn't match. If this entry is optional, just * ignore it and read the next header entry. */ *use_next = 1; return ARCHIVE_OK; } } mtree->cur_size = archive_entry_size(entry); mtree->offset = 0; return r; } /* * Each line contains a sequence of keywords. */ static int parse_line(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws) { struct mtree_option *iter; int r = ARCHIVE_OK, r1; for (iter = mp->options; iter != NULL; iter = iter->next) { r1 = parse_keyword(a, mtree, entry, iter, parsed_kws); if (r1 < r) r = r1; } if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Missing type keyword in mtree specification"); return (ARCHIVE_WARN); } return (r); } /* * Device entries have one of the following forms: * - raw dev_t * - format,major,minor[,subdevice] * When parsing succeeded, `pdev' will contain the appropriate dev_t value. */ /* strsep() is not in C90, but strcspn() is. */ /* Taken from http://unixpapa.com/incnote/string.html */ static char * la_strsep(char **sp, const char *sep) { char *p, *s; if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL); s = *sp; p = s + strcspn(s, sep); if (*p != '\0') *p++ = '\0'; *sp = p; return(s); } static int parse_device(dev_t *pdev, struct archive *a, char *val) { #define MAX_PACK_ARGS 3 unsigned long numbers[MAX_PACK_ARGS]; char *p, *dev; int argc; pack_t *pack; dev_t result; const char *error = NULL; memset(pdev, 0, sizeof(*pdev)); if ((dev = strchr(val, ',')) != NULL) { /* * Device's major/minor are given in a specified format. * Decode and pack it accordingly. */ *dev++ = '\0'; if ((pack = pack_find(val)) == NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown format `%s'", val); return ARCHIVE_WARN; } argc = 0; while ((p = la_strsep(&dev, ",")) != NULL) { if (*p == '\0') { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Missing number"); return ARCHIVE_WARN; } - numbers[argc++] = (unsigned long)mtree_atol(&p); - if (argc > MAX_PACK_ARGS) { + if (argc >= MAX_PACK_ARGS) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Too many arguments"); return ARCHIVE_WARN; } + numbers[argc++] = (unsigned long)mtree_atol(&p); } if (argc < 2) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Not enough arguments"); return ARCHIVE_WARN; } result = (*pack)(argc, numbers, &error); if (error != NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "%s", error); return ARCHIVE_WARN; } } else { /* file system raw value. */ result = (dev_t)mtree_atol(&val); } *pdev = result; return ARCHIVE_OK; #undef MAX_PACK_ARGS } /* * Parse a single keyword and its value. */ static int parse_keyword(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws) { char *val, *key; key = opt->value; if (*key == '\0') return (ARCHIVE_OK); if (strcmp(key, "nochange") == 0) { *parsed_kws |= MTREE_HAS_NOCHANGE; return (ARCHIVE_OK); } if (strcmp(key, "optional") == 0) { *parsed_kws |= MTREE_HAS_OPTIONAL; return (ARCHIVE_OK); } if (strcmp(key, "ignore") == 0) { /* * The mtree processing is not recursive, so * recursion will only happen for explicitly listed * entries. */ return (ARCHIVE_OK); } val = strchr(key, '='); if (val == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed attribute \"%s\" (%d)", key, key[0]); return (ARCHIVE_WARN); } *val = '\0'; ++val; switch (key[0]) { case 'c': if (strcmp(key, "content") == 0 || strcmp(key, "contents") == 0) { parse_escapes(val, NULL); archive_strcpy(&mtree->contents_name, val); break; } if (strcmp(key, "cksum") == 0) break; case 'd': if (strcmp(key, "device") == 0) { /* stat(2) st_rdev field, e.g. the major/minor IDs * of a char/block special file */ int r; dev_t dev; *parsed_kws |= MTREE_HAS_DEVICE; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_rdev(entry, dev); return r; } case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); break; } case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "gname") == 0) { *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); break; } case 'i': if (strcmp(key, "inode") == 0) { archive_entry_set_ino(entry, mtree_atol10(&val)); break; } case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); break; } case 'm': if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) break; if (strcmp(key, "mode") == 0) { if (val[0] >= '0' && val[0] <= '9') { *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, (mode_t)mtree_atol8(&val)); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic mode \"%s\" unsupported", val); return ARCHIVE_WARN; } break; } case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; archive_entry_set_nlink(entry, (unsigned int)mtree_atol10(&val)); break; } case 'r': if (strcmp(key, "resdevice") == 0) { /* stat(2) st_dev field, e.g. the device ID where the * inode resides */ int r; dev_t dev; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_dev(entry, dev); return r; } if (strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) break; case 's': if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) break; if (strcmp(key, "sha256") == 0 || strcmp(key, "sha256digest") == 0) break; if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) break; if (strcmp(key, "sha512") == 0 || strcmp(key, "sha512digest") == 0) break; if (strcmp(key, "size") == 0) { archive_entry_set_size(entry, mtree_atol10(&val)); break; } case 't': if (strcmp(key, "tags") == 0) { /* * Comma delimited list of tags. * Ignore the tags for now, but the interface * should be extended to allow inclusion/exclusion. */ break; } if (strcmp(key, "time") == 0) { int64_t m; int64_t my_time_t_max = get_time_t_max(); int64_t my_time_t_min = get_time_t_min(); long ns = 0; *parsed_kws |= MTREE_HAS_MTIME; m = mtree_atol10(&val); /* Replicate an old mtree bug: * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { ++val; ns = (long)mtree_atol10(&val); } else ns = 0; if (m > my_time_t_max) m = my_time_t_max; else if (m < my_time_t_min) m = my_time_t_min; archive_entry_set_mtime(entry, (time_t)m, ns); break; } if (strcmp(key, "type") == 0) { switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { archive_entry_set_filetype(entry, AE_IFBLK); break; } case 'c': if (strcmp(val, "char") == 0) { archive_entry_set_filetype(entry, AE_IFCHR); break; } case 'd': if (strcmp(val, "dir") == 0) { archive_entry_set_filetype(entry, AE_IFDIR); break; } case 'f': if (strcmp(val, "fifo") == 0) { archive_entry_set_filetype(entry, AE_IFIFO); break; } if (strcmp(val, "file") == 0) { archive_entry_set_filetype(entry, AE_IFREG); break; } case 'l': if (strcmp(val, "link") == 0) { archive_entry_set_filetype(entry, AE_IFLNK); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized file type \"%s\"; " "assuming \"file\"", val); archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_WARN); } *parsed_kws |= MTREE_HAS_TYPE; break; } case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "uname") == 0) { *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { size_t bytes_to_read; ssize_t bytes_read; struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd < 0) { *buff = NULL; *offset = 0; *size = 0; return (ARCHIVE_EOF); } if (mtree->buff == NULL) { mtree->buffsize = 64 * 1024; mtree->buff = malloc(mtree->buffsize); if (mtree->buff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } *buff = mtree->buff; *offset = mtree->offset; if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset) bytes_to_read = (size_t)(mtree->cur_size - mtree->offset); else bytes_to_read = mtree->buffsize; bytes_read = read(mtree->fd, mtree->buff, bytes_to_read); if (bytes_read < 0) { archive_set_error(&a->archive, errno, "Can't read"); return (ARCHIVE_WARN); } if (bytes_read == 0) { *size = 0; return (ARCHIVE_EOF); } mtree->offset += bytes_read; *size = bytes_read; return (ARCHIVE_OK); } /* Skip does nothing except possibly close the contents file. */ static int skip(struct archive_read *a) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } return (ARCHIVE_OK); } /* * Since parsing backslash sequences always makes strings shorter, * we can always do this conversion in-place. */ static void parse_escapes(char *src, struct mtree_entry *mentry) { char *dest = src; char c; if (mentry != NULL && strcmp(src, ".") == 0) mentry->full = 1; while (*src != '\0') { c = *src++; if (c == '/' && mentry != NULL) mentry->full = 1; if (c == '\\') { switch (src[0]) { case '0': if (src[1] < '0' || src[1] > '7') { c = 0; ++src; break; } /* FALLTHROUGH */ case '1': case '2': case '3': if (src[1] >= '0' && src[1] <= '7' && src[2] >= '0' && src[2] <= '7') { c = (src[0] - '0') << 6; c |= (src[1] - '0') << 3; c |= (src[2] - '0'); src += 3; } break; case 'a': c = '\a'; ++src; break; case 'b': c = '\b'; ++src; break; case 'f': c = '\f'; ++src; break; case 'n': c = '\n'; ++src; break; case 'r': c = '\r'; ++src; break; case 's': c = ' '; ++src; break; case 't': c = '\t'; ++src; break; case 'v': c = '\v'; ++src; break; case '\\': c = '\\'; ++src; break; } } *dest++ = c; } *dest = '\0'; } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t mtree_atol8(char **p) { int64_t l, limit, last_digit_limit; int digit, base; base = 8; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; l = 0; digit = **p - '0'; while (digit >= 0 && digit < base) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = INT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++(*p) - '0'; } return (l); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t mtree_atol10(char **p) { int64_t l, limit, last_digit_limit; int base, digit, sign; base = 10; if (**p == '-') { sign = -1; limit = ((uint64_t)(INT64_MAX) + 1) / base; last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; ++(*p); } else { sign = 1; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; } l = 0; digit = **p - '0'; while (digit >= 0 && digit < base) { if (l > limit || (l == limit && digit > last_digit_limit)) return (sign < 0) ? INT64_MIN : INT64_MAX; l = (l * base) + digit; digit = *++(*p) - '0'; } return (sign < 0) ? -l : l; } /* Parse a hex digit. */ static int parsehex(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') return c - 'a'; else if (c >= 'A' && c <= 'F') return c - 'A'; else return -1; } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t mtree_atol16(char **p) { int64_t l, limit, last_digit_limit; int base, digit, sign; base = 16; if (**p == '-') { sign = -1; limit = ((uint64_t)(INT64_MAX) + 1) / base; last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; ++(*p); } else { sign = 1; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; } l = 0; digit = parsehex(**p); while (digit >= 0 && digit < base) { if (l > limit || (l == limit && digit > last_digit_limit)) return (sign < 0) ? INT64_MIN : INT64_MAX; l = (l * base) + digit; digit = parsehex(*++(*p)); } return (sign < 0) ? -l : l; } static int64_t mtree_atol(char **p) { if (**p != '0') return mtree_atol10(p); if ((*p)[1] == 'x' || (*p)[1] == 'X') { *p += 2; return mtree_atol16(p); } return mtree_atol8(p); } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. */ static ssize_t readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; const void *t; void *nl; char *u; /* Accumulate line in a line buffer. */ for (;;) { /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (t == NULL) return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); nl = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read to end exactly there. */ if (nl != NULL) { bytes_read = ((const char *)nl) - ((const char *)t) + 1; } if (total_size + bytes_read + 1 > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&mtree->line, total_size + bytes_read + 1) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } /* Append new bytes to string. */ memcpy(mtree->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; mtree->line.s[total_size] = '\0'; for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { /* Ends with unescaped newline. */ *start = mtree->line.s; return total_size; } else if (u[0] == '#') { /* Ends with comment sequence #...\n */ if (nl == NULL) { /* But we've not found the \n yet */ break; } } else if (u[0] == '\\') { if (u[1] == '\n') { /* Trim escaped newline. */ total_size -= 2; mtree->line.s[total_size] = '\0'; break; } else if (u[1] != '\0') { /* Skip the two-char escape sequence */ ++u; } } } find_off = u - mtree->line.s; } } Index: vendor/libarchive/dist/libarchive/archive_read_support_format_zip.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_zip.c (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_zip.c (revision 302003) @@ -1,3053 +1,3062 @@ /*- * 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 void process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; 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) { break; } #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; } #ifdef DEBUG if (offset != extra_length) { fprintf(stderr, "Extra data field contents do not match reported size!\n"); } #endif } /* * Assumes file pointer is at beginning of local file header. */ static int zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const char *p; const void *h; const wchar_t *wp; const char *cp; size_t len, filename_length, extra_length; struct archive_string_conv *sconv; struct zip_entry *zip_entry = zip->entry; struct zip_entry zip_entry_central_dir; int ret = ARCHIVE_OK; char version; /* Save a copy of the original for consistency checks. */ zip_entry_central_dir = *zip_entry; zip->decompress_init = 0; zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); /* Setup default conversion. */ if (zip->sconv == NULL && !zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_read(&(a->archive)); zip->init_default_conversion = 1; } if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_FATAL; } version = p[4]; zip_entry->system = p[5]; zip_entry->zip_flags = archive_le16dec(p + 6); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { zip->has_encrypted_entries = 1; archive_entry_set_is_data_encrypted(entry, 1); if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED && zip_entry->zip_flags & ZIP_ENCRYPTED && zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) { archive_entry_set_is_metadata_encrypted(entry, 1); return ARCHIVE_FATAL; } } zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); zip_entry->mtime = zip_time(p + 10); zip_entry->crc32 = archive_le32dec(p + 14); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[11]; else zip_entry->decdat = p[17]; zip_entry->compressed_size = archive_le32dec(p + 18); zip_entry->uncompressed_size = archive_le32dec(p + 22); filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); __archive_read_consume(a, 30); /* Read the filename. */ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (zip_entry->zip_flags & ZIP_UTF8_NAME) { /* The filename is stored to be UTF-8. */ if (zip->sconv_utf8 == NULL) { zip->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); if (zip->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = zip->sconv_utf8; } else if (zip->sconv != NULL) sconv = zip->sconv; else sconv = zip->sconv_default; if (archive_entry_copy_pathname_l(entry, h, filename_length, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } __archive_read_consume(a, filename_length); /* Read the extra data. */ if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } process_extra(h, extra_length, zip_entry); __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. */ 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; } 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; } if (zip_entry->mode == AE_IFDIR) { zip_entry->mode |= 0775; } else if (zip_entry->mode == AE_IFREG) { zip_entry->mode |= 0664; } } /* 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_size - - (zip->decrypted_ptr - zip->decrypted_buffer); + 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; } process_extra(p + filename_length, extra_length, zip_entry); /* * 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_write_disk_windows.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_disk_windows.c (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_write_disk_windows.c (revision 302003) @@ -1,2504 +1,2512 @@ /*- * Copyright (c) 2003-2010 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 * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #if defined(_WIN32) && !defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_UTIME_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 #include /* TODO: Support Mac OS 'quarantine' feature. This is really just a * standard tag to mark files that have been downloaded as "tainted". * On Mac OS, we should mark the extracted files as tainted if the * archive being read was tainted. Windows has a similar feature; we * should investigate ways to support this generically. */ #include "archive.h" #include "archive_acl_private.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_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 static BOOL SetFilePointerEx_perso(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) { LARGE_INTEGER li; li.QuadPart = liDistanceToMove.QuadPart; li.LowPart = SetFilePointer( hFile, li.LowPart, &li.HighPart, dwMoveMethod); if(lpNewFilePointer) { lpNewFilePointer->QuadPart = li.QuadPart; } return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR; } struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; int64_t ctime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long ctime_nanos; unsigned long fflags_set; int fixup; /* bitmask of what needs fixing */ wchar_t *name; }; /* * We use a bitmask to track which operations remain to be done for * this file. In particular, this helps us avoid unnecessary * operations when it's possible to take care of one step as a * side-effect of another. For example, mkdir() can specify the mode * for the newly-created object but symlink() cannot. This means we * can skip chmod() if mkdir() succeeded, but we must explicitly * chmod() if we're trying to create a directory that already exists * (mkdir() failed) or if we're restoring a symlink. Similarly, we * need to verify UID/GID before trying to restore SUID/SGID bits; * that verification can occur explicitly through a stat() call or * implicitly because of a successful chown() call. */ #define TODO_MODE_FORCE 0x40000000 #define TODO_MODE_BASE 0x20000000 #define TODO_SUID 0x10000000 #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 #define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) #define TODO_TIMES ARCHIVE_EXTRACT_TIME #define TODO_OWNER ARCHIVE_EXTRACT_OWNER #define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS #define TODO_ACLS ARCHIVE_EXTRACT_ACL #define TODO_XATTR ARCHIVE_EXTRACT_XATTR #define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; int64_t user_uid; int skip_file_set; int64_t skip_file_dev; int64_t skip_file_ino; time_t start_time; int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid); void (*cleanup_uid)(void *private); void *lookup_uid_data; /* * Full path of last file to satisfy symlink checks. */ struct archive_wstring path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ BY_HANDLE_FILE_INFORMATION st; BY_HANDLE_FILE_INFORMATION *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ wchar_t *name; /* Name of entry, possibly edited. */ struct archive_wstring _name_data; /* backing store for 'name' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ int deferred; /* Options requested by the client. */ int flags; /* Handle for the file we're restoring. */ HANDLE fh; /* Current offset for writing data to the file. */ int64_t offset; /* Last offset actually written to disk. */ int64_t fd_offset; /* Total bytes actually written to files. */ int64_t total_bytes_written; /* Maximum size of file, -1 if unknown. */ int64_t filesize; /* Dir we were in before this restore; only for deep paths. */ int restore_pwd; /* Mode we should use for this entry; affected by _PERM and umask. */ mode_t mode; /* UID/GID to use in restoring this entry. */ int64_t uid; int64_t gid; }; /* * Default mode for dirs created automatically (will be modified by umask). * Note that POSIX specifies 0777 for implicity-created dirs, "modified * by the process' file creation mask." */ #define DEFAULT_DIR_MODE 0777 /* * Dir modes are restored in two steps: During the extraction, the permissions * in the archive are modified to match the following limits. During * the post-extract fixup pass, the permissions from the archive are * applied. */ #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const wchar_t *pathname); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, wchar_t *); static int create_parent_dir(struct archive_write_disk *, wchar_t *); static int la_chmod(const wchar_t *, mode_t); static int older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *); static int permissive_name_w(struct archive_write_disk *); static int restore_entry(struct archive_write_disk *); static int set_acls(struct archive_write_disk *, HANDLE h, const wchar_t *, struct archive_acl *); static int set_xattrs(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_times(struct archive_write_disk *, HANDLE, int, const wchar_t *, time_t, long, time_t, long, time_t, long, time_t, long); static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); static struct archive_vtable *archive_write_disk_vtable(void); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); static int _archive_write_disk_header(struct archive *, struct archive_entry *); static int64_t _archive_write_disk_filter_bytes(struct archive *, int); static int _archive_write_disk_finish_entry(struct archive *); static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); #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) #define bhfi_size(bhfi) \ ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow) static int file_information(struct archive_write_disk *a, wchar_t *path, BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat) { HANDLE h; int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; WIN32_FIND_DATAW findData; if (sim_lstat || mode != NULL) { h = FindFirstFileW(path, &findData); if (h == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME) { wchar_t *full; full = __la_win_permissive_name_w(path); h = FindFirstFileW(full, &findData); free(full); } if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); return (-1); } FindClose(h); } /* Is symlink file ? */ if (sim_lstat && ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))) flag |= FILE_FLAG_OPEN_REPARSE_POINT; h = CreateFileW(a->name, 0, 0, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME) { wchar_t *full; full = __la_win_permissive_name_w(path); h = CreateFileW(full, 0, 0, NULL, OPEN_EXISTING, flag, NULL); free(full); } if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); return (-1); } r = GetFileInformationByHandle(h, st); CloseHandle(h); if (r == 0) { la_dosmaperr(GetLastError()); return (-1); } if (mode == NULL) return (0); *mode = S_IRUSR | S_IRGRP | S_IROTH; if ((st->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) *mode |= S_IWUSR | S_IWGRP | S_IWOTH; if ((st->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) *mode |= S_IFLNK; else if (st->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; } } } return (0); } /* * Note: The path, for example, "aa/a/../b../c" will be converted to "aa/c" * by GetFullPathNameW() W32 API, which __la_win_permissive_name_w uses. * It means we cannot handle multiple dirs in one archive_entry. * So we have to make the full-pathname in another way, which does not * break "../" path string. */ static int permissive_name_w(struct archive_write_disk *a) { wchar_t *wn, *wnp; wchar_t *ws, *wsp; DWORD l; wnp = a->name; if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] == L'?' && wnp[3] == L'\\') /* We have already a permissive name. */ return (0); if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] == L'.' && wnp[3] == L'\\') { /* This is a device name */ if (((wnp[4] >= L'a' && wnp[4] <= L'z') || (wnp[4] >= L'A' && wnp[4] <= L'Z')) && wnp[5] == L':' && wnp[6] == L'\\') { wnp[2] = L'?';/* Not device name. */ return (0); } } /* * A full-pathname starting with a drive name like "C:\abc". */ if (((wnp[0] >= L'a' && wnp[0] <= L'z') || (wnp[0] >= L'A' && wnp[0] <= L'Z')) && wnp[1] == L':' && wnp[2] == L'\\') { wn = _wcsdup(wnp); if (wn == NULL) return (-1); archive_wstring_ensure(&(a->_name_data), 4 + wcslen(wn) + 1); a->name = a->_name_data.s; /* Prepend "\\?\" */ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4); archive_wstrcat(&(a->_name_data), wn); free(wn); return (0); } /* * A full-pathname pointig a network drive * like "\\\\file". */ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') { const wchar_t *p = &wnp[2]; /* Skip server-name letters. */ while (*p != L'\\' && *p != L'\0') ++p; if (*p == L'\\') { const wchar_t *rp = ++p; /* Skip share-name letters. */ while (*p != L'\\' && *p != L'\0') ++p; if (*p == L'\\' && p != rp) { /* Now, match patterns such as * "\\server-name\share-name\" */ wn = _wcsdup(wnp); if (wn == NULL) return (-1); archive_wstring_ensure(&(a->_name_data), 8 + wcslen(wn) + 1); a->name = a->_name_data.s; /* Prepend "\\?\UNC\" */ archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8); archive_wstrcat(&(a->_name_data), wn+2); free(wn); return (0); } } return (0); } /* * Get current working directory. */ l = GetCurrentDirectoryW(0, NULL); if (l == 0) return (-1); ws = malloc(l * sizeof(wchar_t)); l = GetCurrentDirectoryW(l, ws); if (l == 0) { free(ws); return (-1); } wsp = ws; /* * A full-pathname starting without a drive name like "\abc". */ if (wnp[0] == L'\\') { wn = _wcsdup(wnp); if (wn == NULL) return (-1); archive_wstring_ensure(&(a->_name_data), 4 + 2 + wcslen(wn) + 1); a->name = a->_name_data.s; /* Prepend "\\?\" and drive name. */ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4); archive_wstrncat(&(a->_name_data), wsp, 2); archive_wstrcat(&(a->_name_data), wn); free(wsp); free(wn); return (0); } wn = _wcsdup(wnp); if (wn == NULL) return (-1); archive_wstring_ensure(&(a->_name_data), 4 + l + 1 + wcslen(wn) + 1); a->name = a->_name_data.s; - /* Prepend "\\?\" and drive name. */ - archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4); - archive_wstrncat(&(a->_name_data), wsp, l); + /* Prepend "\\?\" and drive name if not already added. */ + if (l > 3 && wsp[0] == L'\\' && wsp[1] == L'\\' && + wsp[2] == L'?' && wsp[3] == L'\\') + { + archive_wstrncpy(&(a->_name_data), wsp, l); + } + else + { + archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4); + archive_wstrncat(&(a->_name_data), wsp, l); + } archive_wstrncat(&(a->_name_data), L"\\", 1); archive_wstrcat(&(a->_name_data), wn); a->name = a->_name_data.s; free(wsp); free(wn); return (0); } static int la_chmod(const wchar_t *path, mode_t mode) { DWORD attr; BOOL r; wchar_t *fullname; int ret = 0; fullname = NULL; attr = GetFileAttributesW(path); if (attr == (DWORD)-1 && GetLastError() == ERROR_INVALID_NAME) { fullname = __la_win_permissive_name_w(path); attr = GetFileAttributesW(fullname); } if (attr == (DWORD)-1) { la_dosmaperr(GetLastError()); ret = -1; goto exit_chmode; } if (mode & _S_IWRITE) attr &= ~FILE_ATTRIBUTE_READONLY; else attr |= FILE_ATTRIBUTE_READONLY; if (fullname != NULL) r = SetFileAttributesW(fullname, attr); else r = SetFileAttributesW(path, attr); if (r == 0) { la_dosmaperr(GetLastError()); ret = -1; } exit_chmode: free(fullname); return (ret); } static void * la_GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary(TEXT("kernel32.dll")); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) { static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES); static int set; BOOL ret; if (!set) { set = 1; f = la_GetFunctionKernel32("CreateHardLinkW"); } if (!f) return (0); ret = (*f)(linkname, target, NULL); if (!ret) { /* Under windows 2000, it is necessary to remove * the "\\?\" prefix. */ #define IS_UNC(name) ((name[0] == L'U' || name[0] == L'u') && \ (name[1] == L'N' || name[1] == L'n') && \ (name[2] == L'C' || name[2] == L'c') && \ name[3] == L'\\') if (!wcsncmp(linkname,L"\\\\?\\", 4)) { linkname += 4; if (IS_UNC(linkname)) linkname += 4; } if (!wcsncmp(target,L"\\\\?\\", 4)) { target += 4; if (IS_UNC(target)) target += 4; } #undef IS_UNC ret = (*f)(linkname, target, NULL); } return (ret); } static int la_ftruncate(HANDLE handle, int64_t length) { LARGE_INTEGER distance; if (GetFileType(handle) != FILE_TYPE_DISK) { errno = EBADF; return (-1); } distance.QuadPart = length; if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) { la_dosmaperr(GetLastError()); return (-1); } if (!SetEndOfFile(handle)) { la_dosmaperr(GetLastError()); return (-1); } return (0); } static int lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ return (ARCHIVE_OK); } if (a->fh != INVALID_HANDLE_VALUE && GetFileInformationByHandle(a->fh, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ if (file_information(a, a->name, &a->st, NULL, 1) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static struct archive_vtable * archive_write_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_disk_close; av.archive_filter_bytes = _archive_write_disk_filter_bytes; av.archive_free = _archive_write_disk_free; av.archive_write_header = _archive_write_disk_header; av.archive_write_finish_entry = _archive_write_disk_finish_entry; av.archive_write_data = _archive_write_disk_data; av.archive_write_data_block = _archive_write_disk_data_block; inited = 1; } return (&av); } static int64_t _archive_write_disk_filter_bytes(struct archive *_a, int n) { struct archive_write_disk *a = (struct archive_write_disk *)_a; (void)n; /* UNUSED */ if (n == -1 || n == 0) return (a->total_bytes_written); return (-1); } int archive_write_disk_set_options(struct archive *_a, int flags) { struct archive_write_disk *a = (struct archive_write_disk *)_a; a->flags = flags; return (ARCHIVE_OK); } /* * Extract this entry to disk. * * TODO: Validate hardlinks. According to the standards, we're * supposed to check each extracted hardlink and squawk if it refers * to a file that we didn't restore. I'm not entirely convinced this * is a good idea, but more importantly: Is there any way to validate * hardlinks without keeping a complete list of filenames from the * entire archive?? Ugh. * */ static int _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; int ret, r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_header"); archive_clear_error(&a->archive); if (a->archive.state & ARCHIVE_STATE_DATA) { r = _archive_write_disk_finish_entry(&a->archive); if (r == ARCHIVE_FATAL) return (r); } /* Set up for this particular entry. */ a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->entry = archive_entry_clone(entry); a->fh = INVALID_HANDLE_VALUE; a->fd_offset = 0; a->offset = 0; a->restore_pwd = -1; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); if (archive_entry_size_is_set(a->entry)) a->filesize = archive_entry_size(a->entry); else a->filesize = -1; archive_wstrcpy(&(a->_name_data), archive_entry_pathname_w(a->entry)); a->name = a->_name_data.s; archive_clear_error(&a->archive); /* * Clean up the requested path. This is necessary for correct * dir restores; the dir restore logic otherwise gets messed * up by nonsense like "dir/.". */ ret = cleanup_pathname(a); if (ret != ARCHIVE_OK) return (ret); /* * Generate a full-pathname and use it from here. */ if (permissive_name_w(a) < 0) { errno = EINVAL; return (ARCHIVE_FAILED); } /* * Query the umask so we get predictable mode settings. * This gets done on every call to _write_header in case the * user edits their umask during the extraction for some * reason. */ umask(a->user_umask = umask(0)); /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; if (a->flags & ARCHIVE_EXTRACT_PERM) { a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */ /* * SGID requires an extra "check" step because we * cannot easily predict the GID that the system will * assign. (Different systems assign GIDs to files * based on a variety of criteria, including process * credentials and the gid of the enclosing * directory.) We can only restore the SGID bit if * the file has the right GID, and we only know the * GID if we either set it (see set_ownership) or if * we've actually called stat() on the file after it * was restored. Since there are several places at * which we might verify the GID, we need a TODO bit * to keep track. */ if (a->mode & S_ISGID) a->todo |= TODO_SGID | TODO_SGID_CHECK; /* * Verifying the SUID is simpler, but can still be * done in multiple ways, hence the separate "check" bit. */ if (a->mode & S_ISUID) a->todo |= TODO_SUID | TODO_SUID_CHECK; } else { /* * User didn't request full permissions, so don't * restore SUID, SGID bits and obey umask. */ a->mode &= ~S_ISUID; a->mode &= ~S_ISGID; a->mode &= ~S_ISVTX; a->mode &= ~a->user_umask; } #if 0 if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; #endif if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) { if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_ACLS; else a->todo |= TODO_ACLS; } if (a->flags & ARCHIVE_EXTRACT_XATTR) a->todo |= TODO_XATTR; if (a->flags & ARCHIVE_EXTRACT_FFLAGS) a->todo |= TODO_FFLAGS; if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { ret = check_symlinks(a); if (ret != ARCHIVE_OK) return (ret); } ret = restore_entry(a); /* * TODO: There are rumours that some extended attributes must * be restored before file data is written. If this is true, * then we either need to write all extended attributes both * before and after restoring the data, or find some rule for * determining which must go first and which last. Due to the * many ways people are using xattrs, this may prove to be an * intractable problem. */ /* * Fixup uses the unedited pathname from archive_entry_pathname(), * because it is relative to the base dir and the edited path * might be relative to some intermediate dir as a result of the * deep restore logic. */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } if ((a->deferred & TODO_TIMES) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->mode = a->mode; fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { fe->atime = archive_entry_atime(entry); fe->atime_nanos = archive_entry_atime_nsec(entry); } else { /* If atime is unset, use start time. */ fe->atime = a->start_time; fe->atime_nanos = 0; } if (archive_entry_mtime_is_set(entry)) { fe->mtime = archive_entry_mtime(entry); fe->mtime_nanos = archive_entry_mtime_nsec(entry); } else { /* If mtime is unset, use start time. */ fe->mtime = a->start_time; fe->mtime_nanos = 0; } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); fe->birthtime_nanos = archive_entry_birthtime_nsec(entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; fe->birthtime_nanos = fe->mtime_nanos; } } if (a->deferred & TODO_ACLS) { fe = current_fixup(a, archive_entry_pathname_w(entry)); archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } /* * On Windows, A creating sparse file requires a special mark. */ if (a->fh != INVALID_HANDLE_VALUE && archive_entry_sparse_count(entry) > 0) { int64_t base = 0, offset, length; int i, cnt = archive_entry_sparse_reset(entry); int sparse = 0; for (i = 0; i < cnt; i++) { archive_entry_sparse_next(entry, &offset, &length); if (offset - base >= 4096) { sparse = 1;/* we have a hole. */ break; } base = offset + length; } if (sparse) { DWORD dmy; /* Mark this file as sparse. */ DeviceIoControl(a->fh, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dmy, NULL); } } /* We've created the object and are ready to pour data into it. */ if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. * In particular, dirs, links, etc, don't get written to. */ if (a->fh == INVALID_HANDLE_VALUE) { archive_entry_set_size(entry, 0); a->filesize = 0; } return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, int64_t d, int64_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file"); a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } static ssize_t write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { OVERLAPPED ol; uint64_t start_size = size; DWORD bytes_written = 0; ssize_t block_size = 0, bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } if (a->flags & ARCHIVE_EXTRACT_SPARSE) { /* XXX TODO XXX Is there a more appropriate choice here ? */ /* This needn't match the filesystem allocation size. */ block_size = 16*1024; } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { if (block_size == 0) { bytes_to_write = size; } else { /* We're sparsifying the file. */ const char *p, *end; int64_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { if (*p != '\0') break; } a->offset += p - buff; size -= p - buff; buff = p; if (size == 0) break; /* Calculate next block boundary after offset. */ block_end = (a->offset / block_size + 1) * block_size; /* If the adjusted write would cross block boundary, * truncate it to the block boundary. */ bytes_to_write = size; if (a->offset + bytes_to_write > block_end) bytes_to_write = (DWORD)(block_end - a->offset); } memset(&ol, 0, sizeof(ol)); ol.Offset = (DWORD)(a->offset & 0xFFFFFFFF); ol.OffsetHigh = (DWORD)(a->offset >> 32); if (!WriteFile(a->fh, buff, (uint32_t)bytes_to_write, &bytes_written, &ol)) { DWORD lasterr; lasterr = GetLastError(); if (lasterr == ERROR_ACCESS_DENIED) errno = EBADF; else la_dosmaperr(lasterr); archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return ((ssize_t)(start_size - size)); } static ssize_t _archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, int64_t offset) { struct archive_write_disk *a = (struct archive_write_disk *)_a; ssize_t r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data_block"); a->offset = offset; r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, "Write request too large"); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 return (ARCHIVE_OK); #else return (size); #endif } static ssize_t _archive_write_disk_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); return (write_data_block(a, buff, size)); } static int _archive_write_disk_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_HEADER) return (ARCHIVE_OK); archive_clear_error(&a->archive); /* Pad or truncate file to the right size. */ if (a->fh == INVALID_HANDLE_VALUE) { /* There's no file. */ } else if (a->filesize < 0) { /* File size is unknown, so we can't set the size. */ } else if (a->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ } else { if (la_ftruncate(a->fh, a->filesize) == -1) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } } /* Restore metadata. */ /* * Look up the "real" UID only if we're going to need it. * TODO: the TODO_SGID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) { a->uid = archive_write_disk_uid(&a->archive, archive_entry_uname(a->entry), archive_entry_uid(a->entry)); } /* Look up the "real" GID only if we're going to need it. */ /* TODO: the TODO_SUID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) { a->gid = archive_write_disk_gid(&a->archive, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * Restore ownership before set_mode tries to restore suid/sgid * bits. If we set the owner, we know what it is and can skip * a stat() call to examine the ownership of the file on disk. */ if (a->todo & TODO_OWNER) ret = set_ownership(a); /* * set_mode must precede ACLs on systems such as Solaris and * FreeBSD where setting the mode implicitly clears extended ACLs */ if (a->todo & TODO_MODE) { int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } /* * Security-related extended attributes (such as * security.capability on Linux) have to be restored last, * since they're implicitly removed by other file changes. */ if (a->todo & TODO_XATTR) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } /* * Some flags prevent file modification; they must be restored after * file contents are written. */ if (a->todo & TODO_FFLAGS) { int r2 = set_fflags(a); if (r2 < ret) ret = r2; } /* * Time must follow most other metadata; * otherwise atime will get changed. */ if (a->todo & TODO_TIMES) { int r2 = set_times_from_entry(a); if (r2 < ret) ret = r2; } /* * ACLs must be restored after timestamps because there are * ACLs that prevent attribute changes (including time). */ if (a->todo & TODO_ACLS) { int r2 = set_acls(a, a->fh, archive_entry_pathname_w(a->entry), archive_entry_acl(a->entry)); if (r2 < ret) ret = r2; } /* If there's an fd, we can close it now. */ if (a->fh != INVALID_HANDLE_VALUE) { CloseHandle(a->fh); a->fh = INVALID_HANDLE_VALUE; } /* If there's an entry, we can release it now. */ if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup"); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); a->lookup_gid = lookup_gid; a->cleanup_gid = cleanup_gid; a->lookup_gid_data = private_data; return (ARCHIVE_OK); } int archive_write_disk_set_user_lookup(struct archive *_a, void *private_data, int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid), void (*cleanup_uid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup"); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } int64_t archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_gid"); if (a->lookup_gid) return (a->lookup_gid)(a->lookup_gid_data, name, id); return (id); } int64_t archive_write_disk_uid(struct archive *_a, const char *name, int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_uid"); if (a->lookup_uid) return (a->lookup_uid)(a->lookup_uid_data, name, id); return (id); } /* * Create a new archive_write_disk object and initialize it with global state. */ struct archive * archive_write_disk_new(void) { struct archive_write_disk *a; a = (struct archive_write_disk *)malloc(sizeof(*a)); if (a == NULL) return (NULL); memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = archive_write_disk_vtable(); a->start_time = time(NULL); /* Query and restore the umask. */ umask(a->user_umask = umask(0)); if (archive_wstring_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } return (&a->archive); } static int disk_unlink(wchar_t *path) { wchar_t *fullname; int r; r = _wunlink(path); if (r != 0 && GetLastError() == ERROR_INVALID_NAME) { fullname = __la_win_permissive_name_w(path); r = _wunlink(fullname); free(fullname); } return (r); } static int disk_rmdir(wchar_t *path) { wchar_t *fullname; int r; r = _wrmdir(path); if (r != 0 && GetLastError() == ERROR_INVALID_NAME) { fullname = __la_win_permissive_name_w(path); r = _wrmdir(fullname); free(fullname); } return (r); } /* * The main restore function. */ static int restore_entry(struct archive_write_disk *a) { int ret = ARCHIVE_OK, en; if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { /* * TODO: Fix this. Apparently, there are platforms * that still allow root to hose the entire filesystem * by unlinking a dir. The S_ISDIR() test above * prevents us from using unlink() here if the new * object is a dir, but that doesn't mean the old * object isn't a dir. */ if (disk_unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (disk_rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); return(ARCHIVE_FAILED); } } /* Try creating it first; if this fails, we'll try to recover. */ en = create_filesystem_object(a); if ((en == ENOTDIR || en == ENOENT) && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) { wchar_t *full; /* If the parent dir doesn't exist, try creating it. */ create_parent_dir(a, a->name); /* Now try to create the object again. */ full = __la_win_permissive_name_w(a->name); if (full == NULL) { en = EINVAL; } else { /* Remove multiple directories such as "a/../b../c" */ archive_wstrcpy(&(a->_name_data), full); a->name = a->_name_data.s; free(full); en = create_filesystem_object(a); } } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } /* * Some platforms return EISDIR if you call * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some * return EEXIST. POSIX is ambiguous, requiring EISDIR * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) * on an existing item. */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ if (disk_rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { mode_t st_mode; /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; /* * The SECURE_SYMLINK logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) r = file_information(a, a->name, &a->st, &st_mode, 0); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) r = file_information(a, a->name, &a->st, &st_mode, 1); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); } /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) && !S_ISDIR(st_mode)) { if (!older(&(a->st), a->entry)) { archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } } /* If it's our archive, we're done. */ if (a->skip_file_set && bhfi_dev(&a->st) == a->skip_file_dev && bhfi_ino(&a->st) == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } if (!S_ISDIR(st_mode)) { /* A non-dir is in the way, unlink it. */ if (disk_unlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ if (disk_rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } /* Try again. */ en = create_filesystem_object(a); } else { /* * There's a dir in the way of a dir. Don't * waste time with rmdir()/mkdir(), just fix * up the permissions on the existing dir. * Note that we don't change perms on existing * dirs unless _EXTRACT_PERM is specified. */ if ((a->mode != st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ en = 0; /* Forget the EEXIST. */ } } if (en) { /* Everything failed; give up here. */ archive_set_error(&a->archive, en, "Can't create '%ls'", a->name); return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ return (ret); } /* * Returns 0 if creation succeeds, or else returns errno value from * the failed system call. Note: This function should only ever perform * a single system call. */ static int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const wchar_t *linkname; wchar_t *fullname; mode_t final_mode, mode; int r; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ linkname = archive_entry_hardlink_w(a->entry); if (linkname != NULL) { wchar_t *linkfull, *namefull; linkfull = __la_win_permissive_name_w(linkname); namefull = __la_win_permissive_name_w(a->name); if (linkfull == NULL || namefull == NULL) { errno = EINVAL; r = -1; } else { r = la_CreateHardLinkW(namefull, linkfull); if (r == 0) { la_dosmaperr(GetLastError()); r = errno; } else r = 0; } /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. * * If the hardlink was successfully created and * the archive doesn't have carry data for it, * consider it to be non-authoritative for meta data. * This is consistent with GNU tar and BSD pax. * If the hardlink does carry data, let the last * archive entry decide ownership. */ if (r == 0 && a->filesize <= 0) { a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (a->fh == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); r = errno; } } free(linkfull); free(namefull); return (r); } linkname = archive_entry_symlink_w(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else return (EPERM); #endif } /* * The remaining system calls all set permissions, so let's * try to take advantage of that to avoid an extra chmod() * call. (Recall that umask is set to zero right now!) */ /* Mode we want for the final restored object (w/o file type bits). */ final_mode = a->mode & 07777; /* * The mode that will actually be restored in this step. Note * that SUID, SGID, etc, require additional work to ensure * security, so we never restore them at this point. */ mode = final_mode & 0777 & ~a->user_umask; switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: fullname = a->name; /* O_WRONLY | O_CREAT | O_EXCL */ a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (a->fh == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME && fullname == a->name) { fullname = __la_win_permissive_name_w(a->name); a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); } if (a->fh == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_ACCESS_DENIED) { DWORD attr; /* Simulate an errno of POSIX system. */ attr = GetFileAttributesW(fullname); if (attr == (DWORD)-1) la_dosmaperr(GetLastError()); else if (attr & FILE_ATTRIBUTE_DIRECTORY) errno = EISDIR; else errno = EACCES; } else la_dosmaperr(GetLastError()); r = 1; } else r = 0; if (fullname != a->name) free(fullname); break; case AE_IFCHR: case AE_IFBLK: /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; fullname = a->name; r = CreateDirectoryW(fullname, NULL); if (r == 0 && GetLastError() == ERROR_INVALID_NAME && fullname == a->name) { fullname = __la_win_permissive_name_w(a->name); r = CreateDirectoryW(fullname, NULL); } if (r != 0) { r = 0; /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; /* Never use an immediate chmod(). */ /* We can't avoid the chmod() entirely if EXTRACT_PERM * because of SysV SGID inheritance. */ if ((mode != final_mode) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; } else { la_dosmaperr(GetLastError()); r = -1; } if (fullname != a->name) free(fullname); break; case AE_IFIFO: /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); } /* All the system calls above set errno on failure. */ if (r) return (errno); /* If we managed to set the final mode, we've avoided a chmod(). */ if (mode == final_mode) a->todo &= ~TODO_MODE; return (0); } /* * Cleanup function for archive_extract. Mostly, this involves processing * the fixup list, which is used to address a number of problems: * * Dir permissions might prevent us from restoring a file in that * dir, so we restore the dir with minimum 0700 permissions first, * then correct the mode at the end. * * Similarly, the act of restoring a file touches the directory * and changes the timestamp on the dir, so we have to touch-up dir * timestamps at the end as well. * * Some file flags can interfere with the restore by, for example, * preventing the creation of hardlinks to those files. * * Mac OS extended metadata includes ACLs, so must be deferred on dirs. * * Note that tar/cpio do not require that archives be in a particular * order; there is no way to know when the last file has been restored * within a directory, so there's no way to optimize the memory usage * here by fixing up the directory any earlier than the * end-of-archive. * * XXX TODO: Directory ACLs should be restored here, for the same * reason we set directory perms here. XXX */ static int _archive_write_disk_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; int ret; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_close"); ret = _archive_write_disk_finish_entry(&a->archive); /* Sort dir list so directories are fixed up in depth-first order. */ p = sort_dir_list(a->fixup_list); while (p != NULL) { a->pst = NULL; /* Mark stat cache as out-of-date. */ if (p->fixup & TODO_TIMES) { set_times(a, INVALID_HANDLE_VALUE, p->mode, p->name, p->atime, p->atime_nanos, p->birthtime, p->birthtime_nanos, p->mtime, p->mtime_nanos, p->ctime, p->ctime_nanos); } if (p->fixup & TODO_MODE_BASE) la_chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl); next = p->next; archive_acl_clear(&p->acl); free(p->name); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_disk_free(struct archive *_a) { struct archive_write_disk *a; int ret; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free"); a = (struct archive_write_disk *)_a; ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); if (a->entry) archive_entry_free(a->entry); archive_wstring_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_wstring_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (ret); } /* * Simple O(n log n) merge sort to order the fixup list. In * particular, we want to restore dir timestamps depth-first. */ static struct fixup_entry * sort_dir_list(struct fixup_entry *p) { struct fixup_entry *a, *b, *t; if (p == NULL) return (NULL); /* A one-item list is already sorted. */ if (p->next == NULL) return (p); /* Step 1: split the list. */ t = p; a = p->next->next; while (a != NULL) { /* Step a twice, t once. */ a = a->next; if (a != NULL) a = a->next; t = t->next; } /* Now, t is at the mid-point, so break the list here. */ b = t->next; t->next = NULL; a = p; /* Step 2: Recursively sort the two sub-lists. */ a = sort_dir_list(a); b = sort_dir_list(b); /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ if (wcscmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { t = p = b; b = b->next; } /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { if (wcscmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { t->next = b; b = b->next; } t = t->next; } /* Only one list is non-empty, so just splice it on. */ if (a != NULL) t->next = a; if (b != NULL) t->next = b; return (p); } /* * Returns a new, initialized fixup entry. * * TODO: Reduce the memory requirements for this list by using a tree * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive_write_disk *a, const wchar_t *pathname) { struct fixup_entry *fe; fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry)); if (fe == NULL) return (NULL); fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; fe->name = _wcsdup(pathname); return (fe); } /* * Returns a fixup structure for the current entry. */ static struct fixup_entry * current_fixup(struct archive_write_disk *a, const wchar_t *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); return (a->current_fixup); } /* TODO: Make this work. */ /* * TODO: The deep-directory support bypasses this; disable deep directory * support if we're doing symlink checks. */ /* * TODO: Someday, integrate this with the deep dir support; they both * scan the path and both can be optimized by comparing against other * recent paths. */ /* TODO: Extend this to support symlinks on Windows Vista and later. */ static int check_symlinks(struct archive_write_disk *a) { wchar_t *pn, *p; wchar_t c; int r; BY_HANDLE_FILE_INFORMATION st; mode_t st_mode; /* * Guard against symlink tricks. Reject any archive entry whose * destination would be altered by a symlink. */ /* Whatever we checked last time doesn't need to be re-checked. */ pn = a->name; p = a->path_safe.s; while ((*pn != '\0') && (*p == *pn)) ++p, ++pn; c = pn[0]; /* Keep going until we've checked the entire name. */ while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) { /* Skip the next path element. */ while (*pn != '\0' && *pn != '\\') ++pn; c = pn[0]; pn[0] = '\0'; /* Check that we haven't hit a symlink. */ r = file_information(a, a->name, &st, &st_mode, 1); if (r != 0) { /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) break; } else if (S_ISLNK(st_mode)) { if (c == '\0') { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ if (disk_unlink(a->name)) { archive_set_error(&a->archive, errno, "Could not remove symlink %ls", a->name); pn[0] = c; return (ARCHIVE_FAILED); } a->pst = NULL; /* * Even if we did remove it, a warning * is in order. The warning is silly, * though, if we're just replacing one * symlink with another symlink. */ if (!S_ISLNK(a->mode)) { archive_set_error(&a->archive, 0, "Removing symlink %ls", a->name); } /* Symlink gone. No more problem! */ pn[0] = c; return (0); } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ if (disk_unlink(a->name) != 0) { archive_set_error(&a->archive, 0, "Cannot remove intervening " "symlink %ls", a->name); pn[0] = c; return (ARCHIVE_FAILED); } a->pst = NULL; } else { archive_set_error(&a->archive, 0, "Cannot extract through symlink %ls", a->name); pn[0] = c; return (ARCHIVE_FAILED); } } } pn[0] = c; /* We've checked and/or cleaned the whole path, so remember it. */ archive_wstrcpy(&a->path_safe, a->name); return (ARCHIVE_OK); } static int guidword(wchar_t *p, int n) { int i; for (i = 0; i < n; i++) { if ((*p >= L'0' && *p <= L'9') || (*p >= L'a' && *p <= L'f') || (*p >= L'A' && *p <= L'F')) p++; else return (-1); } return (0); } /* * Canonicalize the pathname. In particular, this strips duplicate * '\' characters, '.' elements, and trailing '\'. It also raises an * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is * set) any '..' in the path. */ static int cleanup_pathname(struct archive_write_disk *a) { wchar_t *dest, *src, *p, *top; wchar_t separator = L'\0'; p = a->name; if (*p == L'\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid empty pathname"); return (ARCHIVE_FAILED); } /* Replace '/' by '\' */ for (; *p != L'\0'; p++) { if (*p == L'/') *p = L'\\'; } p = a->name; /* Skip leading "\\.\" or "\\?\" or "\\?\UNC\" or * "\\?\Volume{GUID}\" * (absolute path prefixes used by Windows API) */ if (p[0] == L'\\' && p[1] == L'\\' && (p[2] == L'.' || p[2] == L'?') && p[3] == L'\\') { /* A path begin with "\\?\UNC\" */ if (p[2] == L'?' && (p[4] == L'U' || p[4] == L'u') && (p[5] == L'N' || p[5] == L'n') && (p[6] == L'C' || p[6] == L'c') && p[7] == L'\\') p += 8; /* A path begin with "\\?\Volume{GUID}\" */ else if (p[2] == L'?' && (p[4] == L'V' || p[4] == L'v') && (p[5] == L'O' || p[5] == L'o') && (p[6] == L'L' || p[6] == L'l') && (p[7] == L'U' || p[7] == L'u') && (p[8] == L'M' || p[8] == L'm') && (p[9] == L'E' || p[9] == L'e') && p[10] == L'{') { if (guidword(p+11, 8) == 0 && p[19] == L'-' && guidword(p+20, 4) == 0 && p[24] == L'-' && guidword(p+25, 4) == 0 && p[29] == L'-' && guidword(p+30, 4) == 0 && p[34] == L'-' && guidword(p+35, 12) == 0 && p[47] == L'}' && p[48] == L'\\') p += 49; else p += 4; /* A path begin with "\\.\PhysicalDriveX" */ } else if (p[2] == L'.' && (p[4] == L'P' || p[4] == L'p') && (p[5] == L'H' || p[5] == L'h') && (p[6] == L'Y' || p[6] == L'y') && (p[7] == L'S' || p[7] == L's') && (p[8] == L'I' || p[8] == L'i') && (p[9] == L'C' || p[9] == L'c') && (p[9] == L'A' || p[9] == L'a') && (p[9] == L'L' || p[9] == L'l') && (p[9] == L'D' || p[9] == L'd') && (p[9] == L'R' || p[9] == L'r') && (p[9] == L'I' || p[9] == L'i') && (p[9] == L'V' || p[9] == L'v') && (p[9] == L'E' || p[9] == L'e') && (p[10] >= L'0' && p[10] <= L'9') && p[11] == L'\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Path is a physical drive name"); return (ARCHIVE_FAILED); } else p += 4; } /* Skip leading drive letter from archives created * on Windows. */ if (((p[0] >= L'a' && p[0] <= L'z') || (p[0] >= L'A' && p[0] <= L'Z')) && p[1] == L':') { if (p[2] == L'\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Path is a drive name"); return (ARCHIVE_FAILED); } if (p[2] == L'\\') p += 2; } top = dest = src = p; /* Rewrite the path name if its character is a unusable. */ for (; *p != L'\0'; p++) { if (*p == L':' || *p == L'*' || *p == L'?' || *p == L'"' || *p == L'<' || *p == L'>' || *p == L'|') *p = L'_'; } /* Skip leading '\'. */ if (*src == L'\\') separator = *src++; /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '\' */ if (src[0] == L'\0') { break; } else if (src[0] == L'\\') { /* Found '\\'('//'), ignore second one. */ src++; continue; } else if (src[0] == L'.') { if (src[1] == L'\0') { /* Ignore trailing '.' */ break; } else if (src[1] == L'\\') { /* Skip '.\'. */ src += 2; continue; } else if (src[1] == L'.') { if (src[2] == L'\\' || src[2] == L'\0') { /* Conditionally warn about '..' */ if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Path contains '..'"); return (ARCHIVE_FAILED); } } /* * Note: Under no circumstances do we * remove '..' elements. In * particular, restoring * '\foo\..\bar\' should create the * 'foo' dir as a side-effect. */ } } /* Copy current element, including leading '\'. */ if (separator) *dest++ = L'\\'; while (*src != L'\0' && *src != L'\\') { *dest++ = *src++; } if (*src == L'\0') break; /* Skip '\' separator. */ separator = *src++; } /* * We've just copied zero or more path elements, not including the * final '\'. */ if (dest == top) { /* * Nothing got copied. The path must have been something * like '.' or '\' or './' or '/././././/./'. */ if (separator) *dest++ = L'\\'; else *dest++ = L'.'; } /* Terminate the result. */ *dest = L'\0'; return (ARCHIVE_OK); } /* * Create the parent directory of the specified path, assuming path * is already in mutable storage. */ static int create_parent_dir(struct archive_write_disk *a, wchar_t *path) { wchar_t *slash; int r; /* Remove tail element to obtain parent name. */ slash = wcsrchr(path, L'\\'); if (slash == NULL) return (ARCHIVE_OK); *slash = L'\0'; r = create_dir(a, path); *slash = L'\\'; return (r); } /* * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int create_dir(struct archive_write_disk *a, wchar_t *path) { BY_HANDLE_FILE_INFORMATION st; struct fixup_entry *le; wchar_t *slash, *base, *full; mode_t mode_final, mode, st_mode; int r; /* Check for special names and just skip them. */ slash = wcsrchr(path, L'\\'); if (slash == NULL) base = path; else base = slash + 1; if (base[0] == L'\0' || (base[0] == L'.' && base[1] == L'\0') || (base[0] == L'.' && base[1] == L'.' && base[2] == L'\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = L'\0'; r = create_dir(a, path); *slash = L'\\'; return (r); } return (ARCHIVE_OK); } /* * Yes, this should be stat() and not lstat(). Using lstat() * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ if (file_information(a, path, &st, &st_mode, 0) == 0) { if (S_ISDIR(st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%ls'", path); return (ARCHIVE_FAILED); } if (disk_unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%ls': " "Conflicting file cannot be removed", path); return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%ls'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '\\'; if (r != ARCHIVE_OK) return (r); } /* * Mode we want for the final restored directory. Per POSIX, * implicitly-created dirs must be created obeying the umask. * There's no mention whether this is different for privileged * restores (which the rest of this code handles by pretending * umask=0). I've chosen here to always obey the user's umask for * implicit dirs, even if _EXTRACT_PERM was specified. */ mode_final = DEFAULT_DIR_MODE & ~a->user_umask; /* Mode we want on disk during the restore process. */ mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; /* * Apply __la_win_permissive_name_w to path in order to * remove '../' path string. */ full = __la_win_permissive_name_w(path); if (full == NULL) errno = EINVAL; else if (CreateDirectoryW(full, NULL) != 0) { if (mode != mode_final) { le = new_fixup(a, path); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } free(full); return (ARCHIVE_OK); } else { la_dosmaperr(GetLastError()); } free(full); /* * Without the following check, a/b/../b/c/d fails at the * second visit to 'b', so 'd' can't be created. Note that we * don't add it to the fixup list here, as it's already been * added. */ if (file_information(a, path, &st, &st_mode, 0) == 0 && S_ISDIR(st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%ls'", path); return (ARCHIVE_FAILED); } /* * Note: Although we can skip setting the user id if the desired user * id matches the current user, we cannot skip setting the group, as * many systems set the gid based on the containing directory. So * we have to perform a chown syscall if we want to set the SGID * bit. (The alternative is to stat() and then possibly chown(); it's * more efficient to skip the stat() and just always chown().) Note * that a successful chown() here clears the TODO_SGID_CHECK bit, which * allows set_mode to skip the stat() check for the GID. */ static int set_ownership(struct archive_write_disk *a) { /* unfortunately, on win32 there is no 'root' user with uid 0, so we just have to try the chown and see if it works */ /* If we know we can't change it, don't bother trying. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%jd", (intmax_t)a->uid); return (ARCHIVE_WARN); } archive_set_error(&a->archive, errno, "Can't set user=%jd/group=%jd for %ls", (intmax_t)a->uid, (intmax_t)a->gid, a->name); return (ARCHIVE_WARN); } static int set_times(struct archive_write_disk *a, HANDLE h, int mode, const wchar_t *name, time_t atime, long atime_nanos, time_t birthtime, long birthtime_nanos, time_t mtime, long mtime_nanos, time_t ctime_sec, long ctime_nanos) { #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE hw = 0; ULARGE_INTEGER wintm; FILETIME *pfbtime; FILETIME fatime, fbtime, fmtime; (void)ctime_sec; /* UNUSED */ (void)ctime_nanos; /* UNUSED */ if (h != INVALID_HANDLE_VALUE) { hw = NULL; } else { wchar_t *ws; if (S_ISLNK(mode)) return (ARCHIVE_OK); ws = __la_win_permissive_name_w(name); if (ws == NULL) goto settimes_failed; hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); free(ws); if (hw == INVALID_HANDLE_VALUE) goto settimes_failed; h = hw; } wintm.QuadPart = WINTIME(atime, atime_nanos); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; wintm.QuadPart = WINTIME(mtime, mtime_nanos); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; /* * SetFileTime() supports birthtime. */ if (birthtime > 0 || birthtime_nanos > 0) { wintm.QuadPart = WINTIME(birthtime, birthtime_nanos); fbtime.dwLowDateTime = wintm.LowPart; fbtime.dwHighDateTime = wintm.HighPart; pfbtime = &fbtime; } else pfbtime = NULL; if (SetFileTime(h, pfbtime, &fatime, &fmtime) == 0) goto settimes_failed; CloseHandle(hw); return (ARCHIVE_OK); settimes_failed: CloseHandle(hw); archive_set_error(&a->archive, EINVAL, "Can't restore time"); return (ARCHIVE_WARN); } static int set_times_from_entry(struct archive_write_disk *a) { time_t atime, birthtime, mtime, ctime_sec; long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec; /* Suitable defaults. */ atime = birthtime = mtime = ctime_sec = a->start_time; atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0; /* If no time was provided, we're done. */ if (!archive_entry_atime_is_set(a->entry) && !archive_entry_birthtime_is_set(a->entry) && !archive_entry_mtime_is_set(a->entry)) return (ARCHIVE_OK); if (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } if (archive_entry_birthtime_is_set(a->entry)) { birthtime = archive_entry_birthtime(a->entry); birthtime_nsec = archive_entry_birthtime_nsec(a->entry); } if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (archive_entry_ctime_is_set(a->entry)) { ctime_sec = archive_entry_ctime(a->entry); ctime_nsec = archive_entry_ctime_nsec(a->entry); } return set_times(a, a->fh, a->mode, a->name, atime, atime_nsec, birthtime, birthtime_nsec, mtime, mtime_nsec, ctime_sec, ctime_nsec); } static int set_mode(struct archive_write_disk *a, int mode) { int r = ARCHIVE_OK; mode &= 07777; /* Strip off file type bits. */ if (a->todo & TODO_SGID_CHECK) { /* * If we don't know the GID is right, we must stat() * to verify it. We can't just check the GID of this * process, since systems sometimes set GID from * the enclosing dir or based on ACLs. */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); if (0 != a->gid) { mode &= ~ S_ISGID; } /* While we're here, double-check the UID. */ if (0 != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; } else if (a->todo & TODO_SUID_CHECK) { /* * If we don't know the UID is right, we can just check * the user, since all systems set the file UID from * the process UID. */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; } a->todo &= ~TODO_SUID_CHECK; } if (S_ISLNK(a->mode)) { #ifdef HAVE_LCHMOD /* * If this is a symlink, use lchmod(). If the * platform doesn't support lchmod(), just skip it. A * platform that doesn't provide a way to set * permissions on symlinks probably ignores * permissions on symlinks, so a failure here has no * impact. */ if (lchmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } #endif } else if (!S_ISDIR(a->mode)) { /* * If it's not a symlink and not a dir, then use * fchmod() or chmod(), depending on whether we have * an fd. Dirs get their perms set during the * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD if (a->fd >= 0) { if (fchmod(a->fd, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } else #endif /* If this platform lacks fchmod(), then * we'll just use chmod(). */ if (la_chmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } return (r); } static int set_fflags(struct archive_write_disk *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } /* Default empty function body to satisfy mainline code. */ static int set_acls(struct archive_write_disk *a, HANDLE h, const wchar_t *name, struct archive_acl *acl) { (void)a; /* UNUSED */ (void)h; /* UNUSED */ (void)name; /* UNUSED */ (void)acl; /* UNUSED */ return (ARCHIVE_OK); } /* * Restore extended attributes - stub implementation for unsupported systems */ static int set_xattrs(struct archive_write_disk *a) { static int warning_done = 0; /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); } /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); } 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; } } /* * Test if file on disk is older than entry. */ static int older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry) { time_t sec; long nsec; fileTimeToUtc(&st->ftLastWriteTime, &sec, &nsec); /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (sec < archive_entry_mtime(entry)) return (1); /* Definitely younger. */ if (sec > archive_entry_mtime(entry)) return (0); if (nsec < archive_entry_mtime_nsec(entry)) return (1); /* Same age or newer, so not older. */ return (0); } #endif /* _WIN32 && !__CYGWIN__ */ Index: vendor/libarchive/dist/libarchive/archive_write_filter.3 =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_filter.3 (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_write_filter.3 (revision 302003) @@ -1,129 +1,130 @@ .\" Copyright (c) 2003-2011 Tim Kientzle .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd August 14, 2014 .Dt ARCHIVE_WRITE_FILTER 3 .Os .Sh NAME .Nm archive_write_add_filter_b64encode , .Nm archive_write_add_filter_by_name , .Nm archive_write_add_filter_bzip2 , .Nm archive_write_add_filter_compress , .Nm archive_write_add_filter_grzip , .Nm archive_write_add_filter_gzip , .Nm archive_write_add_filter_lrzip , .Nm archive_write_add_filter_lz4 , .Nm archive_write_add_filter_lzip , .Nm archive_write_add_filter_lzma , .Nm archive_write_add_filter_lzop , .Nm archive_write_add_filter_none , .Nm archive_write_add_filter_program , .Nm archive_write_add_filter_uuencode , .Nm archive_write_add_filter_xz +.Nd functions enabling output filters .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive.h .Ft int .Fn archive_write_add_filter_b64encode "struct archive *" .Ft int .Fn archive_write_add_filter_bzip2 "struct archive *" .Ft int .Fn archive_write_add_filter_compress "struct archive *" .Ft int .Fn archive_write_add_filter_grzip "struct archive *" .Ft int .Fn archive_write_add_filter_gzip "struct archive *" .Ft int .Fn archive_write_add_filter_lrzip "struct archive *" .Ft int .Fn archive_write_add_filter_lz4 "struct archive *" .Ft int .Fn archive_write_add_filter_lzip "struct archive *" .Ft int .Fn archive_write_add_filter_lzma "struct archive *" .Ft int .Fn archive_write_add_filter_lzop "struct archive *" .Ft int .Fn archive_write_add_filter_none "struct archive *" .Ft int .Fn archive_write_add_filter_program "struct archive *" "const char * cmd" .Ft int .Fn archive_write_add_filter_uuencode "struct archive *" .Ft int .Fn archive_write_add_filter_xz "struct archive *" .Sh DESCRIPTION .Bl -tag -width indent .It Xo .Fn archive_write_add_filter_bzip2 , .Fn archive_write_add_filter_compress , .Fn archive_write_add_filter_grzip , .Fn archive_write_add_filter_gzip , .Fn archive_write_add_filter_lrzip , .Fn archive_write_add_filter_lz4 , .Fn archive_write_add_filter_lzip , .Fn archive_write_add_filter_lzma , .Fn archive_write_add_filter_lzop , .Fn archive_write_add_filter_xz , .Xc The resulting archive will be compressed as specified. Note that the compressed output is always properly blocked. .It Xo .Fn archive_write_add_filter_b64encode , .Fn archive_write_add_filter_uuencode , .Xc The output will be encoded as specified. The encoded output is always properly blocked. .It Fn archive_write_add_filter_none This is never necessary. It is provided only for backwards compatibility. .It Fn archive_write_add_filter_program The archive will be fed into the specified compression program. The output of that program is blocked and written to the client write callbacks. .El .Sh RETURN VALUES These functions return .Cm ARCHIVE_OK on success, or .Cm ARCHIVE_FATAL . .\" .Sh ERRORS Detailed error codes and textual descriptions are available from the .Fn archive_errno and .Fn archive_error_string functions. .\" .Sh SEE ALSO .Xr tar 1 , .Xr libarchive 3 , .Xr archive_write 3 , .Xr archive_write_format 3 , .Xr archive_write_set_options 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 Index: vendor/libarchive/dist/libarchive/archive_write_set_format_gnutar.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_set_format_gnutar.c (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_write_set_format_gnutar.c (revision 302003) @@ -1,761 +1,763 @@ /*- * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). * Author: Jonas Gastal * 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_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" struct gnutar { uint64_t entry_bytes_remaining; uint64_t entry_padding; const char * linkname; size_t linkname_length; const char * pathname; size_t pathname_length; const char * uname; size_t uname_length; const char * gname; size_t gname_length; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; /* * Define structure of GNU tar header. */ #define GNUTAR_name_offset 0 #define GNUTAR_name_size 100 #define GNUTAR_mode_offset 100 #define GNUTAR_mode_size 7 #define GNUTAR_mode_max_size 8 #define GNUTAR_uid_offset 108 #define GNUTAR_uid_size 7 #define GNUTAR_uid_max_size 8 #define GNUTAR_gid_offset 116 #define GNUTAR_gid_size 7 #define GNUTAR_gid_max_size 8 #define GNUTAR_size_offset 124 #define GNUTAR_size_size 11 #define GNUTAR_size_max_size 12 #define GNUTAR_mtime_offset 136 #define GNUTAR_mtime_size 11 #define GNUTAR_mtime_max_size 11 #define GNUTAR_checksum_offset 148 #define GNUTAR_checksum_size 8 #define GNUTAR_typeflag_offset 156 #define GNUTAR_typeflag_size 1 #define GNUTAR_linkname_offset 157 #define GNUTAR_linkname_size 100 #define GNUTAR_magic_offset 257 #define GNUTAR_magic_size 6 #define GNUTAR_version_offset 263 #define GNUTAR_version_size 2 #define GNUTAR_uname_offset 265 #define GNUTAR_uname_size 32 #define GNUTAR_gname_offset 297 #define GNUTAR_gname_size 32 #define GNUTAR_rdevmajor_offset 329 #define GNUTAR_rdevmajor_size 6 #define GNUTAR_rdevmajor_max_size 8 #define GNUTAR_rdevminor_offset 337 #define GNUTAR_rdevminor_size 6 #define GNUTAR_rdevminor_max_size 8 /* * A filled-in copy of the header for initialization. */ static const char template_header[] = { /* name: 100 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Mode, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', /* uid, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', /* gid, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', /* size, space termation: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', '\0', /* mtime, space termation: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', '\0', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', /* Typeflag: 1 byte */ '0', /* '0' = regular file */ /* Linkname: 100 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Magic: 8 bytes */ 'u','s','t','a','r',' ', ' ','\0', /* Uname: 32 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, /* Gname: 32 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, /* rdevmajor + null padding: 8 bytes */ '\0','\0','\0','\0','\0','\0', '\0','\0', /* rdevminor + null padding: 8 bytes */ '\0','\0','\0','\0','\0','\0', '\0','\0', /* Padding: 167 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0 }; static int archive_write_gnutar_options(struct archive_write *, const char *, const char *); static int archive_format_gnutar_header(struct archive_write *, char h[512], struct archive_entry *, int tartype); static int archive_write_gnutar_header(struct archive_write *, struct archive_entry *entry); static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s); static int archive_write_gnutar_free(struct archive_write *); static int archive_write_gnutar_close(struct archive_write *); static int archive_write_gnutar_finish_entry(struct archive_write *); static int format_256(int64_t, char *, int); static int format_number(int64_t, char *, int size, int maxsize); static int format_octal(int64_t, char *, int); /* * Set output format to 'GNU tar' format. */ int archive_write_set_format_gnutar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct gnutar *gnutar; gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar)); if (gnutar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate gnutar data"); return (ARCHIVE_FATAL); } a->format_data = gnutar; a->format_name = "gnutar"; a->format_options = archive_write_gnutar_options; a->format_write_header = archive_write_gnutar_header; a->format_write_data = archive_write_gnutar_data; a->format_close = archive_write_gnutar_close; a->format_free = archive_write_gnutar_free; a->format_finish_entry = archive_write_gnutar_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; a->archive.archive_format_name = "GNU tar"; return (ARCHIVE_OK); } static int archive_write_gnutar_options(struct archive_write *a, const char *key, const char *val) { struct gnutar *gnutar = (struct gnutar *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { gnutar->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (gnutar->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_write_gnutar_close(struct archive_write *a) { return (__archive_write_nulls(a, 512*2)); } static int archive_write_gnutar_free(struct archive_write *a) { struct gnutar *gnutar; gnutar = (struct gnutar *)a->format_data; free(gnutar); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_gnutar_finish_entry(struct archive_write *a) { struct gnutar *gnutar; int ret; gnutar = (struct gnutar *)a->format_data; ret = __archive_write_nulls(a, (size_t) (gnutar->entry_bytes_remaining + gnutar->entry_padding)); gnutar->entry_bytes_remaining = gnutar->entry_padding = 0; return (ret); } static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s) { struct gnutar *gnutar; int ret; gnutar = (struct gnutar *)a->format_data; if (s > gnutar->entry_bytes_remaining) s = (size_t)gnutar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); gnutar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) return (ret); return (s); } static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int r, ret, ret2 = ARCHIVE_OK; int tartype; struct gnutar *gnutar; struct archive_string_conv *sconv; struct archive_entry *entry_main; gnutar = (struct gnutar *)a->format_data; /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { gnutar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); gnutar->init_default_conversion = 1; } sconv = gnutar->sconv_default; } else sconv = gnutar->opt_sconv; /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; size_t path_length; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w(entry, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname(entry, as.s); archive_string_free(&as); } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pahtname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathame"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", archive_entry_uname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_gname_l(entry, &(gnutar->gname), &(gnutar->gname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", archive_entry_gname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } /* If linkname is longer than 100 chars we need to add a 'K' header. */ r = archive_entry_hardlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (gnutar->linkname_length == 0) { r = archive_entry_symlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } } if (gnutar->linkname_length > GNUTAR_linkname_size) { - size_t todo = gnutar->linkname_length; + size_t length = gnutar->linkname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); - archive_entry_set_size(temp, gnutar->linkname_length + 1); + archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'K'); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) goto exit_write_header; archive_entry_free(temp); - /* Write as many 512 bytes blocks as needed to write full name. */ - ret = __archive_write_output(a, gnutar->linkname, todo); + /* Write name and trailing null byte. */ + ret = __archive_write_output(a, gnutar->linkname, length); if(ret < ARCHIVE_WARN) goto exit_write_header; - ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo)); + /* Pad to 512 bytes */ + ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } /* If pathname is longer than 100 chars we need to add an 'L' header. */ if (gnutar->pathname_length > GNUTAR_name_size) { const char *pathname = gnutar->pathname; - size_t todo = gnutar->pathname_length; + size_t length = gnutar->pathname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); - archive_entry_set_size(temp, gnutar->pathname_length + 1); + archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'L'); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) goto exit_write_header; archive_entry_free(temp); - /* Write as many 512 bytes blocks as needed to write full name. */ - ret = __archive_write_output(a, pathname, todo); + /* Write pathname + trailing null byte. */ + ret = __archive_write_output(a, pathname, length); if(ret < ARCHIVE_WARN) goto exit_write_header; - ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo)); + /* Pad to multiple of 512 bytes. */ + ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } if (archive_entry_hardlink(entry) != NULL) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { case AE_IFREG: tartype = '0' ; break; case AE_IFLNK: tartype = '2' ; break; case AE_IFCHR: tartype = '3' ; break; case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); ret = ARCHIVE_FAILED; goto exit_write_header; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); ret = ARCHIVE_FAILED; goto exit_write_header; } ret = archive_format_gnutar_header(a, buff, entry, tartype); if (ret < ARCHIVE_WARN) goto exit_write_header; if (ret2 < ret) ret = ret2; ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { ret = ret2; goto exit_write_header; } if (ret2 < ret) ret = ret2; gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); exit_write_header: if (entry_main) archive_entry_free(entry_main); return (ret); } static int archive_format_gnutar_header(struct archive_write *a, char h[512], struct archive_entry *entry, int tartype) { unsigned int checksum; int i, ret; size_t copy_length; const char *p; struct gnutar *gnutar; gnutar = (struct gnutar *)a->format_data; ret = 0; /* * The "template header" already includes the signature, * various end-of-field markers, and other required elements. */ memcpy(h, &template_header, 512); /* * Because the block is already null-filled, and strings * are allowed to exactly fill their destination (without null), * I use memcpy(dest, src, strlen()) here a lot to copy strings. */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_pathname(entry); copy_length = strlen(p); } else { p = gnutar->pathname; copy_length = gnutar->pathname_length; } if (copy_length > GNUTAR_name_size) copy_length = GNUTAR_name_size; memcpy(h + GNUTAR_name_offset, p, copy_length); if ((copy_length = gnutar->linkname_length) > 0) { if (copy_length > GNUTAR_linkname_size) copy_length = GNUTAR_linkname_size; memcpy(h + GNUTAR_linkname_offset, gnutar->linkname, copy_length); } /* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_uname(entry); copy_length = strlen(p); } else { p = gnutar->uname; copy_length = gnutar->uname_length; } if (copy_length > 0) { if (copy_length > GNUTAR_uname_size) copy_length = GNUTAR_uname_size; memcpy(h + GNUTAR_uname_offset, p, copy_length); } /* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_gname(entry); copy_length = strlen(p); } else { p = gnutar->gname; copy_length = gnutar->gname_length; } if (copy_length > 0) { if (strlen(p) > GNUTAR_gname_size) copy_length = GNUTAR_gname_size; memcpy(h + GNUTAR_gname_offset, p, copy_length); } /* By truncating the mode here, we ensure it always fits. */ format_octal(archive_entry_mode(entry) & 07777, h + GNUTAR_mode_offset, GNUTAR_mode_size); /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset, GNUTAR_uid_size, GNUTAR_uid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID %jd too large", (intmax_t)archive_entry_uid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset, GNUTAR_gid_size, GNUTAR_gid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID %jd too large", (intmax_t)archive_entry_gid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset, GNUTAR_size_size, GNUTAR_size_max_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } /* Shouldn't overflow before 2106, since mtime field is 33 bits. */ format_octal(archive_entry_mtime(entry), h + GNUTAR_mtime_offset, GNUTAR_mtime_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { if (format_octal(archive_entry_rdevmajor(entry), h + GNUTAR_rdevmajor_offset, GNUTAR_rdevmajor_size)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } if (format_octal(archive_entry_rdevminor(entry), h + GNUTAR_rdevminor_offset, GNUTAR_rdevminor_size)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } h[GNUTAR_typeflag_offset] = tartype; checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ /* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ format_octal(checksum, h + GNUTAR_checksum_offset, 6); return (ret); } /* * Format a number into a field, falling back to base-256 if necessary. */ static int format_number(int64_t v, char *p, int s, int maxsize) { int64_t limit = ((int64_t)1 << (s*3)); if (v < limit) return (format_octal(v, p, s)); return (format_256(v, p, maxsize)); } /* * Format a number into the specified field using base-256. */ static int format_256(int64_t v, char *p, int s) { p += s; while (s-- > 0) { *--p = (char)(v & 0xff); v >>= 8; } *p |= 0x80; /* Set the base-256 marker bit. */ return (0); } /* * Format a number into the specified field using octal. */ static int format_octal(int64_t v, char *p, int s) { int len = s; /* Octal values can't be negative, so use 0. */ if (v < 0) v = 0; p += s; /* Start at the end and work backwards. */ while (s-- > 0) { *--p = (char)('0' + (v & 7)); v >>= 3; } if (v == 0) return (0); /* If it overflowed, fill field with max value. */ while (len-- > 0) *p++ = '7'; return (-1); } Index: vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c (revision 302003) @@ -1,8158 +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 ths 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. */ 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. */ 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 componet * - clen : length of componet */ 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 */ 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. */ 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 */ 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. */ 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; - int ffmax, parent_len; + 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, ffmax, 6, 2, &rb_ops); + 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 ((int)(l = np->file->basename_utf16.length) > ffmax) + 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 ((int)np->file->basename_utf16.length > ffmax) { + 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 + np->mb_len > 240) { + 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 ((int)l == ffmax) + if (l == ffmax) noff = ext_off - 6; - else if ((int)l == ffmax-2) + else if (l == ffmax-2) noff = ext_off - 4; - else if ((int)l == ffmax-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. */ 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 */ 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 */ 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/archive_write_set_options.3 =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_set_options.3 (revision 302002) +++ vendor/libarchive/dist/libarchive/archive_write_set_options.3 (revision 302003) @@ -1,480 +1,480 @@ .\" Copyright (c) 2003-2010 Tim Kientzle .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd February 2, 2012 .Dt ARCHIVE_WRITE_OPTIONS 3 .Os .Sh NAME .Nm archive_write_set_filter_option , .Nm archive_write_set_format_option , .Nm archive_write_set_option , .Nm archive_write_set_options -.Nd functions controlling options for reading archives +.Nd functions controlling options for writing archives .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .Ft int .Fo archive_write_set_filter_option .Fa "struct archive *" .Fa "const char *module" .Fa "const char *option" .Fa "const char *value" .Fc .Ft int .Fo archive_write_set_format_option .Fa "struct archive *" .Fa "const char *module" .Fa "const char *option" .Fa "const char *value" .Fc .Ft int .Fo archive_write_set_option .Fa "struct archive *" .Fa "const char *module" .Fa "const char *option" .Fa "const char *value" .Fc .Ft int .Fo archive_write_set_options .Fa "struct archive *" .Fa "const char *options" .Fc .Sh DESCRIPTION These functions provide a way for libarchive clients to configure specific write modules. .Bl -tag -width indent .It Xo .Fn archive_write_set_filter_option , .Fn archive_write_set_format_option .Xc Specifies an option that will be passed to currently-registered filters (including decompression filters) or format readers. .Pp If .Ar option and .Ar value are both .Dv NULL , these functions will do nothing and .Cm ARCHIVE_OK will be returned. If .Ar option is .Dv NULL but .Ar value is not, these functions will do nothing and .Cm ARCHIVE_FAILED will be returned. .Pp If .Ar module is not .Dv NULL , .Ar option and .Ar value will be provided to the filter or reader named .Ar module . The return value will be either .Cm ARCHIVE_OK if the option was successfully handled or .Cm ARCHIVE_WARN if the option was unrecognized by the module or could otherwise not be handled. If there is no such module, .Cm ARCHIVE_FAILED will be returned. .Pp If .Ar module is .Dv NULL , .Ar option and .Ar value will be provided to every registered module. If any module returns .Cm ARCHIVE_FATAL , this value will be returned immediately. Otherwise, .Cm ARCHIVE_OK will be returned if any module accepts the option, and .Cm ARCHIVE_FAILED in all other cases. .\" .It Fn archive_write_set_option Calls .Fn archive_write_set_format_option , then .Fn archive_write_set_filter_option . If either function returns .Cm ARCHIVE_FATAL , .Cm ARCHIVE_FATAL will be returned immediately. Otherwise, greater of the two values will be returned. .\" .It Fn archive_write_set_options .Ar options is a comma-separated list of options. If .Ar options is .Dv NULL or empty, .Cm ARCHIVE_OK will be returned immediately. .Pp Individual options have one of the following forms: .Bl -tag -compact -width indent .It Ar option=value The option/value pair will be provided to every module. Modules that do not accept an option with this name will ignore it. .It Ar option The option will be provided to every module with a value of .Dq 1 . .It Ar !option The option will be provided to every module with a NULL value. .It Ar module:option=value , Ar module:option , Ar module:!option As above, but the corresponding option and value will be provided only to modules whose name matches .Ar module . .El .El .\" .Sh OPTIONS .Bl -tag -compact -width indent .It Filter gzip .Bl -tag -compact -width indent .It Cm compression-level The value is interpreted as a decimal integer specifying the gzip compression level. .El .It Filter xz .Bl -tag -compact -width indent .It Cm compression-level The value is interpreted as a decimal integer specifying the compression level. .El .It Format mtree .Bl -tag -compact -width indent .It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname Enable a particular keyword in the mtree output. Prefix with an exclamation mark to disable the corresponding keyword. The default is equivalent to .Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . .It Cm all Enables all of the above keywords. .It Cm use-set Enables generation of .Cm /set lines that specify default values for the following files and/or directories. .It Cm indent XXX needs explanation XXX .El .It Format iso9660 - volume metadata These options are used to set standard ISO9660 metadata. .Bl -tag -compact -width indent .It Cm abstract-file Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata as holding the abstract for this volume. Default: none. .It Cm application-id Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata as holding the application identifier for this volume. Default: none. .It Cm biblio-file Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata as holding the bibliography for this volume. Default: none. .It Cm copyright-file Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata as holding the copyright for this volume. Default: none. .It Cm publisher Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata as holding the publisher information for this volume. Default: none. .It Cm volume-id Ns = Ns Ar string The specified string will be used as the Volume Identifier in the ISO9660 metadata. It is limited to 32 bytes. Default: none. .El .It Format iso9660 - boot support These options are used to make an ISO9660 image that can be directly booted on various systems. .Bl -tag -compact -width indent .It Cm boot Ns = Ns Ar filename The file matching this name will be used as the El Torito boot image file. .It Cm boot-catalog Ns = Ns Ar name The name that will be used for the El Torito boot catalog. Default: .Ar boot.catalog .It Cm boot-info-table The boot image file provided by the .Cm boot Ns = Ns Ar filename option will be edited with appropriate boot information in bytes 8 through 64. Default: disabled .It Cm boot-load-seg Ns = Ns Ar hexadecimal-number The load segment for a no-emulation boot image. .It Cm boot-load-size Ns = Ns Ar decimal-number The number of "virtual" 512-byte sectors to be loaded from a no-emulation boot image. Some very old BIOSes can only load very small images, setting this value to 4 will often allow such BIOSes to load the first part of the boot image (which will then need to be intelligent enough to load the rest of itself). This should not be needed unless you are trying to support systems with very old BIOSes. This defaults to the full size of the image. .It Cm boot-type Ns = Ns Ar value Specifies the boot semantics used by the El Torito boot image: If the .Ar value is .Cm fd , then the boot image is assumed to be a bootable floppy image. If the .Ar value is .Cm hd , then the boot image is assumed to be a bootable hard disk image. If the .Ar value is .Cm no-emulation , the boot image is used without floppy or hard disk emulation. If the boot image is exactly 1.2MB, 1.44MB, or 2.88MB, then the default is .Cm fd , otherwise the default is .Cm no-emulation. .El .It Format iso9660 - filename and size extensions Various extensions to the base ISO9660 format. .Bl -tag -compact -width indent .It Cm allow-ldots If enabled, allows filenames to begin with a leading period. If disabled, filenames that begin with a leading period will have that period replaced by an underscore character in the standard ISO9660 namespace. This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm allow-lowercase If enabled, allows filenames to contain lowercase characters. If disabled, filenames will be forced to uppercase. This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm allow-multidot If enabled, allows filenames to contain multiple period characters, in violation of the ISO9660 specification. If disabled, additional periods will be converted to underscore characters. This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm allow-period If enabled, allows filenames to contain trailing period characters, in violation of the ISO9660 specification. If disabled,trailing periods will be converted to underscore characters. This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm allow-pvd-lowercase If enabled, the Primary Volume Descriptor may contain lowercase ASCII characters, in violation of the ISO9660 specification. If disabled, characters will be converted to uppercase ASCII. Default: disabled. .It Cm allow-sharp-tilde If enabled, sharp and tilde characters will be permitted in filenames, in violation if the ISO9660 specification. If disabled, such characters will be converted to underscore characters. Default: disabled. .It Cm allow-vernum If enabled, version numbers will be included with files. If disabled, version numbers will be suppressed, in violation of the ISO9660 standard. This does not impact names stored in the Rockridge or Joliet extension area. Default: enabled. .It Cm iso-level This enables support for file size and file name extensions in the core ISO9660 area. The name extensions specified here do not affect the names stored in the Rockridge or Joliet extension areas. .Bl -tag -compact -width indent .It Cm iso-level=1 The most compliant form of ISO9660 image. Filenames are limited to 8.3 uppercase format, directory names are limited to 8 uppercase characters, files are limited to 4 GiB, the complete ISO9660 image cannot exceed 4 GiB. .It Cm iso-level=2 Filenames are limited to 30 uppercase characters with a 30-character extension, directory names are limited to 30 characters, files are limited to 4 GiB. .It Cm iso-level=3 As with .Cm iso-level=2 , except that files may exceed 4 GiB. .It Cm iso-level=4 As with .Cm iso-level=3 , except that filenames may be up to 193 characters and may include arbitrary 8-bit characters. .El .It Cm joliet Microsoft's Joliet extensions store a completely separate set of directory information about each file. In particular, this information includes Unicode filenames of up to 255 characters. Default: enabled. .It Cm limit-depth If enabled, libarchive will use directory relocation records to ensure that no pathname exceeds the ISO9660 limit of 8 directory levels. If disabled, no relocation will occur. Default: enabled. .It Cm limit-dirs If enabled, libarchive will cause an error if there are more than 65536 directories. If disabled, there is no limit on the number of directories. Default: enabled .It Cm pad If enabled, 300 kiB of zero bytes will be appended to the end of the archive. Default: enabled .It Cm relaxed-filenames If enabled, all 7-bit ASCII characters are permitted in filenames (except lowercase characters unless .Cm allow-lowercase is also specified). This violates ISO9660 standards. This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm rockridge The Rockridge extensions store an additional set of POSIX-style file information with each file, including mtime, atime, ctime, permissions, and long filenames with arbitrary 8-bit characters. These extensions also support symbolic links and other POSIX file types. Default: enabled. .El .It Format iso9660 - zisofs support The zisofs extensions permit each file to be independently compressed using a gzip-compatible compression. This can provide significant size savings, but requires the reading system to have support for these extensions. These extensions are disabled by default. .Bl -tag -compact -width indent .It Cm compression-level Ns = Ns number The compression level used by the deflate compressor. Ranges from 0 (least effort) to 9 (most effort). Default: 6 .It Cm zisofs Synonym for .Cm zisofs=direct . .It Cm zisofs=direct Compress each file in the archive. Unlike .Cm zisofs=indirect , this is handled entirely within libarchive and does not require a separate utility. For best results, libarchive tests each file and will store the file uncompressed if the compression does not actually save any space. In particular, files under 2k will never be compressed. Note that boot image files are never compressed. .It Cm zisofs=indirect Recognizes files that have already been compressed with the .Cm mkzftree utility and sets up the necessary file metadata so that readers will correctly identify these as zisofs-compressed files. .It Cm zisofs-exclude Ns = Ns Ar filename Specifies a filename that should not be compressed when using .Cm zisofs=direct . This option can be provided multiple times to suppress compression on many files. .El .It Format zip .Bl -tag -compact -width indent .It Cm compression The value is either .Dq store or .Dq deflate to indicate how the following entries should be compressed. Note that this setting is ignored for directories, symbolic links, and other special entries. .It Cm experimental This boolean option enables or disables experimental Zip features that may not be compatible with other Zip implementations. .It Cm fakecrc32 This boolean option disables CRC calculations. All CRC fields are set to zero. It should not be used except for testing purposes. .It Cm hdrcharset This sets the character set used for filenames. .It Cm zip64 Zip64 extensions provide additional file size information for entries larger than 4 GiB. They also provide extended file offset and archive size information when archives exceed 4 GiB. By default, the Zip writer selectively enables these extensions only as needed. In particular, if the file size is unknown, the Zip writer will include Zip64 extensions to guard against the possibility that the file might be larger than 4 GiB. .Pp Setting this boolean option will force the writer to use Zip64 extensions even for small files that would not otherwise require them. This is primarily useful for testing. .Pp Disabling this option with .Cm !zip64 will force the Zip writer to avoid Zip64 extensions: It will reject files with size greater than 4 GiB, it will reject any new entries once the total archive size reaches 4 GiB, and it will not use Zip64 extensions for files with unknown size. In particular, this can improve compatibility when generating archives where the entry sizes are not known in advance. .El .El .Sh EXAMPLES The following example creates an archive write handle to create a gzip-compressed ISO9660 format image. The two options here specify that the ISO9660 archive will use .Ar kernel.img as the boot image for El Torito booting, and that the gzip compressor should use the maximum compression level. .Bd -literal -offset indent a = archive_write_new(); archive_write_add_filter_gzip(a); archive_write_set_format_iso9660(a); archive_write_set_options(a, "boot=kernel.img,compression=9"); archive_write_open_filename(a, filename, blocksize); .Ed .\" .Sh ERRORS More detailed error codes and textual descriptions are available from the .Fn archive_errno and .Fn archive_error_string functions. .\" .Sh SEE ALSO .Xr tar 1 , .Xr libarchive 3 , .Xr archive_read_set_options 3 , .Xr archive_write 3 .Sh HISTORY The .Nm libarchive library first appeared in .Fx 5.3 . .Sh AUTHORS .An -nosplit The options support for libarchive was originally implemented by .An Michihiro NAKAJIMA . .Sh BUGS Index: vendor/libarchive/dist/libarchive/libarchive-formats.5 =================================================================== --- vendor/libarchive/dist/libarchive/libarchive-formats.5 (revision 302002) +++ vendor/libarchive/dist/libarchive/libarchive-formats.5 (revision 302003) @@ -1,468 +1,467 @@ .\" 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 AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd March 18, 2012 .Dt LIBARCHIVE-FORMATS 5 .Os .Sh NAME .Nm libarchive-formats .Nd archive formats supported by the libarchive library .Sh DESCRIPTION The .Xr libarchive 3 library reads and writes a variety of streaming archive formats. Generally speaking, all of these archive formats consist of a series of .Dq entries . Each entry stores a single file system object, such as a file, directory, or symbolic link. .Pp The following provides a brief description of each format supported by libarchive, with some information about recognized extensions or limitations of the current library support. Note that just because a format is supported by libarchive does not imply that a program that uses libarchive will support that format. Applications that use libarchive specify which formats they wish to support, though many programs do use libarchive convenience functions to enable all supported formats. .Ss Tar Formats The .Xr libarchive 3 library can read most tar archives. It can write POSIX-standard .Dq ustar and .Dq pax interchange formats as well as v7 tar format and a subset of the legacy GNU tar format. .Pp All tar formats store each entry in one or more 512-byte records. The first record is used for file metadata, including filename, timestamp, and mode information, and the file data is stored in subsequent records. Later variants have extended this by either appropriating undefined areas of the header record, extending the header to multiple records, or by storing special entries that modify the interpretation of subsequent entries. -.Pp .Bl -tag -width indent .It Cm gnutar The .Xr libarchive 3 library can read most GNU-format tar archives. It currently supports the most popular GNU extensions, including modern long filename and linkname support, as well as atime and ctime data. The libarchive library does not support multi-volume archives, nor the old GNU long filename format. It can read GNU sparse file entries, including the new POSIX-based formats. .Pp The .Xr libarchive 3 library can write GNU tar format, including long filename and linkname support, as well as atime and ctime data. .It Cm pax The .Xr libarchive 3 library can read and write POSIX-compliant pax interchange format archives. Pax interchange format archives are an extension of the older ustar format that adds a separate entry with additional attributes stored as key/value pairs immediately before each regular entry. The presence of these additional entries is the only difference between pax interchange format and the older ustar format. The extended attributes are of unlimited length and are stored as UTF-8 Unicode strings. Keywords defined in the standard are in all lowercase; vendors are allowed to define custom keys by preceding them with the vendor name in all uppercase. When writing pax archives, libarchive uses many of the SCHILY keys defined by Joerg Schilling's .Dq star archiver and a few LIBARCHIVE keys. The libarchive library can read most of the SCHILY keys and most of the GNU keys introduced by GNU tar. It silently ignores any keywords that it does not understand. .Pp The pax interchange format converts filenames to Unicode and stores them using the UTF-8 encoding. Prior to libarchive 3.0, libarchive erroneously assumed that the system wide-character routines natively supported Unicode. This caused it to mis-handle non-ASCII filenames on systems that did not satisfy this assumption. .It Cm restricted pax The libarchive library can also write pax archives in which it attempts to suppress the extended attributes entry whenever possible. The result will be identical to a ustar archive unless the extended attributes entry is required to store a long file name, long linkname, extended ACL, file flags, or if any of the standard ustar data (user name, group name, UID, GID, etc) cannot be fully represented in the ustar header. In all cases, the result can be dearchived by any program that can read POSIX-compliant pax interchange format archives. Programs that correctly read ustar format (see below) will also be able to read this format; any extended attributes will be extracted as separate files stored in .Pa PaxHeader directories. .It Cm ustar The libarchive library can both read and write this format. This format has the following limitations: .Bl -bullet -compact .It Device major and minor numbers are limited to 21 bits. Nodes with larger numbers will not be added to the archive. .It Path names in the archive are limited to 255 bytes. (Shorter if there is no / character in exactly the right place.) .It Symbolic links and hard links are stored in the archive with the name of the referenced file. This name is limited to 100 bytes. .It Extended attributes, file flags, and other extended security information cannot be stored. .It Archive entries are limited to 8 gigabytes in size. .El Note that the pax interchange format has none of these restrictions. The ustar format is old and widely supported. It is recommended when compatibility is the primary concern. .It Cm v7 The libarchive library can read and write the legacy v7 tar format. This format has the following limitations: .Bl -bullet -compact .It Only regular files, directories, and symbolic links can be archived. Block and character device nodes, FIFOs, and sockets cannot be archived. .It Path names in the archive are limited to 100 bytes. .It Symbolic links and hard links are stored in the archive with the name of the referenced file. This name is limited to 100 bytes. .It User and group information are stored as numeric IDs; there is no provision for storing user or group names. .It Extended attributes, file flags, and other extended security information cannot be stored. .It Archive entries are limited to 8 gigabytes in size. .El Generally, users should prefer the ustar format for portability as the v7 tar format is both less useful and less portable. .El .Pp The libarchive library also reads a variety of commonly-used extensions to the basic tar format. These extensions are recognized automatically whenever they appear. .Bl -tag -width indent .It Numeric extensions. The POSIX standards require fixed-length numeric fields to be written with some character position reserved for terminators. Libarchive allows these fields to be written without terminator characters. This extends the allowable range; in particular, ustar archives with this extension can support entries up to 64 gigabytes in size. Libarchive also recognizes base-256 values in most numeric fields. This essentially removes all limitations on file size, modification time, and device numbers. .It Solaris extensions Libarchive recognizes ACL and extended attribute records written by Solaris tar. Currently, libarchive only has support for old-style ACLs; the newer NFSv4 ACLs are recognized but discarded. .El .Pp The first tar program appeared in Seventh Edition Unix in 1979. The first official standard for the tar file format was the .Dq ustar (Unix Standard Tar) format defined by POSIX in 1988. POSIX.1-2001 extended the ustar format to create the .Dq pax interchange format. .Ss Cpio Formats The libarchive library can read a number of common cpio variants and can write .Dq odc and .Dq newc format archives. A cpio archive stores each entry as a fixed-size header followed by a variable-length filename and variable-length data. Unlike the tar format, the cpio format does only minimal padding of the header or file data. There are several cpio variants, which differ primarily in how they store the initial header: some store the values as octal or hexadecimal numbers in ASCII, others as binary values of varying byte order and length. .Bl -tag -width indent .It Cm binary The libarchive library transparently reads both big-endian and little-endian variants of the original binary cpio format. This format used 32-bit binary values for file size and mtime, and 16-bit binary values for the other fields. .It Cm odc The libarchive library can both read and write this POSIX-standard format, which is officially known as the .Dq cpio interchange format or the .Dq octet-oriented cpio archive format and sometimes unofficially referred to as the .Dq old character format . This format stores the header contents as octal values in ASCII. It is standard, portable, and immune from byte-order confusion. File sizes and mtime are limited to 33 bits (8GB file size), other fields are limited to 18 bits. .It Cm SVR4/newc The libarchive library can read both CRC and non-CRC variants of this format. The SVR4 format uses eight-digit hexadecimal values for all header fields. This limits file size to 4GB, and also limits the mtime and other fields to 32 bits. The SVR4 format can optionally include a CRC of the file contents, although libarchive does not currently verify this CRC. .El .Pp Cpio first appeared in PWB/UNIX 1.0, which was released within AT&T in 1977. PWB/UNIX 1.0 formed the basis of System III Unix, released outside of AT&T in 1981. This makes cpio older than tar, although cpio was not included in Version 7 AT&T Unix. As a result, the tar command became much better known in universities and research groups that used Version 7. The combination of the .Nm find and .Nm cpio utilities provided very precise control over file selection. Unfortunately, the format has many limitations that make it unsuitable for widespread use. Only the POSIX format permits files over 4GB, and its 18-bit limit for most other fields makes it unsuitable for modern systems. In addition, cpio formats only store numeric UID/GID values (not usernames and group names), which can make it very difficult to correctly transfer archives across systems with dissimilar user numbering. .Ss Shar Formats A .Dq shell archive is a shell script that, when executed on a POSIX-compliant system, will recreate a collection of file system objects. The libarchive library can write two different kinds of shar archives: .Bl -tag -width indent .It Cm shar The traditional shar format uses a limited set of POSIX commands, including .Xr echo 1 , .Xr mkdir 1 , and .Xr sed 1 . It is suitable for portably archiving small collections of plain text files. However, it is not generally well-suited for large archives (many implementations of .Xr sh 1 have limits on the size of a script) nor should it be used with non-text files. .It Cm shardump This format is similar to shar but encodes files using .Xr uuencode 1 so that the result will be a plain text file regardless of the file contents. It also includes additional shell commands that attempt to reproduce as many file attributes as possible, including owner, mode, and flags. The additional commands used to restore file attributes make shardump archives less portable than plain shar archives. .El .Ss ISO9660 format Libarchive can read and extract from files containing ISO9660-compliant CDROM images. In many cases, this can remove the need to burn a physical CDROM just in order to read the files contained in an ISO9660 image. It also avoids security and complexity issues that come with virtual mounts and loopback devices. Libarchive supports the most common Rockridge extensions and has partial support for Joliet extensions. If both extensions are present, the Joliet extensions will be used and the Rockridge extensions will be ignored. In particular, this can create problems with hardlinks and symlinks, which are supported by Rockridge but not by Joliet. .Pp Libarchive reads ISO9660 images using a streaming strategy. This allows it to read compressed images directly (decompressing on the fly) and allows it to read images directly from network sockets, pipes, and other non-seekable data sources. This strategy works well for optimized ISO9660 images created by many popular programs. Such programs collect all directory information at the beginning of the ISO9660 image so it can be read from a physical disk with a minimum of seeking. However, not all ISO9660 images can be read in this fashion. .Pp Libarchive can also write ISO9660 images. Such images are fully optimized with the directory information preceding all file data. This is done by storing all file data to a temporary file while collecting directory information in memory. When the image is finished, libarchive writes out the directory structure followed by the file data. The location used for the temporary file can be changed by the usual environment variables. .Ss Zip format Libarchive can read and write zip format archives that have uncompressed entries and entries compressed with the .Dq deflate algorithm. Other zip compression algorithms are not supported. It can extract jar archives, archives that use Zip64 extensions and self-extracting zip archives. Libarchive can use either of two different strategies for reading Zip archives: a streaming strategy which is fast and can handle extremely large archives, and a seeking strategy which can correctly process self-extracting Zip archives and archives with deleted members or other in-place modifications. .Pp The streaming reader processes Zip archives as they are read. It can read archives of arbitrary size from tape or network sockets, and can decode Zip archives that have been separately compressed or encoded. However, self-extracting Zip archives and archives with certain types of modifications cannot be correctly handled. Such archives require that the reader first process the Central Directory, which is ordinarily located at the end of a Zip archive and is thus inaccessible to the streaming reader. If the program using libarchive has enabled seek support, then libarchive will use this to processes the central directory first. .Pp In particular, the seeking reader must be used to correctly handle self-extracting archives. Such archives consist of a program followed by a regular Zip archive. The streaming reader cannot parse the initial program portion, but the seeking reader starts by reading the Central Directory from the end of the archive. Similarly, Zip archives that have been modified in-place can have deleted entries or other garbage data that can only be accurately detected by first reading the Central Directory. .Ss Archive (library) file format The Unix archive format (commonly created by the .Xr ar 1 archiver) is a general-purpose format which is used almost exclusively for object files to be read by the link editor .Xr ld 1 . The ar format has never been standardised. There are two common variants: the GNU format derived from SVR4, and the BSD format, which first appeared in 4.4BSD. The two differ primarily in their handling of filenames longer than 15 characters: the GNU/SVR4 variant writes a filename table at the beginning of the archive; the BSD format stores each long filename in an extension area adjacent to the entry. Libarchive can read both extensions, including archives that may include both types of long filenames. Programs using libarchive can write GNU/SVR4 format if they provide an entry called .Pa // containing a filename table to be written into the archive before any of the entries. Any entries whose names are not in the filename table will be written using BSD-style long filenames. This can cause problems for programs such as GNU ld that do not support the BSD-style long filenames. .Ss mtree Libarchive can read and write files in .Xr mtree 5 format. This format is not a true archive format, but rather a textual description of a file hierarchy in which each line specifies the name of a file and provides specific metadata about that file. Libarchive can read all of the keywords supported by both the NetBSD and FreeBSD versions of .Xr mtree 8 , although many of the keywords cannot currently be stored in an .Tn archive_entry object. When writing, libarchive supports use of the .Xr archive_write_set_options 3 interface to specify which keywords should be included in the output. If libarchive was compiled with access to suitable cryptographic libraries (such as the OpenSSL libraries), it can compute hash entries such as .Cm sha512 or .Cm md5 from file data being written to the mtree writer. .Pp When reading an mtree file, libarchive will locate the corresponding files on disk using the .Cm contents keyword if present or the regular filename. If it can locate and open the file on disk, it will use that to fill in any metadata that is missing from the mtree file and will read the file contents and return those to the program using libarchive. If it cannot locate and open the file on disk, libarchive will return an error for any attempt to read the entry body. .Ss 7-Zip Libarchive can read and write 7-Zip format archives. TODO: Need more information .Ss CAB Libarchive can read Microsoft Cabinet ( .Dq CAB ) format archives. TODO: Need more information. .Ss LHA TODO: Information about libarchive's LHA support .Ss RAR Libarchive has limited support for reading RAR format archives. Currently, libarchive can read RARv3 format archives which have been either created uncompressed, or compressed using any of the compression methods supported by the RARv3 format. Libarchive can also read self-extracting RAR archives. .Ss Warc Libarchive can read and write .Dq web archives . TODO: Need more information .Ss XAR Libarchive can read and write the XAR format used by many Apple tools. TODO: Need more information .Sh SEE ALSO .Xr ar 1 , .Xr cpio 1 , .Xr mkisofs 1 , .Xr shar 1 , .Xr tar 1 , .Xr zip 1 , .Xr zlib 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 Index: vendor/libarchive/dist/libarchive/libarchive_changes.3 =================================================================== --- vendor/libarchive/dist/libarchive/libarchive_changes.3 (revision 302002) +++ vendor/libarchive/dist/libarchive/libarchive_changes.3 (revision 302003) @@ -1,341 +1,341 @@ .\" Copyright (c) 2011 Tim Kientzle .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd December 23, 2011 .Dt LIBARCHIVE_CHANGES 3 .Os .Sh NAME -.Nm changes in libarchive interface +.Nd changes in libarchive interface .\" .Sh CHANGES IN LIBARCHIVE 3 This page describes user-visible changes in libarchive3, and lists public functions and other symbols changed, deprecated or removed in libarchive3, along with their replacements if any. .Pp .\" .Ss Multiple Filters .\" Libarchive2 permitted a single (input or output) filter active on an archive. Libarchive3 extends this into a variable-length stack. Where .Fn archive_write_set_compression_XXX would replace any existing filter, .Fn archive_write_add_filter_XXX extends the write pipeline with another filter. .\" .Ss Character Set Handling .\" Libarchive2 assumed that the local platform uses .Tn Unicode as the native .Tn wchar_t encoding, which is true on .Tn Windows , modern .Tn Linux , and a few other systems, but is certainly not universal. As a result, pax format archives were written incorrectly on some systems, since pax format requires .Tn UTF-8 and libarchive 2 incorrectly assumed that .Tn wchar_t strings can be easily converted to .Tn UTF-8 . .Pp Libarchive3 uses the standard iconv library to convert between character sets and is introducing the notion of a .Dq default character set for the archive . To support this, .Tn archive_entry objects can now be bound to a particular archive when they are created. The automatic character set conversions performed by .Tn archive_entry objects when reading and writing filenames, usernames, and other strings will now use an appropriate default character set: .Pp If the .Tn archive_entry object is bound to an archive, it will use the default character set for that archive. .Pp The platform default character encoding (as returned by .Fn nl_langinfo CHARSET ) will be used if nothing else is specified. .Pp Libarchive3 also introduces charset options to many of the archive readers and writers to control the character set that will be used for filenames written in those archives. When possible, this will be set automatically based on information in the archive itself. Combining this with the notion of a default character set for the archive should allow you to configure libarchive to read archives from other platforms and have the filenames and other information transparently converted to the character encoding suitable for your application. .\" .Ss Prototype Changes .\" These changes break binary compatibility; libarchive3 has a new shared library version to reflect these changes. The library now uses portable wide types such as .Tn int64_t instead of less-portable types such as .Tn off_t , .Tn gid_t , .Tn uid_t , and .Tn ino_t . .Pp There are a few cases where these changes will affect your source code: .Bl -bullet -width ind .It In some cases, libarchive's wider types will introduce the possibility of truncation: for example, on a system with a 16-bit .Tn uid_t , you risk having uid .Li 65536 be truncated to uid .Li 0 , which can cause serious security problems. .It Typedef function pointer types will be incompatible. For example, if you define custom skip callbacks, you may have to use code similar to the following if you want to support building against libarchive2 and libarchive3: .Bd -literal #if ARCHIVE_VERSION_NUMBER < 3000000 typedef off_t myoff_t; #else typedef int64_t myoff_t; #endif myoff_t my_skip_function(struct archive *a, void *v, myoff_t o) { ... implementation ... } .Ed .El .Pp Affected functions: .Pp .Bl -bullet -compact .It .Xo .Fn archive_entry_gid , .Fn archive_entry_set_gid .Xc .It .Xo .Fn archive_entry_uid , .Fn archive_entry_set_uid .Xc .It .Xo .Fn archive_entry_ino , .Fn archive_entry_set_ino .Xc .It .Xo .Fn archive_read_data_block , .Fn archive_write_data_block .Xc .It .Xo .Fn archive_read_disk_gname , .Fn archive_read_disk_uname .Xc .It .Xo .Fn archive_read_disk_set_gname_lookup , .Fn archive_read_disk_set_group_lookup , .Fn archive_read_disk_set_uname_lookup , .Fn archive_read_disk_set_user_lookup .Xc .It .Fn archive_skip_callback .It .Xo .Fn archive_read_extract_set_skip_file , .Fn archive_write_disk_set_skip_file , .Fn archive_write_set_skip_file .Xc .It .Xo .Fn archive_write_disk_set_group_lookup , .Fn archive_write_disk_set_user_lookup .Xc .El .Pp Where these functions or their arguments took or returned .Tn gid_t , .Tn ino_t , .Tn off_t , or .Tn uid_t they now take or return .Tn int64_t or equivalent. .\" .Ss Deprecated Symbols .\" Symbols deprecated in libarchive3 will be removed in libarchive4. These symbols, along with their replacements if any, are listed below: .\" .Bl -tag -width ind .It Fn archive_position_compressed , Fn archive_position_uncompressed .Fn archive_filter_bytes .It Fn archive_compression .Fn archive_filter_code .It Fn archive_compression_name .Fn archive_filter_name .It Fn archive_read_finish , Fn archive_write_finish .Fn archive_read_free , .Fn archive_write_free .It Fn archive_read_open_file , Fn archive_write_open_file .Fn archive_read_open_filename , .Fn archive_write_open_filename .It Fn archive_read_support_compression_all .\" archive_read_support_compression_* -> archive_read_support_filter_* .Fn archive_read_support_filter_all .It Fn archive_read_support_compression_bzip2 .Fn archive_read_support_filter_bzip2 .It Fn archive_read_support_compression_compress .Fn archive_read_support_filter_compress .It Fn archive_read_support_compression_gzip .Fn archive_read_support_filter_gzip .It Fn archive_read_support_compression_lzip .Fn archive_read_support_filter_lzip .It Fn archive_read_support_compression_lzma .Fn archive_read_support_filter_lzma .It Fn archive_read_support_compression_none .Fn archive_read_support_filter_none .It Fn archive_read_support_compression_program .Fn archive_read_support_filter_program .It Fn archive_read_support_compression_program_signature .Fn archive_read_support_filter_program_signature .It Fn archive_read_support_compression_rpm .Fn archive_read_support_filter_rpm .It Fn archive_read_support_compression_uu .Fn archive_read_support_filter_uu .It Fn archive_read_support_compression_xz .Fn archive_read_support_filter_xz .\" archive_write_set_compression_* -> archive_write_add_filter_* .It Fn archive_write_set_compression_bzip2 .Fn archive_write_add_filter_bzip2 .It Fn archive_write_set_compression_compress .Fn archive_write_add_filter_compress .It Fn archive_write_set_compression_gzip .Fn archive_write_add_filter_gzip .It Fn archive_write_set_compression_lzip .Fn archive_write_add_filter_lzip .It Fn archive_write_set_compression_lzma .Fn archive_write_add_filter_lzma .It Fn archive_write_set_compression_none .Fn archive_write_add_filter_none .It Fn archive_write_set_compression_program .Fn archive_write_add_filter_program .It Fn archive_write_set_compression_filter .Fn archive_write_add_filter_filter .El .\" .Ss Removed Symbols .\" These symbols, listed below along with their replacements if any, were deprecated in libarchive2, and are not part of libarchive3. .\" .Bl -tag -width ind .It Fn archive_api_feature .Fn archive_version_number .It Fn archive_api_version .Fn archive_version_number .It Fn archive_version .Fn archive_version_string .It Fn archive_version_stamp .Fn archive_version_number .It Fn archive_read_set_filter_options .Fn archive_read_set_options or .Fn archive_read_set_filter_option .It Fn archive_read_set_format_options .Fn archive_read_set_options or .Fn archive_read_set_format_option .It Fn archive_write_set_filter_options .Fn archive_write_set_options or .Fn archive_write_set_filter_option .It Fn archive_write_set_format_options .Fn archive_write_set_options or .Fn archive_write_set_format_option .It Dv ARCHIVE_API_FEATURE .Dv ARCHIVE_VERSION_NUMBER .It Dv ARCHIVE_API_VERSION .Dv ARCHIVE_VERSION_NUMBER .It Dv ARCHIVE_VERSION_STAMP .Dv ARCHIVE_VERSION_NUMBER .It Dv ARCHIVE_LIBRARY_VERSION .Dv ARCHIVE_VERSION_STRING .\" .It Dv ARCHIVE_COMPRESSION_NONE .Dv ARCHIVE_FILTER_NONE .It Dv ARCHIVE_COMPRESSION_GZIP .Dv ARCHIVE_FILTER_GZIP .It Dv ARCHIVE_COMPRESSION_BZIP2 .Dv ARCHIVE_FILTER_BZIP2 .It Dv ARCHIVE_COMPRESSION_COMPRESS .Dv ARCHIVE_FILTER_COMPRESS .It Dv ARCHIVE_COMPRESSION_PROGRAM .Dv ARCHIVE_FILTER_PROGRAM .It Dv ARCHIVE_COMPRESSION_LZMA .Dv ARCHIVE_FILTER_LZMA .It Dv ARCHIVE_COMPRESSION_XZ .Dv ARCHIVE_FILTER_XZ .It Dv ARCHIVE_COMPRESSION_UU .Dv ARCHIVE_FILTER_UU .It Dv ARCHIVE_COMPRESSION_RPM .Dv ARCHIVE_FILTER_RPM .It Dv ARCHIVE_COMPRESSION_LZIP .Dv ARCHIVE_FILTER_LZIP .\" .It Dv ARCHIVE_BYTES_PER_RECORD .Li 512 .It Dv ARCHIVE_DEFAULT_BYTES_PER_BLOCK .Li 10240 .El .Sh SEE ALSO .Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , .Xr archive_read_set_options 3 , .Xr archive_write 3 , .Xr archive_write_filter 3 , .Xr archive_write_format 3 , .Xr archive_write_set_options 3 , .Xr archive_util 3 Index: vendor/libarchive/dist/libarchive/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/libarchive/test/CMakeLists.txt (revision 302002) +++ vendor/libarchive/dist/libarchive/test/CMakeLists.txt (revision 302003) @@ -1,299 +1,300 @@ ############################################ # # 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_solaris_pax_sparse.c test_compat_solaris_tar_acl.c test_compat_tar_hardlink.c test_compat_uudecode.c test_compat_uudecode_large.c test_compat_xz.c test_compat_zip.c test_empty_write.c test_entry.c test_entry_strmode.c test_extattr_freebsd.c test_filter_count.c test_fuzz.c test_gnutar_filename_encoding.c test_link_resolver.c test_open_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_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_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_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_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}) ADD_DEPENDENCIES(run_all_tests run_libarchive_test) ENDIF(ENABLE_TEST) Index: vendor/libarchive/dist/libarchive/test/main.c =================================================================== --- vendor/libarchive/dist/libarchive/test/main.c (revision 302002) +++ vendor/libarchive/dist/libarchive/test/main.c (revision 302003) @@ -1,2968 +1,2991 @@ /* * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include "test_utils.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include /* * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. * TODO: Move this into a separate configuration header, have all test * suites share one copy of this file. */ __FBSDID("$FreeBSD: head/lib/libarchive/test/main.c 201247 2009-12-30 05:59:21Z kientzle $"); #define KNOWNREF "test_compat_gtar_1.tar.uu" #define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */ #undef PROGRAM /* Testing a library, not a program. */ #define LIBRARY "libarchive" #define EXTRA_DUMP(x) archive_error_string((struct archive *)(x)) #define EXTRA_ERRNO(x) archive_errno((struct archive *)(x)) #define EXTRA_VERSION archive_version_details() /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, flags); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalide sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not leagal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %d bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n", fn); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %lld.%09lld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)pathname; /* UNUSED */ (void)contents; /* UNUSED */ assertion_count(file, line); /* Windows sort-of has real symlinks, but they're only usable * by privileged users and are crippled even then, so there's * really not much point in bothering with this. */ return (0); #else char buff[300]; struct stat st; ssize_t linklen; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff)); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents) { if (is_symlink(file, line, path, contents)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) return (1); #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); return (0); } } close(fd); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Create a symlink and report any failures. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto) { #if defined(_WIN32) && !defined(__CYGWIN__) int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Set nodump, report failures. */ int assertion_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 -d -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int canNodump(void) { const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int canNodump(void) { const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); close(fd); if (flags & EXT2_NODUMP_FL) return (1); return (0); } #else int canNodump() { return (0); } #endif /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { char workdir[1024]; char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { - char tried[512] = { '\0' }; - char buff[128]; - char *pwd, *p; + size_t tried_size, buff_size; + char *buff, *tried, *pwd = NULL, *p = NULL; +#ifdef PATH_MAX + buff_size = PATH_MAX; +#else + buff_size = 8192; +#endif + buff = calloc(buff_size, 1); + if (buff == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + + /* Allocate a buffer to hold the various directories we checked. */ + tried_size = buff_size * 2; + tried = calloc(tried_size, 1); + if (tried == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; - snprintf(buff, sizeof(buff), "%s", d); + snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ - snprintf(buff, sizeof(buff), "%s", pwd); + snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd); + snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); + snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM_ALIAS); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, sizeof(buff), "%s", pwd + 8); + snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd + 8); + snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); - return strdup(buff); + free(tried); + + /* Copy result into a fresh buffer to reduce memory usage. */ + p = strdup(buff); + free(buff); + return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: vendor/libarchive/dist/libarchive/test/test_write_format_gnutar_filenames.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_write_format_gnutar_filenames.c (nonexistent) +++ vendor/libarchive/dist/libarchive/test/test_write_format_gnutar_filenames.c (revision 302003) @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2016 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$"); + +/* + * Inspired by Github issue #682, which reported that gnutar filenames + * of exactly 512 bytes weren't getting written correctly. + * + * This writes a filename of every length from 1 to 2000 bytes and + * reads back to verify it. + */ + +static char filename[1024]; + +DEFINE_TEST(test_write_format_gnutar_filenames) +{ + size_t buffsize = 1000000; + char *buff; + struct archive_entry *ae, *template; + struct archive *a; + size_t used; + + buff = malloc(buffsize); /* million bytes of work area */ + assert(buff != NULL); + + /* Create a template entry. */ + assert((template = archive_entry_new()) != NULL); + archive_entry_set_atime(template, 2, 20); + archive_entry_set_birthtime(template, 3, 30); + archive_entry_set_ctime(template, 4, 40); + archive_entry_set_mtime(template, 5, 50); + archive_entry_set_mode(template, S_IFREG | 0755); + archive_entry_set_size(template, 8); + + for (int i = 0; i < 2000; ++i) { + filename[i] = 'a'; + filename[i + 1] = '\0'; + archive_entry_copy_pathname(template, filename); + + /* Write a one-item gnutar format archive. */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_gnutar(a)); + assertA(0 == archive_write_add_filter_none(a)); + assertA(0 == archive_write_open_memory(a, buff, buffsize, &used)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, template)); + assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); + + + /* Read back and verify the filename. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, 0, archive_read_support_format_all(a)); + assertEqualIntA(a, 0, archive_read_support_filter_all(a)); + assertEqualIntA(a, 0, archive_read_open_memory(a, buff, used)); + + assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); + assertEqualString(filename, archive_entry_pathname(ae)); + 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)); + } + + archive_entry_free(template); + + free(buff); +} + + +DEFINE_TEST(test_write_format_gnutar_linknames) +{ + size_t buffsize = 1000000; + char *buff; + struct archive_entry *ae, *template; + struct archive *a; + size_t used; + + buff = malloc(buffsize); /* million bytes of work area */ + assert(buff != NULL); + + /* Create a template entry. */ + assert((template = archive_entry_new()) != NULL); + archive_entry_set_atime(template, 2, 20); + archive_entry_set_birthtime(template, 3, 30); + archive_entry_set_ctime(template, 4, 40); + archive_entry_set_mtime(template, 5, 50); + archive_entry_set_mode(template, S_IFLNK | 0755); + archive_entry_copy_pathname(template, "link"); + + for (int i = 0; i < 2000; ++i) { + filename[i] = 'a'; + filename[i + 1] = '\0'; + archive_entry_copy_symlink(template, filename); + + /* Write a one-item gnutar format archive. */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_gnutar(a)); + assertA(0 == archive_write_add_filter_none(a)); + assertA(0 == archive_write_open_memory(a, buff, buffsize, &used)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, template)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); + + + /* Read back and verify the filename. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, 0, archive_read_support_format_all(a)); + assertEqualIntA(a, 0, archive_read_support_filter_all(a)); + assertEqualIntA(a, 0, archive_read_open_memory(a, buff, used)); + + assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); + assertEqualString("link", archive_entry_pathname(ae)); + assertEqualString(filename, archive_entry_symlink(ae)); + 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)); + } + + archive_entry_free(template); + + free(buff); +} Property changes on: vendor/libarchive/dist/libarchive/test/test_write_format_gnutar_filenames.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/tar/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/tar/test/CMakeLists.txt (revision 302002) +++ vendor/libarchive/dist/tar/test/CMakeLists.txt (revision 302003) @@ -1,106 +1,107 @@ ############################################ # # How to build bsdtar_test # ############################################ IF(ENABLE_TAR AND ENABLE_TEST) SET(bsdtar_test_SOURCES ../../test_utils/test_utils.c main.c test.h test_0.c test_basic.c test_copy.c test_empty_mtree.c test_extract_tar_Z.c test_extract_tar_bz2.c test_extract_tar_grz.c test_extract_tar_gz.c test_extract_tar_lrz.c test_extract_tar_lz.c test_extract_tar_lz4.c test_extract_tar_lzma.c test_extract_tar_lzo.c test_extract_tar_xz.c test_format_newc.c test_help.c test_leading_slash.c + test_missing_file.c test_option_C_upper.c test_option_H_upper.c test_option_L_upper.c test_option_O_upper.c test_option_T_upper.c test_option_U_upper.c test_option_X_upper.c test_option_a.c test_option_b.c test_option_b64encode.c test_option_exclude.c test_option_gid_gname.c test_option_grzip.c test_option_j.c test_option_k.c test_option_keep_newer_files.c test_option_lrzip.c test_option_lz4.c test_option_lzma.c test_option_lzop.c test_option_n.c test_option_newer_than.c test_option_nodump.c test_option_older_than.c test_option_passphrase.c test_option_q.c test_option_r.c test_option_s.c test_option_uid_uname.c test_option_uuencode.c test_option_xz.c test_option_z.c test_patterns.c test_print_longpath.c test_stdio.c test_strip_components.c test_symlink_dir.c test_version.c test_windows.c ) # # Register target # ADD_EXECUTABLE(bsdtar_test ${bsdtar_test_SOURCES}) SET_PROPERTY(TARGET bsdtar_test PROPERTY COMPILE_DEFINITIONS LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${bsdtar_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 bsdtar_${_testname} COMMAND bsdtar_test -vv -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) # Experimental new test handling ADD_CUSTOM_TARGET(run_bsdtar_test COMMAND bsdtar_test -p $ -r ${CMAKE_CURRENT_SOURCE_DIR}) ADD_DEPENDENCIES(run_bsdtar_test bsdtar) ADD_DEPENDENCIES(run_all_tests run_bsdtar_test) ENDIF(ENABLE_TAR AND ENABLE_TEST) Index: vendor/libarchive/dist/tar/test/main.c =================================================================== --- vendor/libarchive/dist/tar/test/main.c (revision 302002) +++ vendor/libarchive/dist/tar/test/main.c (revision 302003) @@ -1,2970 +1,2993 @@ /* * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include "test_utils.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include /* * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. * TODO: Move this into a separate configuration header, have all test * suites share one copy of this file. */ __FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.6 2008/11/05 06:40:53 kientzle Exp $"); #define KNOWNREF "test_patterns_2.tar.uu" #define ENVBASE "BSDTAR" /* Prefix for environment variables. */ #define PROGRAM "bsdtar" /* Name of program being tested. */ #define PROGRAM_ALIAS "tar" /* Generic alias for program */ #undef LIBRARY /* Not testing a library. */ #undef EXTRA_DUMP /* How to dump extra data */ #undef EXTRA_ERRNO /* How to dump errno */ /* How to generate extra version info. */ #define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, flags); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalide sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not leagal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %d bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n", fn); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %lld.%09lld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)pathname; /* UNUSED */ (void)contents; /* UNUSED */ assertion_count(file, line); /* Windows sort-of has real symlinks, but they're only usable * by privileged users and are crippled even then, so there's * really not much point in bothering with this. */ return (0); #else char buff[300]; struct stat st; ssize_t linklen; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff)); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents) { if (is_symlink(file, line, path, contents)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) return (1); #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); return (0); } } close(fd); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Create a symlink and report any failures. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto) { #if defined(_WIN32) && !defined(__CYGWIN__) int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Set nodump, report failures. */ int assertion_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 -d -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int canNodump(void) { const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); return (0); } #elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ && defined(EXT2_NODUMP_FL) int canNodump(void) { const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); flags |= EXT2_NODUMP_FL; r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); if (r < 0) return (0); close(fd); if (flags & EXT2_NODUMP_FL) return (1); return (0); } #else int canNodump() { return (0); } #endif /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { char workdir[1024]; char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { - char tried[512] = { '\0' }; - char buff[128]; - char *pwd, *p; + size_t tried_size, buff_size; + char *buff, *tried, *pwd = NULL, *p = NULL; +#ifdef PATH_MAX + buff_size = PATH_MAX; +#else + buff_size = 8192; +#endif + buff = calloc(buff_size, 1); + if (buff == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + + /* Allocate a buffer to hold the various directories we checked. */ + tried_size = buff_size * 2; + tried = calloc(tried_size, 1); + if (tried == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; - snprintf(buff, sizeof(buff), "%s", d); + snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ - snprintf(buff, sizeof(buff), "%s", pwd); + snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd); + snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); + snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) - snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM_ALIAS); + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, sizeof(buff), "%s", pwd + 8); + snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); - snprintf(buff, sizeof(buff), "%s/test", pwd + 8); + snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; - strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); - strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); - return strdup(buff); + free(tried); + + /* Copy result into a fresh buffer to reduce memory usage. */ + p = strdup(buff); + free(buff); + return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: vendor/libarchive/dist/tar/test/test_missing_file.c =================================================================== --- vendor/libarchive/dist/tar/test/test_missing_file.c (nonexistent) +++ vendor/libarchive/dist/tar/test/test_missing_file.c (revision 302003) @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2016 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_missing_file) +{ + assertMakeFile("file1", 0644, "file1"); + assertMakeFile("file2", 0644, "file2"); + assert(0 == systemf("%s -cf archive.tar file1 file2 2>stderr1", testprog)); + assertEmptyFile("stderr1"); + assert(0 != systemf("%s -cf archive.tar file1 file2 file3 2>stderr2", testprog)); + assert(0 != systemf("%s -cf archive.tar 2>stderr3", testprog)); + assert(0 != systemf("%s -cf archive.tar file3 2>stderr4", testprog)); +} Property changes on: vendor/libarchive/dist/tar/test/test_missing_file.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/tar/write.c =================================================================== --- vendor/libarchive/dist/tar/write.c (revision 302002) +++ vendor/libarchive/dist/tar/write.c (revision 302003) @@ -1,1051 +1,1051 @@ /*- * 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) { char *p; /* Set default write options. */ p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + strlen(writer_options) + 1); 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, writer_options); 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) { char *p; /* Set default write options. */ p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + strlen(reader_options) + 1); 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); 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 cotents * 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) { + if (r == ARCHIVE_FATAL || r == ARCHIVE_FAILED) { bsdtar->return_value = 1; 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"); */ }