pax_global_header00006660000000000000000000000064147670511260014523gustar00rootroot0000000000000052 comment=066c5ec8f63b68f1648f20a21178fdd974dbb22b tarsnap-1.0.41/000077500000000000000000000000001476705112600132565ustar00rootroot00000000000000tarsnap-1.0.41/.autom4te.cfg000066400000000000000000000002671476705112600155640ustar00rootroot00000000000000## ------------------ ## ## User Preferences. ## ## ------------------ ## begin-language: "Autoconf-without-aclocal-m4" args: --no-cache end-language: "Autoconf-without-aclocal-m4" tarsnap-1.0.41/.github/000077500000000000000000000000001476705112600146165ustar00rootroot00000000000000tarsnap-1.0.41/.github/workflows/000077500000000000000000000000001476705112600166535ustar00rootroot00000000000000tarsnap-1.0.41/.github/workflows/compile.yml000066400000000000000000000113301476705112600210240ustar00rootroot00000000000000name: Compile & test # Run whenever we push to any branch other than [coverity-scan], or if we # manually trigger a workflow. on: push: branches: - '**' - '!coverity-scan' workflow_dispatch: env: # Shared variables amongst all projects / platforms / compilers. CFLAGS_ALL: -std=c99 -O2 CFLAGS_CLANG_LIBCPERCIVA: -Wall -Wextra -Werror -Weverything -Wno-#warnings -Wno-pedantic -Wno-padded -Wno-format-nonliteral -Wno-disabled-macro-expansion -Wno-missing-noreturn -Wno-reserved-id-macro -Wno-unused-macros -Wno-documentation-unknown-command CFLAGS_GCC_LIBCPERCIVA: -Wall -Wextra -Werror -Wpedantic -pedantic-errors -Wno-clobbered # Variables for specific projects / platforms / compilers. LIBARCHIVE_OBJECTS: tar/tarsnap-bsdtar.o tar/tarsnap-getdate.o tar/tarsnap-read.o tar/tarsnap-subst.o tar/tarsnap-tree.o tar/tarsnap-util.o tar/tarsnap-write.o libarchive/libarchive.a CFLAGS_CLANG_PROJECT: -Wno-undef -Wno-implicit-fallthrough -Wno-reserved-identifier CFLAGS_GCC_PROJECT: -Wno-cpp # Needed for major() / minor() / makedev() thing. CFLAGS_LIBARCHIVE_GCC: -Wno-error CFLAGS_OSX: -Wno-poison-system-directories -Wno-deprecated-declarations LDFLAGS_OSX: jobs: Ubuntu: name: Ubuntu runs-on: ubuntu-22.04 steps: - name: Update apt-get run: sudo apt-get update - name: Install software run: sudo apt-get install --no-install-recommends valgrind e2fslibs-dev autoconf-archive - name: Checkout code uses: actions/checkout@v4 - name: autoreconf run: autoreconf -i - name: configure with clang env: CC: clang-13 CFLAGS: ${{ env.CFLAGS_ALL }} run: ./configure - name: Compile libarchive with clang env: CC: clang-13 # We need to use more permissive CFLAGS for libarchive code. CFLAGS: ${{ env.CFLAGS_ALL }} run: make ${{ env.LIBARCHIVE_OBJECTS }} - name: Compile with clang env: CC: clang-13 CFLAGS: ${{ env.CFLAGS_ALL }} ${{ env.CFLAGS_CLANG_LIBCPERCIVA }} ${{ env.CFLAGS_CLANG_PROJECT }} # make(1) doesn't automatically override the CFLAGS macro set inside # Makefile with the environment variable. run: make CFLAGS="${{ env.CFLAGS }}" - name: Test clang binaries env: USE_VALGRIND: 1 run: make test VERBOSE=1 - name: Clean run: make clean - name: configure with gcc env: CC: gcc-10 CFLAGS: ${{ env.CFLAGS_ALL }} run: ./configure - name: Compile libarchive with gcc env: CC: gcc-10 # We need to use more permissive CFLAGS for libarchive code. CFLAGS: ${{ env.CFLAGS_ALL }} ${{ env.CFLAGS_LIBARCHIVE_GCC }} run: make ${{ env.LIBARCHIVE_OBJECTS }} - name: Compile with gcc env: CC: gcc-10 CFLAGS: ${{ env.CFLAGS_ALL }} ${{ env.CFLAGS_GCC_LIBCPERCIVA }} ${{ env.CFLAGS_GCC_PROJECT }} # make(1) doesn't automatically override the CFLAGS macro set inside # Makefile with the environment variable. run: make CFLAGS="${{ env.CFLAGS }}" - name: Test gcc binaries env: USE_VALGRIND: 1 run: make test VERBOSE=1 - name: Check for untracked files run: test -z "$(git status --porcelain=v1)" macOS: name: macOS runs-on: macOS-13 steps: - name: Checkout code uses: actions/checkout@v4 - name: Install software run: brew install automake - name: autoreconf run: autoreconf -i - name: configure with clang env: CC: clang CFLAGS: ${{ env.CFLAGS_ALL }} ${{ env.CFLAGS_OSX }} LDFLAGS: ${{ env.LDFLAGS_OSX }} run: ./configure - name: Compile libarchive with clang env: CC: clang # We need to use more permissive CFLAGS for libarchive code. CFLAGS: ${{ env.CFLAGS_ALL }} run: make ${{ env.LIBARCHIVE_OBJECTS }} - name: Compile with clang env: CC: clang CFLAGS: ${{ env.CFLAGS_ALL }} ${{ env.CFLAGS_CLANG_LIBCPERCIVA }} ${{ env.CFLAGS_CLANG_PROJECT }} ${{ env.CFLAGS_OSX }} LDFLAGS: ${{ env.LDFLAGS_OSX }} # make(1) doesn't automatically override the CFLAGS macro set inside # Makefile with the environment variable. run: make CFLAGS="${{ env.CFLAGS }}" - name: Tests clang binaries run: make test VERBOSE=1 tarsnap-1.0.41/.gitignore000066400000000000000000000013721476705112600152510ustar00rootroot00000000000000# development tools /devel-tools/ # autotools-generated files /Makefile.in /aclocal.m4 /config.aux/ /config.h.in /config.h.in~ /config.log /configure /configure~ /confinc /confmf # Standard out-of-tree build directory /build/ # build-generated files *.a *.o .deps .dirstamp /Makefile /a.out.dSYM /apisupport-config.h /config.h /config.status /cpusupport-config.h /stamp-h1 /tests-output/ /tests-valgrind/ /tests/valgrind/potential-memleaks # installed files /keygen/tarsnap-keygen.1 /keymgmt/tarsnap-keymgmt.1 /keyregen/tarsnap-keyregen.1 /recrypt/tarsnap-recrypt.1 /tar/tarsnap.1 /tar/tarsnap.1-mdoc /tar/tarsnap.conf.5 /tar/tarsnap.conf.5-mdoc /tarsnap /tarsnap-keygen /tarsnap-keymgmt /tarsnap-keyregen /tarsnap-recrypt # python cache /tools/__pycache__ tarsnap-1.0.41/BUILDING000066400000000000000000000065141476705112600144040ustar00rootroot00000000000000Installing ---------- To build and install tarsnap, run: ./configure make make install Tarsnap should build and run on most IEEE Std 1003.1 (POSIX) compliant systems which 1. Includes the Software Development Utilities option, 2. Has OpenSSL available via -lcrypto and #include , 3. Has zlib available via -lz and #include , 4. Provides /dev/urandom, and 5. (Linux only) has the header (which is *not* the same as the header file. Building from git ----------------- Normal users should only use the signed tarballs from https://tarsnap.com, but for experimental development, use: autoreconf -i ./configure make - In order to support the `AX_CFLAGS_WARN_ALL` autoconf directive, you will need to install the autoconf archive. On Debian, use the `autoconf-archive` package; on FreeBSD, use `devel/autoconf-archive`. - If you receive this message: configure.ac:...: error: possibly undefined macro: AC_MSG_WARN then you need `pkg-config`. On Debian, use the `pkg-config` package; on FreeBSD, use `devel/pkgconf`. Platform-specific notes ----------------------- - On Debian, Ubuntu, and related Linux distributions, the minimal set of dependencies is: sudo apt-get install gcc libc6-dev make libssl-dev zlib1g-dev e2fslibs-dev To enable all optional features, install: sudo apt-get install bash-completion libacl1-dev libattr1-dev libbz2-dev liblzma-dev pkg-config - On RedHat, CentOS, SuSE, Amazon Linux, and related Linux distributions: sudo yum install gcc make glibc-devel openssl-devel zlib-devel e2fsprogs-devel To enable all optional features, install: sudo yum install bzip2-devel libacl-devel libattr-devel xz-devel pkgconf bash-completion - on MacOS X: in addition to the normal build toolchain (probably installed via XCode), you need the OpenSSL development library. This could be installed manually, or via a package manager: Homebrew: brew install openssl MacPorts: sudo port install openssl If you installed OpenSSL via homebrew, ./configure should find the right directories. If it doesn't, or if you installed it another way, you might need to specify the directory with: ./configure CPPFLAGS="-I/PATH_TO/openssl/include" LDFLAGS="-L/PATH_TO/opt/openssl/lib" Alternatively, you may wish to add these lines to your $HOME/.profile: export CPPFLAGS="-I/PATH_TO/openssl/include $CPPFLAGS" export LDFLAGS="-I/PATH_TO/openssl/lib $LDFLAGS" (And then open a new terminal window.) Whichever method you choose, replace "/PATH_TO/" with the actual path to your OpenSSL installation. - on Solaris: there are no problems compiling with cc, c99, or gcc. To compile with clang, please use: ./configure CC=clang CFLAGS="-U__EXTENSIONS__" make and ignore warning messages about _XOPEN_SOURCE=600. Packaging notes --------------- Optional shell completion scripts: - `--with-bash-completion-dir[=DIR]`, with the DIR specified explicitly or left blank. If `DIR` is left blank, it attempts to use `pkg-config` and `bash-completion >= 2.0` to determine where to put the bash completion scripts. If your system does not match those requirements, please specify `DIR` explicitly. - `--with-zsh-completion-dir=DIR`, with the DIR specified explicitly. tarsnap-1.0.41/COPYING000066400000000000000000000021611476705112600143110ustar00rootroot00000000000000Unless specified otherwise in individual files or directories, the contents of this package is covered by the following copyright, license, and disclaimer: Copyright 2006 - 2025 Tarsnap Backup Inc. All rights reserved. Redistribution and use in source and binary forms, without modification, is permitted for the sole purpose of using the "tarsnap" backup service provided by Tarsnap Backup Inc. 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. tarsnap-1.0.41/Makefile.am000066400000000000000000000651651476705112600153270ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in AUTOMAKE_OPTIONS= foreign subdir-objects # # What to build and install # noinst_LIBRARIES= \ lib/libcperciva_aesni.a \ lib/libcperciva_arm_aes.a \ lib/libcperciva_arm_sha256.a \ lib/libcperciva_cpusupport_detect.a \ lib/libcperciva_crypto_aes.a \ lib/libcperciva_rdrand.a \ lib/libcperciva_shani.a \ lib/libtarsnap.a \ lib/libtarsnap_rsa.a \ lib/libtarsnap_sse2.a \ libarchive/libarchive.a bin_PROGRAMS= \ tarsnap \ tarsnap-keygen \ tarsnap-keymgmt \ tarsnap-keyregen \ tarsnap-recrypt noinst_PROGRAMS= \ tests/valgrind/potential-memleaks man_MANS= \ $(tarsnap_keygen_man_MANS) \ $(tarsnap_keymgmt_man_MANS) \ $(tarsnap_keyregen_man_MANS) \ $(tarsnap_man_MANS) \ $(tarsnap_recrypt_man_MANS) CLEANFILES= $(man_MANS) # # Libarchive headers, source, etc. # # libarchive_libarchive_a_SOURCES= \ libarchive/archive.h \ libarchive/archive_check_magic.c \ 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_private.h \ libarchive/archive_entry_stat.c \ libarchive/archive_entry_strmode.c \ libarchive/archive_platform.h \ libarchive/archive_private.h \ libarchive/archive_read.c \ libarchive/archive_read_data_into_fd.c \ libarchive/archive_read_disk.c \ libarchive/archive_read_disk_entry_from_file.c \ libarchive/archive_read_disk_private.h \ libarchive/archive_read_disk_set_standard_lookup.c \ libarchive/archive_read_extract.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_support_compression_all.c \ libarchive/archive_read_support_compression_bzip2.c \ libarchive/archive_read_support_compression_compress.c \ libarchive/archive_read_support_compression_gzip.c \ libarchive/archive_read_support_compression_none.c \ libarchive/archive_read_support_compression_program.c \ libarchive/archive_read_support_compression_xz.c \ libarchive/archive_read_support_format_all.c \ libarchive/archive_read_support_format_ar.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_mtree.c \ libarchive/archive_read_support_format_tar.c \ libarchive/archive_read_support_format_zip.c \ libarchive/archive_string.c \ libarchive/archive_string.h \ libarchive/archive_string_sprintf.c \ libarchive/archive_util.c \ libarchive/archive_virtual.c \ libarchive/archive_write.c \ libarchive/archive_write_disk.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_set_compression_none.c \ libarchive/archive_write_set_format_pax.c \ libarchive/archive_write_set_format_ustar.c \ libarchive/config_freebsd.h \ libarchive/config_windows.h \ libarchive/filter_fork.c \ libarchive/filter_fork.h if INC_WINDOWS_FILES libarchive_libarchive_a_SOURCES+= \ libarchive/archive_windows.c \ libarchive/archive_windows.h \ libarchive/filter_fork_windows.c endif libarchive_libarchive_a_CPPFLAGS=-I$(top_builddir)/libarchive # # Tarsnap internal library code # # # Don't include crypto_aesctr_shared.c in this list, as it should be included # directly into other translation unit(s), and not compiled on its own. lib_libtarsnap_a_SOURCES= \ lib-platform/crypto/crypto_scrypt.c \ lib-platform/crypto/crypto_scrypt.h \ lib-platform/network/tsnetwork_cork.c \ lib-platform/network/tsnetwork_cork.h \ lib-platform/platform.h \ lib-platform/util/fileutil.c \ lib-platform/util/fileutil.h \ lib-platform/util/memlimit.c \ lib-platform/util/memlimit.h \ lib-platform/util/ts_getfstype.c \ lib-platform/util/ts_getfstype.h \ lib/crypto/crypto.h \ lib/crypto/crypto_compat.h \ lib/crypto/crypto_file.c \ lib/crypto/crypto_hash.c \ lib/crypto/crypto_internal.h \ lib/crypto/crypto_keys_init.c \ lib/crypto/crypto_keys_server.c \ lib/crypto/crypto_passwd_to_dh.c \ lib/crypto/crypto_scrypt_smix.c \ lib/crypto/crypto_scrypt_smix.h \ lib/crypto/crypto_scrypt_smix_sse2.h \ lib/crypto/crypto_session.c \ lib/datastruct/patricia.c \ lib/datastruct/patricia.h \ lib/datastruct/rwhashtab.c \ lib/datastruct/rwhashtab.h \ lib/keyfile/keyfile.c \ lib/keyfile/keyfile.h \ lib/netpacket/netpacket.h \ lib/netpacket/netpacket_delete.c \ lib/netpacket/netpacket_directory.c \ lib/netpacket/netpacket_hmac.c \ lib/netpacket/netpacket_internal.h \ lib/netpacket/netpacket_op.c \ lib/netpacket/netpacket_read.c \ lib/netpacket/netpacket_register.c \ lib/netpacket/netpacket_transaction.c \ lib/netpacket/netpacket_write.c \ lib/netproto/netproto.c \ lib/netproto/netproto.h \ lib/netproto/netproto_connect.c \ lib/netproto/netproto_internal.h \ lib/netproto/netproto_keyexchange.c \ lib/netproto/netproto_packet.c \ lib/network/tsnetwork.h \ lib/network/tsnetwork_buf.c \ lib/network/tsnetwork_bwlimit.c \ lib/network/tsnetwork_connect.c \ lib/network/tsnetwork_internal.h \ lib/network/tsnetwork_register.c \ lib/network/tsnetwork_select.c \ lib/network/tsnetwork_sleep.c \ lib/network/tsnetwork_writeq.c \ lib/scryptenc/scryptenc.c \ lib/scryptenc/scryptenc.h \ lib/scryptenc/scryptenc_cpuperf.c \ lib/scryptenc/scryptenc_cpuperf.h \ lib/util/dirutil.c \ lib/util/dirutil.h \ lib/util/hexlink.c \ lib/util/hexlink.h \ lib/util/passphrase_entry.c \ lib/util/passphrase_entry.h \ lib/util/print_separator.c \ lib/util/print_separator.h \ lib/util/sigquit.c \ lib/util/sigquit.h \ lib/util/tvmath.c \ lib/util/tvmath.h \ libcperciva/alg/sha256.c \ libcperciva/alg/sha256.h \ libcperciva/alg/sha256_arm.h \ libcperciva/alg/sha256_shani.h \ libcperciva/alg/sha256_sse2.h \ libcperciva/cpusupport/cpusupport.h \ libcperciva/crypto/crypto_aes.h \ libcperciva/crypto/crypto_aes_aesni.h \ libcperciva/crypto/crypto_aes_aesni_m128i.h \ libcperciva/crypto/crypto_aes_arm.h \ libcperciva/crypto/crypto_aes_arm_u8.h \ libcperciva/crypto/crypto_aesctr.c \ libcperciva/crypto/crypto_aesctr.h \ libcperciva/crypto/crypto_aesctr_aesni.h \ libcperciva/crypto/crypto_aesctr_arm.h \ libcperciva/crypto/crypto_dh.c \ libcperciva/crypto/crypto_dh.h \ libcperciva/crypto/crypto_dh_group14.c \ libcperciva/crypto/crypto_dh_group14.h \ libcperciva/crypto/crypto_entropy.c \ libcperciva/crypto/crypto_entropy.h \ libcperciva/crypto/crypto_entropy_rdrand.h \ libcperciva/crypto/crypto_verify_bytes.c \ libcperciva/crypto/crypto_verify_bytes.h \ libcperciva/datastruct/elasticarray.c \ libcperciva/datastruct/elasticarray.h \ libcperciva/datastruct/mpool.h \ libcperciva/datastruct/ptrheap.c \ libcperciva/datastruct/ptrheap.h \ libcperciva/datastruct/timerqueue.c \ libcperciva/datastruct/timerqueue.h \ libcperciva/events/events.c \ libcperciva/events/events.h \ libcperciva/events/events_immediate.c \ libcperciva/events/events_internal.h \ libcperciva/events/events_network.c \ libcperciva/events/events_network_selectstats.c \ libcperciva/events/events_timer.c \ libcperciva/external/queue/queue.h \ libcperciva/network/network.h \ libcperciva/network/network_accept.c \ libcperciva/network/network_connect.c \ libcperciva/network/network_read.c \ libcperciva/network/network_write.c \ libcperciva/util/align_ptr.h \ libcperciva/util/asprintf.c \ libcperciva/util/asprintf.h \ libcperciva/util/b64encode.c \ libcperciva/util/b64encode.h \ libcperciva/util/ctassert.h \ libcperciva/util/entropy.c \ libcperciva/util/entropy.h \ libcperciva/util/getopt.c \ libcperciva/util/getopt.h \ libcperciva/util/hexify.c \ libcperciva/util/hexify.h \ libcperciva/util/humansize.c \ libcperciva/util/humansize.h \ libcperciva/util/imalloc.h \ libcperciva/util/insecure_memzero.c \ libcperciva/util/insecure_memzero.h \ libcperciva/util/monoclock.c \ libcperciva/util/monoclock.h \ libcperciva/util/parsenum.h \ libcperciva/util/readpass.c \ libcperciva/util/readpass.h \ libcperciva/util/readpass_file.c \ libcperciva/util/sock.c \ libcperciva/util/sock.h \ libcperciva/util/sock_internal.h \ libcperciva/util/sock_util.c \ libcperciva/util/sock_util.h \ libcperciva/util/sysendian.h \ libcperciva/util/ttyfd.c \ libcperciva/util/ttyfd.h \ libcperciva/util/warnp.c \ libcperciva/util/warnp.h nodist_lib_libtarsnap_a_SOURCES= \ apisupport-config.h \ cpusupport-config.h lib_libtarsnap_a_CPPFLAGS= \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/lib-platform \ -I$(top_srcdir)/lib-platform/crypto \ -I$(top_srcdir)/lib-platform/network \ -I$(top_srcdir)/lib-platform/util \ -I$(top_srcdir)/lib/crypto \ -I$(top_srcdir)/lib/datastruct \ -I$(top_srcdir)/lib/keyfile \ -I$(top_srcdir)/lib/netpacket \ -I$(top_srcdir)/lib/netproto \ -I$(top_srcdir)/lib/network \ -I$(top_srcdir)/lib/scryptenc \ -I$(top_srcdir)/lib/util \ -I$(top_srcdir)/libarchive \ -I$(top_srcdir)/libcperciva/alg \ -I$(top_srcdir)/libcperciva/cpusupport \ -I$(top_srcdir)/libcperciva/crypto \ -I$(top_srcdir)/libcperciva/datastruct \ -I$(top_srcdir)/libcperciva/events \ -I$(top_srcdir)/libcperciva/external/queue \ -I$(top_srcdir)/libcperciva/network \ -I$(top_srcdir)/libcperciva/util \ -I$(top_srcdir)/tar \ -DAPISUPPORT_CONFIG_FILE=\"apisupport-config.h\" \ -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" \ -DTSSERVER=\"$(TSSERVER)\" \ -D_POSIX_C_SOURCE=200809L \ -D_XOPEN_SOURCE=700 \ ${CFLAGS_POSIX} LIBTARSNAP_A= lib/libtarsnap.a # apisupport has extra CFLAGS (compared to cpusupport), because it needs to # access post-configure info: lib-platform/platform.h, config.h, and # -DHAVE_CONFIG_H. apisupport-config.h: ( export CC="${CC}"; export CFLAGS="-I${top_srcdir}/lib-platform -I${builddir} ${DEFS} ${CFLAGS}"; command -p sh $(srcdir)/libcperciva/apisupport/Build/apisupport.sh "$$PATH") > apisupport-config.h.tmp && command -p mv apisupport-config.h.tmp apisupport-config.h cpusupport-config.h: ( export CC="${CC}"; export CFLAGS="${CFLAGS}"; command -p sh $(srcdir)/libcperciva/cpusupport/Build/cpusupport.sh "$$PATH" ) > cpusupport-config.h.tmp && command -p mv cpusupport-config.h.tmp cpusupport-config.h # Add libcperciva files to dist # # crypto_aesctr_shared.c is in this list because it can't be included in the # _SOURCES because it should only be included as part of another translation # unit. EXTRA_DIST= \ libcperciva/POSIX/README \ libcperciva/POSIX/posix-abstract-declarator.c \ libcperciva/POSIX/posix-cflags.sh \ libcperciva/POSIX/posix-clock_gettime.c \ libcperciva/POSIX/posix-clock_realtime.c \ libcperciva/POSIX/posix-inet-addrstrlen.c \ libcperciva/POSIX/posix-inet6-addrstrlen.c \ libcperciva/POSIX/posix-l.sh \ libcperciva/POSIX/posix-msg_nosignal.c \ libcperciva/POSIX/posix-restrict.c \ libcperciva/POSIX/posix-stat-st_mtim.c \ libcperciva/POSIX/posix-trivial.c \ libcperciva/cpusupport/Build/cpusupport-ARM-AES.c \ libcperciva/cpusupport/Build/cpusupport-ARM-SHA256.c \ libcperciva/cpusupport/Build/cpusupport-HWCAP-ELF_AUX_INFO.c \ libcperciva/cpusupport/Build/cpusupport-HWCAP-GETAUXVAL.c \ libcperciva/cpusupport/Build/cpusupport-X86-AESNI.c \ libcperciva/cpusupport/Build/cpusupport-X86-CPUID.c \ libcperciva/cpusupport/Build/cpusupport-X86-CPUID_COUNT.c \ libcperciva/cpusupport/Build/cpusupport-X86-RDRAND.c \ libcperciva/cpusupport/Build/cpusupport-X86-SHANI.c \ libcperciva/cpusupport/Build/cpusupport-X86-SSE2.c \ libcperciva/cpusupport/Build/cpusupport-X86-SSSE3.c \ libcperciva/cpusupport/Build/cpusupport.sh \ libcperciva/crypto/crypto_aesctr_shared.c BUILT_SOURCES= \ apisupport-config.h \ cpusupport-config.h CLEANFILES+= \ apisupport-config.h \ apisupport-config.h.tmp \ cpusupport-config.h \ cpusupport-config.h.tmp # libcperciva_crypto_aes depends on libcperciva_cpusupport_detect, so _crypto_aes # must come before _cpusupport_detect in LIBTARSNAP_A. lib_libcperciva_crypto_aes_a_SOURCES= \ libcperciva/crypto/crypto_aes.c nodist_lib_libcperciva_crypto_aes_a_SOURCES= \ apisupport-config.h \ cpusupport-config.h lib_libcperciva_crypto_aes_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libcperciva_crypto_aes_a_CFLAGS=`. ./apisupport-config.h; echo $${CFLAGS_LIBCRYPTO_LOW_LEVEL_AES}` LIBTARSNAP_A+= lib/libcperciva_crypto_aes.a lib_libcperciva_aesni_a_SOURCES= \ libcperciva/crypto/crypto_aes_aesni.c \ libcperciva/crypto/crypto_aesctr_aesni.c nodist_lib_libcperciva_aesni_a_SOURCES= \ cpusupport-config.h lib_libcperciva_aesni_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libcperciva_aesni_a_CFLAGS=`. ./cpusupport-config.h; echo $${CFLAGS_X86_AESNI}` LIBTARSNAP_A+= lib/libcperciva_aesni.a lib_libcperciva_arm_aes_a_SOURCES= \ libcperciva/crypto/crypto_aes_arm.c \ libcperciva/crypto/crypto_aesctr_arm.c nodist_lib_libcperciva_arm_aes_a_SOURCES= \ cpusupport-config.h lib_libcperciva_arm_aes_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libcperciva_arm_aes_a_CFLAGS=`. ./cpusupport-config.h; echo $${CFLAGS_ARM_AES}` LIBTARSNAP_A+= lib/libcperciva_arm_aes.a lib_libcperciva_rdrand_a_SOURCES= \ libcperciva/crypto/crypto_entropy_rdrand.c nodist_lib_libcperciva_rdrand_a_SOURCES= \ cpusupport-config.h lib_libcperciva_rdrand_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libcperciva_rdrand_a_CFLAGS=`. ./cpusupport-config.h; echo $${CFLAGS_X86_RDRAND}` LIBTARSNAP_A+= lib/libcperciva_rdrand.a lib_libtarsnap_sse2_a_SOURCES= \ lib/crypto/crypto_scrypt_smix_sse2.c \ libcperciva/alg/sha256_sse2.c nodist_lib_libtarsnap_sse2_a_SOURCES= \ cpusupport-config.h lib_libtarsnap_sse2_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libtarsnap_sse2_a_CFLAGS=`. ./cpusupport-config.h; echo $${CFLAGS_X86_SSE2}` LIBTARSNAP_A+= lib/libtarsnap_sse2.a lib_libcperciva_shani_a_SOURCES= \ libcperciva/alg/sha256_shani.c nodist_lib_libcperciva_shani_a_SOURCES= \ cpusupport-config.h lib_libcperciva_shani_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libcperciva_shani_a_CFLAGS=`. ./cpusupport-config.h; echo $${CFLAGS_X86_SHANI} $${CFLAGS_X86_SSSE3}` LIBTARSNAP_A+= lib/libcperciva_shani.a lib_libcperciva_arm_sha256_a_SOURCES= \ libcperciva/alg/sha256_arm.c nodist_lib_libcperciva_arm_sha256_a_SOURCES= \ cpusupport-config.h lib_libcperciva_arm_sha256_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libcperciva_arm_sha256_a_CFLAGS=`. ./cpusupport-config.h; echo $${CFLAGS_ARM_SHA256}` LIBTARSNAP_A+= lib/libcperciva_arm_sha256.a lib_libcperciva_cpusupport_detect_a_SOURCES= \ libcperciva/cpusupport/cpusupport_arm_aes.c \ libcperciva/cpusupport/cpusupport_arm_sha256.c \ libcperciva/cpusupport/cpusupport_x86_aesni.c \ libcperciva/cpusupport/cpusupport_x86_rdrand.c \ libcperciva/cpusupport/cpusupport_x86_shani.c \ libcperciva/cpusupport/cpusupport_x86_sse2.c \ libcperciva/cpusupport/cpusupport_x86_ssse3.c nodist_lib_libcperciva_cpusupport_detect_a_SOURCES= \ cpusupport-config.h lib_libcperciva_cpusupport_detect_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) LIBTARSNAP_A+= lib/libcperciva_cpusupport_detect.a lib_libtarsnap_rsa_a_SOURCES= \ lib/crypto/crypto_compat.c \ lib/crypto/crypto_keys.c \ lib/crypto/crypto_keys_subr.c \ lib/crypto/crypto_rsa.c lib_libtarsnap_rsa_a_CPPFLAGS=$(lib_libtarsnap_a_CPPFLAGS) lib_libtarsnap_rsa_a_CFLAGS=`. ./apisupport-config.h; echo $${CFLAGS_LIBCRYPTO_LOW_LEVEL_RSA}` LIBTARSNAP_A+= lib/libtarsnap_rsa.a # # # tarsnap source, docs, etc. # # tarsnap_SOURCES= \ tar/bsdtar.c \ tar/bsdtar.h \ tar/bsdtar_platform.h \ tar/ccache/ccache.h \ tar/ccache/ccache_entry.c \ tar/ccache/ccache_internal.h \ tar/ccache/ccache_read.c \ tar/ccache/ccache_write.c \ tar/chunks/chunks.h \ tar/chunks/chunks_delete.c \ tar/chunks/chunks_directory.c \ tar/chunks/chunks_internal.h \ tar/chunks/chunks_read.c \ tar/chunks/chunks_stats.c \ tar/chunks/chunks_stats_internal.c \ tar/chunks/chunks_transaction.c \ tar/chunks/chunks_write.c \ tar/cmdline.c \ tar/getdate.c \ tar/glue/archive_multitape.c \ tar/glue/archive_multitape.h \ tar/glue/tape.c \ tar/matching.c \ tar/multitape/chunkify.c \ tar/multitape/chunkify.h \ tar/multitape/multitape.h \ tar/multitape/multitape_chunkiter.c \ tar/multitape/multitape_delete.c \ tar/multitape/multitape_fsck.c \ tar/multitape/multitape_internal.h \ tar/multitape/multitape_metadata.c \ tar/multitape/multitape_metaindex.c \ tar/multitape/multitape_nuke.c \ tar/multitape/multitape_read.c \ tar/multitape/multitape_recover.c \ tar/multitape/multitape_stats.c \ tar/multitape/multitape_transaction.c \ tar/multitape/multitape_write.c \ tar/read.c \ tar/siginfo.c \ tar/storage/storage.h \ tar/storage/storage_delete.c \ tar/storage/storage_directory.c \ tar/storage/storage_internal.h \ tar/storage/storage_read.c \ tar/storage/storage_read_cache.c \ tar/storage/storage_read_cache.h \ tar/storage/storage_transaction.c \ tar/storage/storage_write.c \ tar/subst.c \ tar/tarsnap_opt.h \ tar/tree.c \ tar/tree.h \ tar/util.c \ tar/write.c if INC_WINDOWS_FILES tarsnap_SOURCES+= \ tar/bsdtar_windows.c \ tar/bsdtar_windows.h endif if INC_CYGWIN_FILES tarsnap_SOURCES+= \ tar/bsdtar_cygwin.c \ tar/bsdtar_cygwin.h endif tarsnap_DEPENDENCIES = \ $(LIBTARSNAP_A) \ libarchive/archive.h \ libarchive/archive_entry.h \ libarchive/libarchive.a tarsnap_LDADD= libarchive/libarchive.a $(LIBTARSNAP_A) tarsnap_CPPFLAGS= \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/lib-platform \ -I$(top_srcdir)/lib-platform/util \ -I$(top_srcdir)/lib/crypto \ -I$(top_srcdir)/lib/datastruct \ -I$(top_srcdir)/lib/keyfile \ -I$(top_srcdir)/lib/netpacket \ -I$(top_srcdir)/lib/netproto \ -I$(top_srcdir)/lib/network \ -I$(top_srcdir)/lib/util \ -I$(top_srcdir)/libarchive \ -I$(top_srcdir)/libcperciva/crypto \ -I$(top_srcdir)/libcperciva/datastruct \ -I$(top_srcdir)/libcperciva/util \ -I$(top_srcdir)/tar \ -I$(top_srcdir)/tar/ccache \ -I$(top_srcdir)/tar/chunks \ -I$(top_srcdir)/tar/glue \ -I$(top_srcdir)/tar/multitape \ -I$(top_srcdir)/tar/storage \ "-DETC_TARSNAP_CONF=\"${sysconfdir}/tarsnap.conf\"" \ -DLIBARCHIVE_STATIC \ -DUSERAGENT=\"tarsnap-${VERSION}\" tarsnap_man_MANS= tar/tarsnap.1 tar/tarsnap.conf.5 # # tarsnap-keygen source, docs, etc. # # tarsnap_keygen_SOURCES= \ keygen/keygen.c \ keygen/keygen.h \ keygen/keygen_actual.c \ keygen/keygen_network.c tarsnap_keygen_LDADD= $(LIBTARSNAP_A) tarsnap_keygen_CPPFLAGS= \ -I$(top_srcdir)/keygen \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/lib-platform \ -I$(top_srcdir)/lib/crypto \ -I$(top_srcdir)/lib/datastruct \ -I$(top_srcdir)/lib/keyfile \ -I$(top_srcdir)/lib/netpacket \ -I$(top_srcdir)/lib/netproto \ -I$(top_srcdir)/lib/network \ -I$(top_srcdir)/lib/util \ -I$(top_srcdir)/libarchive \ -I$(top_srcdir)/libcperciva/crypto \ -I$(top_srcdir)/libcperciva/util \ -I$(top_srcdir)/tar \ -DUSERAGENT=\"tarsnap-keygen-${VERSION}\" tarsnap_keygen_man_MANS= keygen/tarsnap-keygen.1 # # tarsnap-keyregen source, docs, etc. # # tarsnap_keyregen_SOURCES= \ keygen/keygen.h \ keygen/keygen_actual.c \ keygen/keygen_network.c \ keyregen/keyregen.c tarsnap_keyregen_LDADD= $(LIBTARSNAP_A) tarsnap_keyregen_CPPFLAGS= \ -I$(top_srcdir)/keygen \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/lib-platform \ -I$(top_srcdir)/lib/crypto \ -I$(top_srcdir)/lib/datastruct \ -I$(top_srcdir)/lib/keyfile \ -I$(top_srcdir)/lib/netpacket \ -I$(top_srcdir)/lib/netproto \ -I$(top_srcdir)/lib/network \ -I$(top_srcdir)/lib/util \ -I$(top_srcdir)/libarchive \ -I$(top_srcdir)/libcperciva/crypto \ -I$(top_srcdir)/libcperciva/util \ -I$(top_srcdir)/tar \ -DUSERAGENT=\"tarsnap-keyregen-${VERSION}\" tarsnap_keyregen_man_MANS= keyregen/tarsnap-keyregen.1 # # # tarsnap-recrypt source, docs, etc. # # tarsnap_recrypt_SOURCES= \ recrypt/recrypt.c \ tar/chunks/chunks_delete.c \ tar/chunks/chunks_directory.c \ tar/chunks/chunks_stats.c \ tar/chunks/chunks_stats_internal.c \ tar/chunks/chunks_transaction.c \ tar/chunks/chunks_write.c \ tar/multitape/multitape_metadata.c \ tar/multitape/multitape_transaction.c \ tar/storage/storage_delete.c \ tar/storage/storage_directory.c \ tar/storage/storage_read.c \ tar/storage/storage_read_cache.c \ tar/storage/storage_read_cache.h \ tar/storage/storage_transaction.c \ tar/storage/storage_write.c tarsnap_recrypt_LDADD= $(LIBTARSNAP_A) tarsnap_recrypt_CPPFLAGS= \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/lib-platform \ -I$(top_srcdir)/lib-platform/util \ -I$(top_srcdir)/lib/crypto \ -I$(top_srcdir)/lib/datastruct \ -I$(top_srcdir)/lib/keyfile \ -I$(top_srcdir)/lib/netpacket \ -I$(top_srcdir)/lib/netproto \ -I$(top_srcdir)/lib/network \ -I$(top_srcdir)/lib/util \ -I$(top_srcdir)/libarchive \ -I$(top_srcdir)/libcperciva/crypto \ -I$(top_srcdir)/libcperciva/util \ -I$(top_srcdir)/tar \ -I$(top_srcdir)/tar/chunks \ -I$(top_srcdir)/tar/multitape \ -I$(top_srcdir)/tar/storage \ -DUSERAGENT=\"tarsnap-recrypt-${VERSION}\" tarsnap_recrypt_man_MANS= recrypt/tarsnap-recrypt.1 # # # tarsnap-keymgmt source, docs, etc. # # tarsnap_keymgmt_SOURCES= keymgmt/keymgmt.c tarsnap_keymgmt_LDADD= $(LIBTARSNAP_A) tarsnap_keymgmt_CPPFLAGS= \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/lib-platform \ -I$(top_srcdir)/lib/crypto \ -I$(top_srcdir)/lib/datastruct \ -I$(top_srcdir)/lib/keyfile \ -I$(top_srcdir)/lib/util \ -I$(top_srcdir)/libarchive \ -I$(top_srcdir)/libcperciva/crypto \ -I$(top_srcdir)/libcperciva/util \ -I$(top_srcdir)/tar tarsnap_keymgmt_man_MANS= keymgmt/tarsnap-keymgmt.1 # # Test framework and support programs # # .PHONY: test test: all $(top_srcdir)/tests/test_tarsnap.sh . # Detect potential memory leaks from libraries. tests_valgrind_potential_memleaks_SOURCES = tests/valgrind/potential-memleaks.c tests_valgrind_potential_memleaks_CPPFLAGS = \ -D_POSIX_C_SOURCE=200809L \ -D_XOPEN_SOURCE=700 \ ${CFLAGS_POSIX} # Add test files to dist EXTRA_DIST+= \ tests/01-trivial.sh \ tests/01-trivial.sh \ tests/02-c-dry-run-no-keyfile.sh \ tests/03-c-dry-run-fake-keyfile.sh \ tests/04-c-d-real-keyfile.sh \ tests/05-passphrase.sh \ tests/06-progress-output-pb.good \ tests/06-progress-output-v.good \ tests/06-progress-output-vpb-1k.good \ tests/06-progress-output-vpb-20k.good \ tests/06-progress-output.sh \ tests/fake-passphrased.keys \ tests/fake.keys \ tests/shared_test_functions.sh \ tests/shared_valgrind_functions.sh \ tests/test_tarsnap.sh # Tarsnap configuration file if INSTALL_CONF_NO_SAMPLE tar/tarsnap.conf: tar/tarsnap.conf.sample cp $< $@ sysconf_DATA = tar/tarsnap.conf CLEANFILES += tar/tarsnap.conf else sysconf_DATA = tar/tarsnap.conf.sample endif # Use the right version of the man pages depending on whether we have mdoc # macros available on this system. tar/tarsnap.1-man tar/tarsnap.1-mdoc \ tar/tarsnap.conf.5-man tar/tarsnap.conf.5-mdoc: tar/tarsnap.1-man.in \ tar/tarsnap.1-mdoc.in tar/tarsnap.conf.5-man.in \ tar/tarsnap.conf.5-mdoc.in sed -e 's|@sysconfdir[@]|${sysconfdir}|g' ${srcdir}/$@.in > $@.tmp mv $@.tmp $@ tar/tarsnap.1: tar/tarsnap.1-@MANVER@ cat tar/tarsnap.1-@MANVER@ > tar/tarsnap.1.tmp mv $@.tmp $@ tar/tarsnap.conf.5: tar/tarsnap.conf.5-@MANVER@ cat tar/tarsnap.conf.5-@MANVER@ > tar/tarsnap.conf.5.tmp mv $@.tmp $@ keygen/tarsnap-keygen.1: keygen/tarsnap-keygen.1-@MANVER@ cat $(top_srcdir)/keygen/tarsnap-keygen.1-@MANVER@ > keygen/tarsnap-keygen.1.tmp mv $@.tmp $@ keyregen/tarsnap-keyregen.1: keyregen/tarsnap-keyregen.1-@MANVER@ cat $(top_srcdir)/keyregen/tarsnap-keyregen.1-@MANVER@ > keyregen/tarsnap-keyregen.1.tmp mv $@.tmp $@ recrypt/tarsnap-recrypt.1: recrypt/tarsnap-recrypt.1-@MANVER@ cat $(top_srcdir)/recrypt/tarsnap-recrypt.1-@MANVER@ > recrypt/tarsnap-recrypt.1.tmp mv $@.tmp $@ keymgmt/tarsnap-keymgmt.1: keymgmt/tarsnap-keymgmt.1-@MANVER@ cat $(top_srcdir)/keymgmt/tarsnap-keymgmt.1-@MANVER@ > keymgmt/tarsnap-keymgmt.1.tmp mv $@.tmp $@ # Add man files to dist EXTRA_DIST+= \ keygen/tarsnap-keygen.1-man \ keygen/tarsnap-keygen.1-mdoc \ keymgmt/tarsnap-keymgmt.1-man \ keymgmt/tarsnap-keymgmt.1-mdoc \ keyregen/tarsnap-keyregen.1-man \ keyregen/tarsnap-keyregen.1-mdoc \ recrypt/tarsnap-recrypt.1-man \ recrypt/tarsnap-recrypt.1-mdoc \ tar/tarsnap.1-man.in \ tar/tarsnap.1-mdoc.in \ tar/tarsnap.conf.5-man.in \ tar/tarsnap.conf.5-mdoc.in \ tar/tarsnap.conf.sample CLEANFILES+= tar/tarsnap.1-mdoc tar/tarsnap.conf.5-mdoc # Add misc files to dist EXTRA_DIST+= \ BUILDING \ lib/netpacket/packets.txt \ libcperciva/COPYRIGHT \ misc/describe-options.txt \ pkg/ # # Misc helper scripts # # if INSTALL_BASH_COMPLETION bashcompdir = $(BASH_COMPLETION_DIR) dist_bashcomp_DATA = \ misc/bash_completion.d/tarsnap \ misc/bash_completion.d/tarsnap-keygen \ misc/bash_completion.d/tarsnap-keymgmt \ misc/bash_completion.d/tarsnap-keyregen \ misc/bash_completion.d/tarsnap-recrypt endif if INSTALL_ZSH_COMPLETION zshcompdir = $(ZSH_COMPLETION_DIR) dist_zshcomp_DATA = misc/zsh_completion/_tarsnap endif tarsnap-1.0.41/NEWS.md000066400000000000000000000622701476705112600143630ustar00rootroot00000000000000Tarsnap Releases ================ ### Tarsnap 1.0.41 (March 21, 2025) - tarsnap now has mitigations to defend against information leakage via chunking: Chunks are padded using the PADME scheme, and small-alphabet cycles are prohibited in chunking to block a chosen-plaintext attack. - tarsnap -c now accepts --dry-run-metadata, which simulates creating an archive without reading any file data. This is significantly faster than a regular --dry-run, and is suitable for checking which filesystem entries will be archived (with -v) or checking the total archive size (with --totals or --progress-bytes). - tarsnap now accepts --noatime with -c mode, which requests that the operating system does not update atime when reading files or directories. Not supported by all operating systems or filesystems. - If the server-side state was modified and tarsnap exits with an error, it will now have an exit code of 2. - tarsnap will read a config file in $XDG_CONFIG_HOME/tarsnap/tarsnap.conf; or ~/.config/tarsnap/tarsnap.conf if $XDG_CONFIG_HOME is not set. The previous config file ~/.tarsnaprc is still supported, and will not be deprecated. - tarsnap now accepts --null-input as a synonym for --null. For compatibility reasons, --null is still supported, and will not be deprecated. - tarsnap now accepts --null-output, which causes --list-archives to separate each archive name with a null character (like `find -print0`). If one or more -v arguments are specified, multiple null characters are used to separate fields; see the man page for details. - tarsnap now accepts --null-output with -x and -t, which causes them to separate each filename with a null character. If there are multiple fields on a line, null characters are used instead of spaces; see the man page for details. - tarsnap now accepts --null-output with --print-stats, which causes it to separate fields and lines with null character(s); see the man page for details. - tarsnap now accepts --hashes, which causes --list-archives to print hashes of archive names. If one or more -v arguments are specified, it will print other metadata (as per --list-archives). This option is intended for the GUI and is not needed for command-line usage. - tarsnap now accepts -f TAPEHASH with --list-archives --hashes, which prints metadata about the specified archive(s). Multiple -f options may be specified. This option is intended for the GUI and is not needed for command-line usage. - Fixed a problem with strict aliasing if somebody compiled with gcc11 or higher using -O3, does not have SSE2, and is using a passphrase-protected keyfile. ### Tarsnap 1.0.40 (February 10, 2022) - tarsnap now accepts a --dump-config option to print the command-line and all non-blank lines read from config files. - tarsnap now gives an error if there are unused command-line arguments. (i.e. "tarsnap -d -f a1 a2", where "a2" is unused.) - Use RDRAND as an additional source of entropy on CPUs which support it. - Use SHANI instructions on CPUs which support them. - When sent SIGINFO or SIGUSR1, tarsnap now prints the number of files and the number of uncompressed bytes processed, in addition to the previous output. - tarsnap now accepts a --passphrase method:arg option which accepts: * --passphrase dev:tty-stdin * --passphrase dev:stdin-once * --passphrase dev:tty-once * --passphrase env:VARNAME * --passphrase file:FILENAME - tarsnap now accepts a --resume-extract option to skip extracting files whose filesize and mtime match existing files on disk. - tarsnap now accepts --progress-bytes SIZE, which prints a progress message after each SIZE bytes are processed, up to once per file. This can be disabled with --no-progress-bytes. - Assorted compatibility fixes for MacOS X, FreeBSD, OpenBSD, Solaris, ZFS, and gcc 4.2.1. ### Tarsnap 1.0.39 (July 29, 2017) - Fix a division-by-zero crash when reading corrupt password-encrypted tarsnap key files. - Fix a crash in reading corrupt "cpio newc" format archives included via the tarsnap @archive directive, on 32-bit systems. - Fix a bug in the handling of corrupt "ar" format archives included via the tarsnap @archive directive. - Fix an abort trap which complained about files with a negative modification time. (This assertion occured after the archive is stored, so no user data was at risk.) ### Tarsnap 1.0.38 (July 15, 2017) - tarsnap now accepts an --initialize-cachedir command, which is intended for the GUI and is not needed for command-line usage. - tarsnap now applies --humanize-numbers to the SIGINFO "progress" output. - tarsnap now gives a warning for --configfile /nosuchfile. - tarsnap now accepts --iso-dates, which prints file and directory dates as yyyy-mm-dd hh:mm:ss in "list archive" mode. This can be cancelled with --no-iso-dates. - the ./configure --with-conf-no-sample flag causes in the sample config file to be installed as tarsnap.conf instead of tarsnap.conf.sample. This may result in overwriting an existing tarsnap.conf file. - tarsnap now accepts --force-resources to force the decryption of a passphrase-encrypted key file to proceed even if it is anticipated to require an excessive amount of memory or CPU time. This can be cancelled with --no-force-resources. - tarsnap now supports OpenSSL 1.1. - tarsnap now displays 'Deleting archive "foo"' when deleting a single archive in verbose mode. (The former behaviour was to print that message only when multiple archives were being deleted by a single tarsnap command.) - tarsnap now accepts --archive-names filename, which reads a list of archive names from a file in addition to any -f options. ### Tarsnap 1.0.37 (March 10, 2016) - tarsnap-key(gen|mgmt|regen) now accept a --passphrase-time option, which specifies the duration to be spent on computing the derived keys used for encrypting the tarsnap key file. - tarsnap now accepts a --keep-going option when deleting or printing statistics about multiple archives. - tarsnap-keymgmt now accepts a --print-key-permissions option for listing the permissions contained in a key file. - tarsnap --print-stats now accepts a --csv-file option for printing statistics in comma-separated-value format. - tarsnap now accepts a --verify-config command which exits silently after checking the configuration file(s) for syntax errors. - tarsnap now looks for a server named "v1-0-0-server.tarsnap.com" instead of the historic "betatest-server.tarsnap.com" hostname. This should have no user-visible effect in most configurations. - tarsnap now correctly warns if a sparse extract fails due to hardlinks. - tarsnap now prints a warning if creating an empty archive. ### Tarsnap 1.0.36.1 (August 20, 2015) - A [theoretically exploitable one-byte buffer overflow] (http://www.daemonology.net/blog/2015-08-21-tarsnap-1000-exploit-bounty.html) when archiving objects with long path names is fixed. - A vulnerability which allowed a corrupt archive to cause tarsnap to allocate a large amount of memory upon listing archives or reading the corrupt archive is fixed. - Tarsnap no longer crashes if its first DNS lookup fails. - Tarsnap no longer exits with "Callbacks uninitialized" when running on a dual-stack network if the first IP stack it attempts fails to connect. - tarsnap -c --dry-run can now run without a keyfile, allowing users to predict how much Tarsnap will cost before signing up. - Tarsnap now includes bash completion scripts. - Tarsnap now automatically detects and uses AESNI and SSE2 cpu features. - Tarsnap now works around an OS X standards-compliance bug which was making tarsnap fail to build. ### Tarsnap 1.0.35 (July 25, 2013) - A bug in tarsnap 1.0.34 which could cause tarsnap to crash (segmentation fault or bus error) when encountering network glitches or outages is fixed. - When tarsnap encounters "insane" filesystems (procfs and other similar synthetic filesystems which are not reasonable to archive), it now archives the filesystem mount point but by default does not recurse into the filesystem. Previous releases (since 1.0.26) did not archive the synthetic filesystem mount point. ### Tarsnap 1.0.34 (July 13, 2013) - Tarsnap now supports both IPv4 and IPv6. - Tarsnap is now more resilient against short network glitches when it first connects to the Tarsnap server. - Tarsnap now supports platforms with mandatory structure alignment (e.g., ARM OABI). - Tarsnap now restores terminal settings if killed with ^C while reading a password or passphrase. - Multiple minor bug fixes and cleanups. ### Tarsnap 1.0.33 (August 10, 2012) - Tarsnap now caches archive metadata blocks in RAM, typically providing a 5x - 10x speedup and reduction in bandwidth usage in the "fsck" operation and when deleting a large number of archives at once. - Tarsnap's internal "chunk" metadata structure is now smaller, providing a ~10% reduction in usage on 32-bit machines and a ~30% reduction in memory usage on 64-bit machines. - Tarsnap's --newer* options now correctly descend into old directories in order to look for new files. (But note that tarsnap's snapshotting makes these options unnecessary in most situations.) - Multiple minor bug fixes and cleanups. ### Tarsnap 1.0.32 (February 22, 2012) - A bug affecting the handling of the --nodump option on Linux (and in most cases rendering it inoperative) is fixed. - A workaround has been added for a compiler bug in OS X 10.7 (Lion). - The NetBSD "kernfs" and "ptyfs" filesystems are now excluded from archival by default. ### Tarsnap 1.0.31 (November 17, 2011) - A race condition in key generation has been fixed which could allow a newly-generated key file to be read by another local user if the key file is being generated in a world-readable directory and the user running tarsnap-keygen has a umask other than 0066. - A bug in key generation has been fixed which could allow a newly-generated key file to be read by another local user if they key file is being generated in a world-writable directory (e.g., /tmp). - Tarsnap now supports Minix. - Tarsnap now ignores blank lines in key files; line-buffers its output (which makes tarsnap --list-archives | foo more responsive); and prints a progress indicator during tarsnap --fsck. - Multiple minor bug fixes. ### Tarsnap 1.0.30 (August 25, 2011) - A bug fix in the handling of readdir errors; in earlier versions, it was theoretically possible for a failing hard drive or other errors in reading directories to result in files being silently omitted from an archive. - Several bug fixes relating to the handling of @archive directives with mtree files. - A bug fix to prevent cache directory corruption resulting in tarsnap failing if it was interrupted at exactly the right (wrong) moment in its operation. - A bug fix to correctly handle ~ in tarsnap -s path substitutions. - Many more minor bug fixes. ### Tarsnap 1.0.29 (February 7, 2011) - New tarsnap-keyregen and tarsnap-recrypt utilities have been added for downloading, decrypting, re-encrypting, and re-uploading Tarsnap data (aka. key rotation). ### Tarsnap 1.0.28 (January 18, 2011) - A [critical security bug] (http://www.daemonology.net/blog/2011-01-18-tarsnap-critical-security-bug.html) has been fixed in Tarsnap's chunk encryption code. ### Tarsnap 1.0.27 (June 27, 2010) - The tarsnap -d and tarsnap --print-stats commands can now take multiple -f options, in which case all the specified archives will be deleted or have their statistics printed. - When the --dry-run option is used, the -f option is no longer required. - By default tarsnap will no longer recurse into synthetic filesystems (e.g., procfs). A new --insane-filesystems option disables this behaviour (i.e., allows tarsnap to recurse into synthetic filesystems). - A new --quiet option to tarsnap disables some usually-harmless warnings. - A new --configfile option to tarsnap makes it possible to specify an additional configuration file; and a --no-default-config option can instruct tarsnap to not process the default configuration files. - The ~/.tarsnaprc configuration file and configuration settings which begin with ~ are now processed by expanding ~ to the home directory of the current user. This is a change from the behaviour in tarsnap 1.0.26 and earlier where ~ was expanded based on the $HOME environment variable, and will affect the use of tarsnap under sudo(8). - The tarsnap --fsck command can now be run if either the delete or write keys are present and no longer attempts to prune corrupted archives from the server. - A new tarsnap --recover command can be used to recover a checkpointed archive. Checkpointed archives will continue to be automatically recovered when tarsnap -c or tarsnap -d are run. ### Tarsnap 1.0.26 (December 24, 2009) - Change to the Tarsnap client-server protocol in order to reduce the need for connections to be dropped and re-established; in particular, this should avoid some "Connection lost, waiting X seconds before reconnecting" warnings when starting or finishing an archive. - Fixes to bandwidth limiting via the --maxbw-rate family of options; prior to this version, those options could break on systems with fast internet connections. - Several improvements to the tarsnap(1) manual page. - Minor build fixes: Tarsnap now supports out-of-tree builds, and now adds the necessary compiler flags when building SSE2 code. ### Tarsnap 1.0.25 (July 10, 2009) - Portability improvement: Configuration files and files passed to the -T and -X options of tarsnap may contain Windows or Mac end-of-line characters. - Portability improvement: A preprocessed version of the Tarsnap man pages is installed on systems such as Solaris which do not have mdoc macros. - New command-line options, for overriding options set in configuration files: --normalmem, --no-aggressive-networking, --no-config-exclude, --no-config-include, --no-disk-pause, --no-humanize-numbers, --no-maxbw, --no-maxbw-rate-down, --no-maxbw-rate-up, --no-nodump, --no-print-stats, --no-snaptime, --no-store-atime, --no-totals. - New configuration file options: disk-pause, humanize-numbers, maxbw, maxbw-rate, maxbw-rate-down, maxbw-rate-up, normalmem, no-aggressive-networking, no-config-exclude, no-config-include, no-disk-pause, no-humanize-numbers, no-maxbw, no-maxbw-rate-down, no-maxbw-rate-up, no-nodump, no-print-stats, no-snaptime, no-store-atime, no-totals. ### Tarsnap 1.0.24 (June 24, 2009) - Build process reorganization: Code shared between the tarsnap client code and the tarsnap-keygen and tarsnap-keymgmt utilities is built into a library and then accessed from there instead of being compiled multiple times. - The autoconf version of Tarsnap (above) should now be used on all supported platforms, including FreeBSD; there is no longer a "native" FreeBSD source tarball. - Source code reorganization: Several source files added in version 1.0.22 have been moved in order to make it easier to keep it in sync with scrypt source code. - Updated to use libarchive 2.7.0: This brings improved UTF8 / Unicode and extended attribute support, along with minor bug fixes. ### Tarsnap 1.0.23 (June 10, 2009) - Tarsnap now caches the result of a DNS lookup for the Tarsnap server; this improves performance when the --aggressive-networking option is used, and increases Tarsnap's resilience in the presence of temporary network glitches. - New --disk-pause X option which allows a duration to be specified to wait after each file is archived and after every 64 kB within each file (thus, approximately after each physical disk operation). - Added SIGNALS section in the Tarsnap man page describing how Tarsnap handles SIGINFO+SIGUSR1 (prints progress of current operation), SIGQUIT (truncates the current archive), and SIGUSR2 (creates a checkpoint in the current archive). - Minor bug fixes and typographical corrections. ### Tarsnap 1.0.22 (June 3, 2009) - Add support for passphrase-protected key files using AES256-CTR, HMAC-SHA256, and the scrypt key derivation function via a --passphrased option to tarsnap-keygen and tarsnap-keymgmt. - Tarsnap key files are now base64-encoded (i.e., printable). - Bug fix for non-FreeBSD systems: The autoconf script now correctly detects support for reading file flags, allowing them to be archived (and unbreaking --nodump support). - Minor bug fixes and typographical corrections. - Due to a change in the Tarsnap key file format, key files generated by the tarsnap-keygen and tarsnap-keymgmt utilities from Tarsnap 1.0.22 and later cannot be used by pre-1.0.22 versions of Tarsnap. (However, old key files can still be used with new versions of Tarsnap.) ### Tarsnap 1.0.21 (March 5, 2009) - Serious bug fix: In version 1.0.20 of Tarsnap, if the --checkpoint-bytes option is used, it is possible for an entry in Tarsnap's chunk cache to become corrupted. If this occurs, then when an archive is created containing a file for which a corrupted cache entry exists, there is a very high probability (99.8% or higher, depending on the --checkpoint-bytes value used) that Tarsnap will print the error "Skip length too long" and exit; but there is also a small chance (0.2% or less) that the archive entry corresponding to that file will be silently corrupted. - Signal handling bug fix for operating systems with SysV signal-handling semantics: In version 1.0.20 and earlier, Tarsnap can exit with a "User Signal 1" or "User Signal 2" error. This affects OpenSolaris and Solaris, and Linux if libc 5 or earlier is used. - Bug fix for OS X: In version 1.0.20 and earlier, Tarsnap can exit with an error of "tcgetattr(stdin): Operation not supported by device" if there is no terminal attached (e.g., if Tarsnap is run from a cron job). - Portability fix: Tarsnap now builds on Solaris 10. ### Tarsnap 1.0.20 (February 3, 2009) - Add --checkpoint-bytes option to Tarsnap and associated configuration file option. This instructs tarsnap -c to store a checkpoint every bytes; if Tarsnap (or the system on which Tarsnap is running) crashes, or the system's internet connection fails, in the middle of creating an archive, it will now be truncated at the last checkpoint instead of being lost entirely. - Add Debian package-building metadata and Arch Linux PKGBUILD file. Thanks to Mads Sülau Jørgensen and Aaron Schaefer for their help with these. - Minor bug fixes to autoconf build. - Due to changes in cache directory and archive formats, pre-1.0.20 versions of Tarsnap should not be used to create archives after 1.0.20 or later versions of Tarsnap are used by the same machine. ### Tarsnap 1.0.19 (January 17, 2009) - Bugfix in the handling of hardlinked files. This corrects a problem where Tarsnap could under certain rare circumstances, exit with an error message of "Skip length too long" when creating an archive. - After printing a "Connection lost" warning, print a "Connection re-established" message once communication with the Tarsnap server has resumed. - Source tarballs have been renamed; the FreeBSD tarball has been renamed from tarsnap-VERSION.tgz to tarsnap-freebsd-VERSION.tgz, while the autoconfed tarball has been renamed from tarsnap-at-VERSION.tgz to tarsnap-autoconf-VERSION.tgz. For backwards compatibility, tarballs will continue to be generated with the old names for the near future. - A GPG-signed file containing the SHA256 hashes of source tarballs is now provided. - Minor changes to improve portability. Tarsnap should now build and run on FreeBSD, Linux (success reported on Debian, Ubuntu, CentOS, Slackware, Gentoo, RedHat, and Arch), OS X, NetBSD, OpenBSD, OpenSolaris, and Cygwin. Thanks to Simon Burns, Justin Haynes, Bruce Leidl, and Aaron Schaefer for their help with testing and fixing issues. ### Tarsnap 1.0.18 (December 25, 2008) - Many portability fixes. Tarsnap should now build and run on FreeBSD, Linux (success has been reported on Debian, Ubuntu, CentOS, Slackware, Gentoo, and RedHat), OS X, NetBSD, OpenBSD, and OpenSolaris. Thanks to "Triskelios" (IRC nick), "WEiRDJE" (IRC nick), Thomas Hurst, and Justin Haynes for their help with testing and fixing portability issues. - Minor bugfixes (mostly harmless). ### Tarsnap 1.0.17 (December 10, 2008) - Minor fixes to error message reporting. - Fixes to terminal setting code so that "tarsnap &" doesn't get stuck. - Bandwidth limiting via the --maxbw-rate family of options is now far more CPU-efficient. - Add "make uninstall" to FreeBSD make logic. - A warning is now printed when SIGQUIT or ^Q is received, since it may take a few seconds for Tarsnap to finish and exit cleanly. - Many minor improvements and bugfixes due to updating the libarchive and bsdtar code used, including new --numeric-owner, -S (extract files as sparse files), and -s (rewrite paths) options. ### Tarsnap 1.0.16 (November 15, 2008) - Add --maxbw-rate, --maxbw-rate-down, and --maxbw-rate-up options for limiting the bandwidth used by Tarsnap in bytes per second. - In the Tarsnap configuration file(s), any leading ~ in a configuration parameter is expanded to $HOME. - Fix "make -jN" in FreeBSD build. ### Tarsnap 1.0.15 (November 14, 2008) - When running without the --lowmem or --verylowmem options, Tarsnap conserves memory if it encounters lots of small files; at worst, Tarsnap's memory consumption is roughly double the memory which would be consumed if the --lowmem option were specified. - Add --dry-run option to tarsnap -c which simulates storing an archive without actually sending anything over the network. - The tarsnap.1 and tarsnap.conf.5 manual pages now reference the correct default configuration file location if the CONFIGDIR variable (on FreeBSD) or the sysconfdir variable (with autoconf) is not /usr/local/etc. - The autoconf build code no longer passes -Werror to the compiler; this allows tarsnap to build with gcc 4.3. ### Tarsnap 1.0.14 (October 14, 2008) - The tarsnap-keygen utility and tarsnap client now understand a "send more money" error response sent by the tarsnap server, and print an appropriate message. - Earlier versions of tarsnap-keygen and tarsnap will instead error out with a "network protocol violation by server" message if a user's account balance is not positive. ### Tarsnap 1.0.13 (October 1, 2008) - As a safeguard against accidental destruction of key files, tarsnap-keygen now refuses to overwrite files. - Add --nuke command to tarsnap which requires only the delete authorization key and deletes all archives. - Add --nuke option to tarsnap-keymgmt to allow creation of a key file which can be used to delete all of a machine's data but cannot be used to read or write archives. ### Tarsnap 1.0.12 (September 27, 2008) - Source code layout has changed, with resulting changes in make logic; but once compiled, there should be no functional changes. ### Tarsnap 1.0.11 (August 21, 2008) - Add --humanize-numbers option which "humanizes" statistics printed by the --print-stats option. - Add --maxbw option to interrupt archival when a specified amount of upload bandwidth has been used. - Make warnings about network glitches (e.g., "Connection lost, waiting X seconds before reconnecting") less noisy, but add a --noisy-warnings option to restore the old behaviour (probably useful for debugging purposes only). ### Tarsnap 1.0.10 (August 7, 2008) - Restricted keyfiles can be generated using a new "tarsnap-keymgmt" utility containing the keys needed to write backups but not read or delete them, to read backups but not write or delete them, etc. - Several minor bugfixes. ### Tarsnap 1.0.9 (June 30, 2008) - Linux 2.4 kernel compatibility fix in TCP_CORK code. - Linux compatibility fix in terminal handling for running tarsnap without a controlling terminal (e.g., from cron). - Linux autoconf fixes for ext2fs header files. - Workaround for FreeBSD net.inet.ip.portrange.randomized + pf interaction. ### Tarsnap 1.0.8 (June 21, 2008) - Error out in ./configure if OpenSSL or zlib are not found. - Correctly detect presence of chroot system call in ./configure. ### Tarsnap 1.0.7 (June 13, 2008) - Add sample tarsnap configuration file. - Add --aggressive-networking option for systems which are limited by internet congestion. - Ignore blank lines in tarsnap configuration file. ### Tarsnap 1.0.6 (June 11, 2008) - Minor bugfixes (mostly harmless). ### Tarsnap 1.0.5 (May 27, 2008) - Add --keep-newer-files option to tarsnap -x. - Add SIGINFO handling to read.c; add progress-within-files reporting to write.c. - Clarify error message if fchmod fails in tarsnap-keygen. - Updated to use a newer libarchive. ### Tarsnap 1.0.4 (May 12, 2008) - Don't print a warning about not archiving the tarsnap cache directory if it is being ignored due to a command-line inclusion/exclusion. ### Tarsnap 1.0.3 (May 9, 2008) - OS X portability fixes to network code. ### Tarsnap 1.0.2 (May 3, 2008) - Prompt in tarsnap-keygen changed for clarity. - OS X portability fixes to network code. - Added warning if tarsnap is passed -f option along with --list-archives or --fsck. ### Tarsnap 1.0.1 (April 27, 2008) - Add EXAMPLES section to tarsnap man page. - Don't free cache records which are still in the cache trie — this fixes a core dump when writing out the updated cache after running tarsnap -c. ### Tarsnap 1.0.0 (April 25, 2008) - First public version tarsnap-1.0.41/README.md000066400000000000000000000011561476705112600145400ustar00rootroot00000000000000Client code for Tarsnap ======================= Tarsnap is a secure, efficient online backup service: "Online backups for the truly paranoid". :exclamation: We strongly recommend that people follow the installation instructions at https://www.tarsnap.com/download.html to use an official release. > This repository is intended for developers who may wish to watch changes in > progress, investigate bugs, or test new (unreleased) features. News ---- A list of major changes in each version is given in [NEWS.md](NEWS.md). Building -------- If you would like to compile Tarsnap manually, see [BUILDING](BUILDING). tarsnap-1.0.41/STYLE000066400000000000000000000156731476705112600141150ustar00rootroot00000000000000Code style ========== In general, FreeBSD style(9) should be followed unless it is irrelevant (e.g., $FreeBSD$ tags). Functions with external linkage are declared like this: /** * module_func(arg1, arg2): * Description of what the function does, referring to arguments as * ${arg1} or suchlike. */ int module_func(void *, int); The identical comment appears in the C file where the function is defined. Static functions may have the above form of comment, or simply a /* Brief description of what the function does. */ line before the function. "Unrewrappable" comments starting in the first column should be /** * Written like this. * * Because (some of) the line-breaks are important. */ whereas when such comments are indented, they should be /*- * Written like this. * * Because (some of) the line-breaks are important. */ Line lengths should generally be 78 characters, and not more than 80 characters. In general, functions should return (int)(-1) or NULL to indicate error. Errors should be printed via warnp (if errno is relevant) or warn0 (if errno is not relevant) when they are first detected and also at higher levels where useful. As an exception to this, malloc failures (i.e., errno = ENOMEM) can result in failure being passed back up the call chain without being printed immediately. (Naturally, other errors can be passed back where a function definition so specifies; e.g., ENOENT in cases where a file not existing is not erroneous.) The first statement in main(), after variable declarations, should be "WARNP_INIT;" in order to set the program name used for printing warnings. We use %d rather than %i in printf and warn0/warnp strings. In general, functions should be structured with one return statement per status, e.g., one return() for success and one return() for failure. Errors should be handled by using goto to enter the error return path, e.g., int foo(int bar) { if (something fails) goto err0; /* ... */ if (something else fails) goto err1; /* ... */ if (yet another operation fails) goto err2; /* Success! */ return (0); err2: /* Clean up something. */ err1: /* Clean up something else. */ err0: /* Failure! */ return (-1); } As an exception to the above, if there is only one way for the function to fail, the idioms return (baz(bar)); and int rc; rc = baz(bar); /* ... cleanup code here ... */ return (rc); are allowed; furthermore, in cases such as foo_free(), the idiom if (we shouldn't do anything) return; is preferred over if (we shouldn't do anything) goto done; at the start of a function. Headers should be included in the following groups, with a blank line after each (non-empty) group: 0. "platform.h", if autoconf defines are needed in this file. 1. , with first followed by others alphabetically. 2. , in alphabetical order. 3. <*.h>, in alphabetical order. 4. header files from /lib/, in alphabetical order. 5. header files from the program being built, in alphabetical order. 6. header files (usually just one) defining the interface for this C file. If ssize_t is needed, should be included to provide it. If size_t is needed, should be included to provide it unless , , , or is already required. If the C99 integer types (uint8_t, int64_t, etc.) are required, should be included to provide them unless is already required. The type 'char' should only be used to represent human-readable characters (input from users, output to users, pathnames, et cetera). The type 'char *' should normally be a NUL-terminated string. The types 'signed char' and 'unsigned char' should never be used; C99 integer types should be used instead. When a variable is declared to have a pointer type, there should be a space between the '*' and the variable name, e.g., int main(int argc, char * argv[]) { char * opt_p = NULL; Note that this is inconsistent with FreeBSD style(9). When used as a unary operator, '*' is not separated from its argument, e.g., while (*p != '\0') p++; When a struct is referenced, the idiom /* Opaque types. */ struct foo; struct bar * bar_from_foo(struct foo *); is preferable to #include "foo.h" /* needed for struct foo */ struct bar * bar_from_foo(struct foo *); unless there is some reason why the internal layout of struct foo is needed (e.g., if struct bar contains a struct foo rather than a struct foo *). Such struct declarations should be sorted alphabetically. The file foo.c should only export symbols of the following forms: foo_* -- most symbols should be of this form. FOO_* / BAR_FOO_* -- allowed in cases where FOO or BAR_FOO is idiomatic (e.g., MD5, HMAC_SHA256). foo() / defoo() / unfoo() -- where "foo" is a verb and this improves code clarity. Functions named foo_free should return void, and foo_free(NULL) should have no effect. The right way to spell a comment about this is /* Behave consistently with free(NULL). */ If static variables need to be initialized to 0 (or NULL) then they should be explicitly declared that way; implicit initialization should not be used. In non-trivial code, comments should be included which describe in English what is being done by the surrounding code with sufficient detail that if the code were removed, it could be replaced based on reading the comments without requiring any significant creativity. Comments and documentation should be written in en-GB-oed; i.e., with the 'u' included in words such as "honour", "colour", and "neighbour", and the ending '-ize' in words such as "organize" and "realize". The Oxford (aka. serial) comma should be used in lists. Quotation marks should be placed logically, i.e., not including punctuation marks which do not form a logical part of the quoted text. Two spaces should be used after a period which ends a sentence. The first local variable declaration in cookie-using functions should be struct foo * bar = cookie; When versions of functions are written to exploit special CPU features (using the cpusupport framework), that code should be placed into a separate file (e.g., crypto_aes_aesni.c) so that it can be compiled with different compiler flags. Such a file should start with #include "cpusupport.h" #ifdef CPUSUPPORT_FOO_BAR /** * CPUSUPPORT CFLAGS: FOO_BAR FOO_BAZ */ and end with #endif /* CPUSUPPORT_FOO_BAR */ For example, we could have #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) /** * CPUSUPPORT CFLAGS: X86_SHANI X86_SSSE3 */ Functions for which special CPU-feature-exploiting variants exist should take the form { /* Variable declarations here. */ /* Asserts here, if any. */ #ifdef CPUSUPPORT_FOO_BAR if (/* We've decided we can use the variant code */) { /* Call variant code and return. */ } #endif /* Normal implementation of the function. */ } If there are multiple CPU-feature-exploiting variants, the `if` could instead be a `switch` which invokes the appropriate variant function. tarsnap-1.0.41/configure.ac000066400000000000000000000424671476705112600155610ustar00rootroot00000000000000dnl 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 # Pull in tarsnap version number and server name from files m4_define([TAR_VERS],m4_include([tar-version])) m4_define([TS_SERVER],m4_include([tsserver])) dnl These first two version numbers are updated automatically on each release. m4_define([LIBARCHIVE_VERSION_S],[2.7.0]) m4_define([LIBARCHIVE_VERSION_N],[2007000]) # # Now starts the "real" configure script. # AC_INIT([tarsnap],[TAR_VERS()],[cperciva@tarsnap.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([config.aux]) AC_CONFIG_MACRO_DIR([m4]) # Check for host and target type AC_CANONICAL_TARGET AC_CANONICAL_HOST # Must follow AC_CONFIG macros above... AM_INIT_AUTOMAKE() # 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_SUBST([TSSERVER],"TS_SERVER()") # 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. 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(LIBARCHIVE_VERSION_STRING) AC_SUBST(LIBARCHIVE_VERSION_NUMBER) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) 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]) # Checks for programs. m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_C99]) AM_PROG_CC_C_O AC_PROG_RANLIB AC_CHECK_TOOL([STRIP],[strip]) # We use some _GNU_SOURCE bits on Linux; this also enables other extensions. m4_version_prereq([2.70], [AC_USE_SYSTEM_EXTENSIONS], [AC_GNU_SOURCE]) # Don't rebuild the configure script. I'm distributing a perfectly good one. AM_MAINTAINER_MODE # Get the POSIX sh early, in case the configure script causes # `command -p getconf` to fail in a broken environment. CHECK_POSIX_SH # # Locate some specific link objects for cygwin # prog_ldadd_extra= case "$host_os" in *cygwin*) binmode_path=`$CC -print-file-name=binmode.o` if test -n "$binmode_path" && test -f "$binmode_path" ; then prog_ldadd_extra="$binmode_path" fi ;; esac PROG_LDADD_EXTRA=$prog_ldadd_extra AC_SUBST(PROG_LDADD_EXTRA) # Checks for header files. m4_version_prereq([2.70], [], [AC_HEADER_STDC]) AC_HEADER_DIRENT AC_HEADER_SYS_WAIT AC_CHECK_HEADERS_ONCE([acl/libacl.h attr/xattr.h ctype.h err.h errno.h]) AC_CHECK_HEADERS_ONCE([fcntl.h grp.h]) AC_CHECK_HEADERS_ONCE([inttypes.h langinfo.h limits.h linux/fs.h]) AC_CHECK_HEADERS_ONCE([locale.h paths.h poll.h pwd.h regex.h signal.h stdarg.h]) AC_CHECK_HEADERS_ONCE([stdint.h stdlib.h string.h]) AC_CHECK_HEADERS_ONCE([sys/acl.h sys/cdefs.h sys/extattr.h sys/ioctl.h sys/mkdev.h]) AC_CHECK_HEADERS_ONCE([sys/param.h sys/poll.h sys/select.h sys/time.h sys/utime.h]) AC_CHECK_HEADERS_ONCE([time.h unistd.h utime.h wchar.h wctype.h windows.h]) # Check for clock_gettime. On some systems, this is provided via librt. AC_CHECK_LIB(rt, clock_gettime) AC_CHECK_FUNCS_ONCE([clock_gettime]) # Check for , , , and , # which are used on various OSes to provide statfs(2) and statvfs(2). AC_CHECK_HEADERS([sys/mount.h sys/statfs.h sys/statvfs.h sys/vfs.h], [], [], [#ifdef HAVE_SYS_PARAM_H #include #endif]) # Check how we can find out the available memory. CHECK_MEMLIMIT_SUPPORT # Check for members of struct statfs and struct statvfs. AC_CHECK_MEMBERS([struct statfs.f_fstypename, struct statfs.f_type, struct statvfs.f_fstypename, struct statvfs.f_basetype], [], [], [#ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif]) # Check for , which contains magic numbers for statfs on # Linux 2.6. (We hard-code values for Linux 2.4.) AC_CHECK_HEADERS_ONCE([linux/magic.h]) # If we're on Solaris, we need to add some extra paths to the Makefile. # This needs to be done *before* we look for OpenSSL; otherwise we won't # be able to find it... CHECK_SOLARIS_PATHS # Ditto for Darwin. CHECK_DARWIN_PATHS # Check if we need -std=c99 in LDFLAGS (for gcc on Solaris). CHECK_SOLARIS_C99 # Check if we should use mdoc versions of the man pages or versions which # are uglier but more portable. CHECK_MDOC_OR_MAN # On Linux, we need ext2fs/ext2_fs.h CHECK_LINUX_EXT2FS # Check if we're on an operating system with a broken TCP_NOPUSH CHECK_BROKEN_TCP_NOPUSH # Check if we have FreeBSD's randomized TCP source port bug CHECK_FREEBSD_PORTRANGE_BUG # Checks for required libraries. AC_CHECK_HEADER(zlib.h, AC_CHECK_LIB(z,inflate, , AC_MSG_ERROR([*** zlib missing ***])), AC_MSG_ERROR([*** zlib header file missing ***])) AC_CHECK_HEADER(openssl/rsa.h, AC_CHECK_LIB(crypto,RSA_private_encrypt, , AC_MSG_ERROR([*** OpenSSL missing ***])), AC_MSG_ERROR([*** OpenSSL header files missing ***])) # Checks for optional libraries. 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_ONCE([bzlib.h]) AC_CHECK_LIB(bz2,BZ2_bzDecompressInit) 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_ONCE([lzmadec.h]) AC_CHECK_LIB(lzmadec,lzmadec_decode) 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_ONCE([lzma.h]) AC_CHECK_LIB(lzma,lzma_stream_decoder) fi AC_CHECK_HEADERS_ONCE([openssl/md5.h openssl/ripemd.h openssl/sha.h]) AC_CHECK_HEADERS_ONCE([md5.h]) AC_CHECK_HEADERS_ONCE([ripemd.h rmd160.h]) AC_CHECK_HEADERS_ONCE([sha.h sha1.h sha2.h sha256.h]) AC_SEARCH_LIBS(SHA1_Init, crypto ssl md) AC_CHECK_FUNC(MD5_Init) if test "x$ac_cv_func_MD5_Init" != "xyes"; then AC_CHECK_FUNC(MD5Init, [AC_DEFINE(MD5_Init, MD5Init, [Define to the initializes MD5 context function.]) AC_DEFINE(MD5_Update, MD5Update, [Define to the updates MD5 context function.]) AC_DEFINE(MD5_Final, MD5Final, [Define to the generates final MD5 hash function.]) ]) fi if test "x$ac_cv_func_MD5_Init" = "xyes" -o "x$ac_cv_func_MD5Init" = "xyes" ; then AC_DEFINE(HAVE_MD5, 1, [Define to 1 if you have the `MD5' functions.]) fi AC_CHECK_FUNC(RIPEMD160_Init) if test "x$ac_cv_func_RIPEMD160_Init" != "xyes"; then AC_CHECK_FUNC(RMD160Init, [AC_DEFINE(RIPEMD160_Init, RMD160Init, [Define to the initializes RIPEMD160 context function.]) AC_DEFINE(RIPEMD160_Update, RMD160Update, [Define to the updates RIPEMD160 context function.]) AC_DEFINE(RIPEMD160_Final, RMD160Final, [Define to the generates final RIPEMD160 hash function.]) ]) fi if test "x$ac_cv_func_RIPEMD160_Init" = "xyes" -o "x$ac_cv_func_RMD160Init" = "xyes" ; then AC_DEFINE(HAVE_RMD160, 1, [Define to 1 if you have the `RIPEMD160' functions.]) fi AC_CHECK_FUNC(SHA1_Init) if test "x$ac_cv_func_SHA1_Init" != "xyes"; then AC_CHECK_FUNC(SHA1Init, [AC_DEFINE(SHA1_Init, SHA1Init, [Define to the initializes SHA1 context function.]) AC_DEFINE(SHA1_Update, SHA1Update, [Define to the updates SHA1 context function.]) AC_DEFINE(SHA1_Final, SHA1Final, [Define to the generates final SHA1 hash function.]) ]) fi if test "x$ac_cv_func_SHA1_Init" = "xyes" -o "x$ac_cv_func_SHA1Init" = "xyes" ; then AC_DEFINE(HAVE_SHA1, 1, [Define to 1 if you have the `SHA1' functions.]) fi AC_CHECK_FUNC(SHA256_Init) if test "x$ac_cv_func_SHA256_Init" = "xyes" ; then AC_DEFINE(HAVE_SHA256, 1, [Define to 1 if you have the `SHA256' functions.]) fi AC_CHECK_FUNC(SHA384_Init) if test "x$ac_cv_func_SHA384_Init" = "xyes" ; then AC_DEFINE(HAVE_SHA384, 1, [Define to 1 if you have the `SHA384' functions.]) fi AC_CHECK_FUNC(SHA512_Init) if test "x$ac_cv_func_SHA512_Init" = "xyes" ; then AC_DEFINE(HAVE_SHA512, 1, [Define to 1 if you have the `SHA512' functions.]) fi # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_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 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 need int64_t, uint64_t, intmax_t, and uintmax_t AC_TYPE_INTMAX_T AC_TYPE_INT64_T AC_TYPE_UINTMAX_T AC_TYPE_UINT64_T # TODO: If any of these are missing, define them right here. AC_CHECK_DECLS_ONCE([SIZE_MAX, SSIZE_MAX, INT64_MAX, INT64_MIN, UINT64_MAX, UINT32_MAX]) 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])], []) m4_version_prereq([2.70], [], [AC_HEADER_TIME]) # Checks for library functions. m4_version_prereq([2.72], [], [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 AC_CHECK_FUNCS_ONCE([chflags chown chroot]) AC_CHECK_FUNCS_ONCE([fchdir fchflags fchmod fchown fcntl fdopendir fork]) AC_CHECK_FUNCS_ONCE([fstat ftruncate futimes geteuid getpid]) AC_CHECK_FUNCS_ONCE([lchflags lchmod lchown]) AC_CHECK_FUNCS_ONCE([lutimes memmove memset mkdir mkfifo mknod]) AC_CHECK_FUNCS_ONCE([nl_langinfo pipe poll readlink select setenv setlocale]) AC_CHECK_FUNCS_ONCE([strchr strdup strerror strrchr timegm]) AC_CHECK_FUNCS_ONCE([tzset unsetenv utime utimes vfork]) AC_CHECK_FUNCS_ONCE([wcrtomb wcscpy wcslen wctomb wmemcmp wmemcpy]) AC_CHECK_FUNCS_ONCE([lockf posix_memalign qsort_r]) # Check for mmap so we can work around its absence on Minix AC_CHECK_FUNCS_ONCE([mmap]) # 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 ]) # If fnmatch() exists and supports FNM_LEADING_DIR, then bsdtar uses that, # otherwise it uses some usable (but less featureful) built-in code for # filename pattern matching. AC_FUNC_FNMATCH AC_CHECK_DECL([FNM_LEADING_DIR], [AC_DEFINE(HAVE_FNM_LEADING_DIR, 1, [Define to 1 if fnmatch(3) supports the FNM_LEADING_DIR flag])], [], [#if HAVE_FNMATCH #define _GNU_SOURCE /* Required on Linux to get GNU extensions */ #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], [Disable Extended Attributes support (default: check)])) if test "x$enable_xattr" != "xno"; then AC_CHECK_HEADERS_ONCE([attr/xattr.h]) AC_CHECK_HEADERS_ONCE([sys/xattr.h]) AC_CHECK_LIB(attr,setxattr) AC_CHECK_FUNCS_ONCE([extattr_get_file extattr_list_file]) AC_CHECK_FUNCS_ONCE([extattr_set_fd extattr_set_file]) AC_CHECK_FUNCS_ONCE([fsetxattr getxattr]) AC_CHECK_FUNCS_ONCE([lgetxattr listxattr llistxattr lsetxattr]) 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], [Disable ACL support (default: check)])) if test "x$enable_acl" != "xno"; then AC_CHECK_HEADERS_ONCE([sys/acl.h]) AC_CHECK_LIB([acl],[acl_get_file]) AC_CHECK_FUNCS_ONCE([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,,, [#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 # Interix doesn't provide optarg and optind AC_CHECK_DECLS_ONCE([optarg]) AC_CHECK_DECLS_ONCE([optind]) # Strip the .sample from tarsnap.conf.sample (if requested by the user). AC_ARG_WITH([conf-no-sample], AS_HELP_STRING([--with-conf-no-sample], [Install tarsnap.conf without the .sample suffix.]), [], [with_conf_no_sample=no]) AM_CONDITIONAL([INSTALL_CONF_NO_SAMPLE], [test "x$with_conf_no_sample" != "xno"]) # install bash completion AC_ARG_WITH([bash-completion-dir], AS_HELP_STRING([--with-bash-completion-dir@<:@=DIRNAME@:>@], [Install bash completion script. @<:@default=no@:>@]), [], [with_bash_completion_dir=no]) # by default, use what the user told us BASH_COMPLETION_DIR="$with_bash_completion_dir" # install zsh completion. Unlike --with-bash-completion-dir, DIRNAME is not # optional; this script has no mechanism for automatically determining the zsh # completion directory. AC_ARG_WITH([zsh-completion-dir], AS_HELP_STRING([--with-zsh-completion-dir=DIRNAME], [Install zsh completion script. @<:@default=no@:>@]), [], [with_zsh_completion_dir=no]) # use what the user told us ZSH_COMPLETION_DIR="$with_zsh_completion_dir" # print results AC_MSG_CHECKING([whether to install bash completion]) AS_IF([test "x$with_bash_completion_dir" != "xno"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) # Check whether the user has requested to disable compiler warnings CHECK_DISABLE_COMPILER_WARNINGS # should we set the value automatically? if test "x$with_bash_completion_dir" = "xyes"; then # try to use pkg-config AC_CHECK_PROG(HAS_PKG_CONFIG, pkg-config, yes) if test "x$HAS_PKG_CONFIG" != "xyes"; then AC_MSG_ERROR([Automatic detection of bash-completion directory requires pkg-config.]) else # find the actual value from the system PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"], [BASH_COMPLETION_DIR="/etc/bash_completion.d/"; AC_MSG_WARN([Automatic detection of bash-completion directory requires bash-completion >= 2.0; guessing a directory.])]) AC_MSG_CHECKING([bash completions directory]) AC_MSG_RESULT([$BASH_COMPLETION_DIR]) fi fi # pass values to Makefile.am AC_SUBST([BASH_COMPLETION_DIR]) AM_CONDITIONAL([INSTALL_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) AC_SUBST([ZSH_COMPLETION_DIR]) AM_CONDITIONAL([INSTALL_ZSH_COMPLETION],[test "x$with_zsh_completion_dir" != "xno"]) # Run the libcperciva POSIX compatibility tests and pass values to the Makefile. CHECK_LIBCPERCIVA_POSIX([${srcdir}/libcperciva]) AC_OUTPUT tarsnap-1.0.41/keygen/000077500000000000000000000000001476705112600145405ustar00rootroot00000000000000tarsnap-1.0.41/keygen/keygen.c000066400000000000000000000064671476705112600162030ustar00rootroot00000000000000#include "platform.h" #include #include #include #include #include #include #include #include #include #include #include "crypto.h" #include "getopt.h" #include "humansize.h" #include "keyfile.h" #include "keygen.h" #include "readpass.h" #include "sysendian.h" #include "tarsnap_opt.h" #include "tsnetwork.h" #include "warnp.h" static void usage(void); /* Be noisy about network errors while registering a machine. */ int tarsnap_opt_noisy_warnings = 1; static void usage(void) { fprintf(stderr, "usage: tarsnap-keygen %s %s %s %s %s %s\n", "--keyfile key-file", "--user user-name", "--machine machine-name", "[--passphrased]", "[--passphrase-mem maxmem]", "[--passphrase-time maxtime]"); fprintf(stderr, " tarsnap-keygen --version\n"); exit(1); /* NOTREACHED */ } int main(int argc, char **argv) { struct register_internal C; const char * keyfilename; int passphrased; uint64_t maxmem; double maxtime; const char * ch; WARNP_INIT; /* We have no username, machine name, or key filename yet. */ C.user = C.name = NULL; keyfilename = NULL; /* * So far we're not using a passphrase, have unlimited RAM, and allow * up to 1 second of CPU time. */ passphrased = 0; maxmem = 0; maxtime = 1.0; /* Parse arguments. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("--user"): if (C.user != NULL) usage(); C.user = optarg; break; GETOPT_OPTARG("--machine"): if (C.name != NULL) usage(); C.name = optarg; break; GETOPT_OPTARG("--keyfile"): if (keyfilename != NULL) usage(); keyfilename = optarg; break; GETOPT_OPTARG("--passphrase-mem"): if (maxmem != 0) usage(); if (humansize_parse(optarg, &maxmem)) { warnp("Cannot parse --passphrase-mem" " argument: %s", optarg); exit(1); } break; GETOPT_OPTARG("--passphrase-time"): if (maxtime != 1.0) usage(); maxtime = strtod(optarg, NULL); if ((maxtime < 0.05) || (maxtime > 86400)) { warn0("Invalid --passphrase-time argument: %s", optarg); exit(1); } break; GETOPT_OPT("--passphrased"): if (passphrased != 0) usage(); passphrased = 1; break; GETOPT_OPT("--version"): fprintf(stderr, "tarsnap-keygen %s\n", PACKAGE_VERSION); exit(0); GETOPT_MISSING_ARG: warn0("Missing argument to %s", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } } argc -= optind; argv += optind; /* We should have processed all the arguments. */ if (argc != 0) usage(); (void)argv; /* argv is not used beyond this point. */ /* We must have a user name, machine name, and key file specified. */ if ((C.user == NULL) || (C.name == NULL) || (keyfilename == NULL)) usage(); /* * It doesn't make sense to specify --passphrase-mem or * --passphrase-time if we're not using a passphrase. */ if (((maxmem != 0) || (maxtime != 1.0)) && (passphrased == 0)) usage(); /* * Use shared code between keygen and keyregen for the actual * processing. The NULL parameter is the oldkeyfilename (used for * keyregen) */ if (keygen_actual(&C, keyfilename, passphrased, maxmem, maxtime, NULL) != 0) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (1); } tarsnap-1.0.41/keygen/keygen.h000066400000000000000000000023111476705112600161700ustar00rootroot00000000000000#ifndef KEYGEN_H_ #define KEYGEN_H_ #include struct register_internal { /* Parameters provided from main() to network code. */ const char * user; char * passwd; const char * name; /* State information. */ int donechallenge; int done; /* Key used to send challenge response and verify server response. */ uint8_t register_key[32]; /* Data returned by server. */ uint8_t status; uint64_t machinenum; }; /** * keygen_network_register(C): * Register a new machine key with the Tarsnap server. */ int keygen_network_register(struct register_internal *); /** * Create key files (either new keys or regenerated keys) and * register with the server. ${C} is general information for * keygen code. ${keyfilename} is the new key filename. * ${passphrased} and ${maxmem} are command-line arguments for * adding a passphrase to the key and how much memory scrypt can * use, respectively. ${oldkeyfilename} is the old key filename * for keyregen, and must be NULL for keygen (a new key). */ int keygen_actual(struct register_internal * C, const char * keyfilename, const int passphrased, const uint64_t maxmem, const double maxtime, const char * oldkeyfilename); #endif /* !KEYGEN_H_ */ tarsnap-1.0.41/keygen/keygen_actual.c000066400000000000000000000113251476705112600175210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "crypto.h" #include "insecure_memzero.h" #include "keyfile.h" #include "keygen.h" #include "passphrase_entry.h" #include "readpass.h" #include "sysendian.h" #include "tarsnap_opt.h" #include "warnp.h" static int check_printable(const char * str) { size_t i; /* * Bail if there's any unprintable character in the "C" locale. This * assumes that we haven't run setlocale(), so this process is using * the "C" locale by default (as specified in C99). */ for (i = 0; i < strlen(str); i++) { if (!isprint(str[i])) return (1); } /* Success! */ return (0); } int keygen_actual(struct register_internal * C, const char * keyfilename, const int passphrased, const uint64_t maxmem, const double maxtime, const char *oldkeyfilename) { FILE * keyfile; char * passphrase = NULL; int keymask = CRYPTO_KEYMASK_USER; uint64_t dummy; /* Sanity-check the user name. */ if (strlen(C->user) > 255) { fprintf(stderr, "User name too long: %s\n", C->user); goto err0; } if (strlen(C->user) == 0) { fprintf(stderr, "User name must be non-empty\n"); goto err0; } /* Sanity-check the machine name. */ if (strlen(C->name) > 255) { fprintf(stderr, "Machine name too long: %s\n", C->name); goto err0; } if (strlen(C->name) == 0) { fprintf(stderr, "Machine name must be non-empty\n"); goto err0; } /* The machine name must be printable. */ if (check_printable(C->name)) { warn0("Machine name must be printable 7-bit ASCII: %s", C->name); goto err0; } /* Sanity-check the memory size. */ if (maxmem > SIZE_MAX) { fprintf(stderr, "Passphrase memory size is too large\n"); goto err0; } /* Get a password. */ if (readpass(&C->passwd, "Enter tarsnap account password", NULL, 0)) { warnp("Error reading password"); goto err0; } /* * Create key file -- we do this now rather than later so that we * avoid registering with the server if we won't be able to create * the key file later. */ if ((keyfile = keyfile_write_open(keyfilename)) == NULL) { warnp("Cannot create %s", keyfilename); goto err1; } /* Initialize key cache. */ if (crypto_keys_init()) { warnp("Key cache initialization failed"); goto err3; } /* keyregen (with oldkeyfilename) only regenerates certain keys. */ if (oldkeyfilename != NULL) { /* * Load the keys CRYPTO_KEY_HMAC_{CHUNK, NAME, CPARAMS} * from the old key file, since these are the keys which need * to be consistent in order for two key sets to be * compatible. (CHUNK and NAME are used to compute the * 32-byte keys for blocks; CPARAMS is used to compute * parameters used to split a stream of bytes into chunks.) */ if (keyfile_read(oldkeyfilename, &dummy, CRYPTO_KEYMASK_HMAC_CHUNK | CRYPTO_KEYMASK_HMAC_NAME | CRYPTO_KEYMASK_HMAC_CPARAMS, 0, PASSPHRASE_TTY_STDIN, NULL)) { warnp("Error reading old key file"); goto err3; } /* * Adjust the keymask to avoid regenerating keys we read from * the old keyfile. */ keymask &= ~CRYPTO_KEYMASK_HMAC_CHUNK & ~CRYPTO_KEYMASK_HMAC_NAME & ~CRYPTO_KEYMASK_HMAC_CPARAMS; } /* Generate keys. */ if (crypto_keys_generate(keymask)) { warnp("Error generating keys"); goto err3; } /* Register the keys with the server. */ if (keygen_network_register(C) != 0) goto err3; /* Exit with a code of 1 if we couldn't register. */ if (C->machinenum == (uint64_t)(-1)) goto err3; /* If the user wants to passphrase the keyfile, get the passphrase. */ if (passphrased != 0) { if (readpass(&passphrase, "Please enter passphrase for keyfile encryption", "Please confirm passphrase for keyfile encryption", 1)) { warnp("Error reading password"); goto err3; } } /* Write keys to file. */ if (keyfile_write_file(keyfile, C->machinenum, CRYPTO_KEYMASK_USER, passphrase, (size_t)maxmem, maxtime)) goto err3; /* Close the key file. */ if (fclose(keyfile)) { warnp("Error closing key file"); goto err2; } /* Free allocated memory. C->passwd is a NUL-terminated string. */ insecure_memzero(C->passwd, strlen(C->passwd)); free(C->passwd); /* Free passphrase, if used. passphrase is a NUL-terminated string. */ if (passphrase != NULL) { insecure_memzero(passphrase, strlen(passphrase)); free(passphrase); } /* Success! */ return (0); err3: fclose(keyfile); err2: unlink(keyfilename); err1: insecure_memzero(C->passwd, strlen(C->passwd)); free(C->passwd); if (passphrase != NULL) { insecure_memzero(passphrase, strlen(passphrase)); free(passphrase); } err0: /* Failure! */ return (-1); } tarsnap-1.0.41/keygen/keygen_network.c000066400000000000000000000131351476705112600177420ustar00rootroot00000000000000#include #include #include "crypto.h" #include "crypto_dh.h" #include "crypto_verify_bytes.h" #include "keygen.h" #include "netpacket.h" #include "netproto.h" #include "sysendian.h" #include "tsnetwork.h" #include "warnp.h" static sendpacket_callback callback_register_send; static handlepacket_callback callback_register_challenge; static handlepacket_callback callback_register_response; /** * keygen_network_register(C): * Register a new machine key with the Tarsnap server. */ int keygen_network_register(struct register_internal * C) { NETPACKET_CONNECTION * NPC; C->done = 0; C->donechallenge = 0; C->machinenum = (uint64_t)(-1); /* Open netpacket connection. */ if ((NPC = netpacket_open(USERAGENT)) == NULL) goto err2; /* Ask the netpacket layer to send a request and get a response. */ if (netpacket_op(NPC, callback_register_send, C)) goto err3; /* Run event loop until an error occurs or we're done. */ if (network_spin(&C->done)) goto err3; /* Close netpacket connection. */ if (netpacket_close(NPC)) goto err2; /* * If we didn't respond to a challenge, the server's response must * have been a "no such user" error. */ if ((C->donechallenge == 0) && (C->status != 1)) { netproto_printerr(NETPROTO_STATUS_PROTERR); goto err1; } /* The machine number should be -1 iff the status is nonzero. */ if (((C->machinenum == (uint64_t)(-1)) && (C->status == 0)) || ((C->machinenum != (uint64_t)(-1)) && (C->status != 0))) { netproto_printerr(NETPROTO_STATUS_PROTERR); goto err1; } /* Parse status returned by server. */ switch (C->status) { case 0: /* Success! */ break; case 1: warn0("No such user: %s", C->user); break; case 2: warn0("Incorrect password"); break; case 3: warn0("Cannot register with server: " "Account balance for user %s is not positive", C->user); break; default: netproto_printerr(NETPROTO_STATUS_PROTERR); goto err2; } /* Shut down the network event loop. */ network_fini(); /* Success! */ return (0); err3: netpacket_close(NPC); err2: warnp("Error registering with server"); err1: /* Shut down the network event loop. */ network_fini(); /* Failure! */ return (-1); } static int callback_register_send(void * cookie, NETPACKET_CONNECTION * NPC) { struct register_internal * C = cookie; /* Tell the server which user is trying to add a machine. */ return (netpacket_register_request(NPC, C->user, callback_register_challenge)); } static int callback_register_challenge(void * cookie, NETPACKET_CONNECTION * NPC, int status, uint8_t packettype, const uint8_t * packetbuf, size_t packetlen) { struct register_internal * C = cookie; uint8_t pub[CRYPTO_DH_PUBLEN]; uint8_t priv[CRYPTO_DH_PRIVLEN]; uint8_t K[CRYPTO_DH_KEYLEN]; uint8_t keys[96]; /* Handle errors. */ if (status != NETWORK_STATUS_OK) { netproto_printerr(status); goto err0; } /* * Make sure we received the right type of packet. It is legal for * the server to send back a NETPACKET_REGISTER_RESPONSE at this * point; call callback_register_response to handle those. Note that * NETPACKET_REGISTER_RESPONSE at this point must be a "no such user" * response; we verify this in callback_register_response. */ if (packettype == NETPACKET_REGISTER_RESPONSE) return (callback_register_response(cookie, NPC, status, packettype, packetbuf, packetlen)); else if (packettype != NETPACKET_REGISTER_CHALLENGE) { netproto_printerr(NETPROTO_STATUS_PROTERR); goto err0; } /* Generate DH parameters from the password and salt. */ if (crypto_passwd_to_dh(C->passwd, packetbuf, pub, priv)) { warnp("Could not generate DH parameter from password"); goto err0; } /* Compute shared key. */ if (crypto_dh_compute(&packetbuf[32], priv, K)) goto err0; if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, K, CRYPTO_DH_KEYLEN, C->register_key)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* Export access keys. */ if (crypto_keys_raw_export_auth(keys)) goto err0; /* Send challenge response packet. */ if (netpacket_register_cha_response(NPC, keys, C->name, C->register_key, callback_register_response)) goto err0; /* We've responded to a challenge. */ C->donechallenge = 1; /* Success! */ return (0); err0: /* Failure! */ return (-1); } static int callback_register_response(void * cookie, NETPACKET_CONNECTION * NPC, int status, uint8_t packettype, const uint8_t * packetbuf, size_t packetlen) { struct register_internal * C = cookie; uint8_t hmac_actual[32]; (void)NPC; /* UNUSED */ (void)packetlen; /* UNUSED */ /* Handle errors. */ if (status != NETWORK_STATUS_OK) { netproto_printerr(status); goto err0; } /* Make sure we received the right type of packet. */ if (packettype != NETPACKET_REGISTER_RESPONSE) goto err1; /* * If the challenge has not been responded to (i.e. this packet * arrived in response to a NETPACKET_REGISTER_REQUEST packet), this * should be a "no such user" response. */ if ((C->donechallenge == 0) && (packetbuf[0] != 1)) goto err1; /* Verify packet hmac. */ if ((packetbuf[0] == 0) || (packetbuf[0] == 3)) { crypto_hash_data_key_2(C->register_key, 32, &packettype, 1, packetbuf, 9, hmac_actual); } else { memset(hmac_actual, 0, 32); } if (crypto_verify_bytes(hmac_actual, &packetbuf[9], 32)) goto err1; /* Record status code and machine number returned by server. */ C->status = packetbuf[0]; C->machinenum = be64dec(&packetbuf[1]); /* We have received a response. */ C->done = 1; /* Success! */ return (0); err1: netproto_printerr(NETPROTO_STATUS_PROTERR); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/keygen/tarsnap-keygen.1-man000066400000000000000000000033261476705112600203270ustar00rootroot00000000000000.TH TARSNAP-KEYGEN 1 "@DATE@" "" .SH NAME .ad l \fB\%tarsnap-keygen\fP \- generate a key file for use with \fBtarsnap\fP(1) .SH SYNOPSIS .ad l .br \fB\%tarsnap-keygen\fP \fB\--keyfile\fP \fIkey-file\fP \fB\--user\fP \fIuser-name\fP \fB\--machine\fP \fImachine-name\fP [\fB\--passphrased\fP] [\fB\--passphrase-mem\fP \fImaxmem\fP] [\fB\--passphrase-time\fP \fImaxtime\fP] .br \fB\%tarsnap-keygen\fP \fB\--version\fP .SH DESCRIPTION .ad l \fB\%tarsnap-keygen\fP generates cryptographic keys, registers with the tarsnap server, and writes a key file for use with \fBtarsnap\fP(1). .PP The \fB\--keyfile\fP \fIkey-file\fP option specifies the name of the file in which to write the newly-generated keys. The \fB\--user\fP \fIuser-name\fP option specifies the name (i.e. email address) of the Tarsnap account. The \fB\--machine\fP \fImachine-name\fP option specifies a name which will be displayed in accounting reports so that you can see how much data each machine is storing. .PP If the \fB\--passphrased\fP option is specified, the user will be prompted to enter a passphrase (twice) to be used to encrypt the key file. .PP If the \fB\--passphrase-mem\fP \fImaxmem\fP option is specified, a maximum of \fImaxmem\fP bytes of RAM will be used in the scrypt key derivation function to encrypt the key file; it may be necessary to set this option if a key file is being generated on a system with far more RAM than the system on which the key file will be used. .PP If the \fB\--passphrase-time\fP \fImaxtime\fP option is specified, a maximum of approximately \fImaxtime\fP seconds will be used in the scrypt key derivation function to encrypt the key file. .PP The \fB\--version\fP option prints the version number of \fB\%tarsnap-keygen\fP, then exits. tarsnap-1.0.41/keygen/tarsnap-keygen.1-mdoc000066400000000000000000000031361476705112600204750ustar00rootroot00000000000000.\" Copyright 2007-2025 Tarsnap Backup Inc. .\" All rights reserved. .\" .Dd @DATE@ .Dt TARSNAP-KEYGEN 1 .Os .Sh NAME .Nm tarsnap-keygen .Nd generate a key file for use with .Xr tarsnap 1 .Sh SYNOPSIS .Nm .Fl -keyfile Ar key-file .Fl -user Ar user-name .Fl -machine Ar machine-name .Op Fl -passphrased .Op Fl -passphrase-mem Ar maxmem .Op Fl -passphrase-time Ar maxtime .Nm .Fl -version .Sh DESCRIPTION .Nm generates cryptographic keys, registers with the tarsnap server, and writes a key file for use with .Xr tarsnap 1 . .Pp The .Fl -keyfile Ar key-file option specifies the name of the file in which to write the newly-generated keys. The .Fl -user Ar user-name option specifies the name (i.e. email address) of the Tarsnap account. The .Fl -machine Ar machine-name option specifies a name which will be displayed in accounting reports so that you can see how much data each machine is storing. .Pp If the .Fl -passphrased option is specified, the user will be prompted to enter a passphrase (twice) to be used to encrypt the key file. .Pp If the .Fl -passphrase-mem Ar maxmem option is specified, a maximum of .Ar maxmem bytes of RAM will be used in the scrypt key derivation function to encrypt the key file; it may be necessary to set this option if a key file is being generated on a system with far more RAM than the system on which the key file will be used. .Pp If the .Fl -passphrase-time Ar maxtime option is specified, a maximum of approximately .Ar maxtime seconds will be used in the scrypt key derivation function to encrypt the key file. .Pp The .Fl -version option prints the version number of .Nm , then exits. tarsnap-1.0.41/keymgmt/000077500000000000000000000000001476705112600147335ustar00rootroot00000000000000tarsnap-1.0.41/keymgmt/keymgmt.c000066400000000000000000000203771476705112600165650ustar00rootroot00000000000000#include "platform.h" #include #include #include #include #include #include #include "crypto.h" #include "getopt.h" #include "humansize.h" #include "keyfile.h" #include "passphrase_entry.h" #include "readpass.h" #include "warnp.h" static void usage(void); static void usage(void) { fprintf(stderr, "usage: tarsnap-keymgmt %s %s %s %s %s key-file ...\n", "--outkeyfile new-key-file", "[--passphrased]", "[--passphrase-mem maxmem]", "[--passphrase-time maxtime]", "[-r] [-w] [-d] [--nuke]"); fprintf(stderr, " tarsnap-keymgmt --print-key-id key-file\n"); fprintf(stderr, " tarsnap-keymgmt --print-key-permissions " "key-file\n"); fprintf(stderr, " tarsnap-keymgmt --version\n"); exit(1); /* NOTREACHED */ } static void print_id(const char *keyfilename) { uint64_t machinenum = (uint64_t)(-1); /* Read keyfile and machine name. */ if (keyfile_read(keyfilename, &machinenum, ~0, 0, PASSPHRASE_TTY_STDIN, NULL)) { warnp("Cannot read key file: %s", keyfilename); exit(1); } /* Print key ID. */ fprintf(stdout, "%" PRIu64 "\n", machinenum); exit(0); /* NOTREACHED */ } static void print_permissions(const char *keyfilename) { uint64_t machinenum = (uint64_t)(-1); int has_read; int has_write; int has_delete; /* Read keyfile and machine name. */ if (keyfile_read(keyfilename, &machinenum, ~0, 0, PASSPHRASE_TTY_STDIN, NULL)) { warnp("Cannot read key file: %s", keyfilename); exit(1); } /* Determine permissions. */ has_read = (crypto_keys_missing(CRYPTO_KEYMASK_READ) == NULL); has_write = (crypto_keys_missing(CRYPTO_KEYMASK_WRITE) == NULL); has_delete = (crypto_keys_missing(CRYPTO_KEYMASK_AUTH_DELETE) == NULL); /* Print key permissions. */ fprintf(stdout, "This key has permissions for: "); if (has_read && has_write && has_delete) fprintf(stdout, "reading, writing, and deleting.\n"); if (has_read && has_write && !has_delete) fprintf(stdout, "reading and writing.\n"); if (has_read && !has_write && has_delete) fprintf(stdout, "reading and deleting.\n"); if (has_read && !has_write && !has_delete) fprintf(stdout, "reading.\n"); if (!has_read && has_write && has_delete) fprintf(stdout, "writing and nuking.\n"); if (!has_read && has_write && !has_delete) fprintf(stdout, "writing.\n"); if (!has_read && !has_write && has_delete) fprintf(stdout, "nuking.\n"); if (!has_read && !has_write && !has_delete) fprintf(stdout, "nothing.\n"); exit(0); /* NOTREACHED */ } int main(int argc, char **argv) { const char * newkeyfile = NULL; int keyswanted = 0; char * tok, * brkb = NULL, * eptr; long keynum; uint64_t machinenum = (uint64_t)(-1); uint64_t kfmachinenum; const char * missingkey; int passphrased = 0; uint64_t maxmem = 0; double maxtime = 1.0; char * passphrase; const char * print_key_id_file = NULL; const char * print_key_permissions_file = NULL; const char * ch; char * optarg_copy; /* for strtok_r. */ WARNP_INIT; /* Initialize key cache. */ if (crypto_keys_init()) { warnp("Key cache initialization failed"); exit(1); } /* Parse arguments. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("--outkeyfile"): if (newkeyfile != NULL) usage(); newkeyfile = optarg; break; GETOPT_OPT("-r"): keyswanted |= CRYPTO_KEYMASK_READ; break; GETOPT_OPT("-w"): keyswanted |= CRYPTO_KEYMASK_WRITE; break; GETOPT_OPT("-d"): /* * Deleting data requires both delete authorization * and being able to read archives -- we need to be * able to figure out which bits are part of the * archive. */ keyswanted |= CRYPTO_KEYMASK_READ; keyswanted |= CRYPTO_KEYMASK_AUTH_DELETE; break; GETOPT_OPT("--nuke"): keyswanted |= CRYPTO_KEYMASK_AUTH_DELETE; break; GETOPT_OPTARG("--keylist"): /* * This is a deliberately undocumented option used * mostly for testing purposes; it allows a list of * keys to be specified according to their numbers in * crypto/crypto.h instead of using the predefined * sets of "read", "write" and "delete" keys. */ if ((optarg_copy = strdup(optarg)) == NULL) { warn0("Out of memory"); exit(0); } for (tok = strtok_r(optarg_copy, ",", &brkb); tok; tok = strtok_r(NULL, ",", &brkb)) { keynum = strtol(tok, &eptr, 0); if ((eptr == tok) || (keynum < 0) || (keynum > 31)) { warn0("Not a valid key number: %s", tok); free(optarg_copy); exit(1); } keyswanted |= (uint32_t)(1) << keynum; } free(optarg_copy); break; GETOPT_OPTARG("--passphrase-mem"): if (maxmem != 0) usage(); if (humansize_parse(optarg, &maxmem)) { warnp("Cannot parse --passphrase-mem" " argument: %s", optarg); exit(1); } if (maxmem > SIZE_MAX) { fprintf(stderr, "Passphrase memory size is too large\n"); exit(1); } break; GETOPT_OPTARG("--passphrase-time"): if (maxtime != 1.0) usage(); maxtime = strtod(optarg, NULL); if ((maxtime < 0.05) || (maxtime > 86400)) { warn0("Invalid --passphrase-time argument: %s", optarg); exit(1); } break; GETOPT_OPT("--passphrased"): if (passphrased != 0) usage(); passphrased = 1; break; GETOPT_OPTARG("--print-key-id"): if (print_key_id_file != NULL) usage(); print_key_id_file = optarg; break; GETOPT_OPTARG("--print-key-permissions"): if (print_key_permissions_file != NULL) usage(); print_key_permissions_file = optarg; break; GETOPT_OPT("--version"): fprintf(stderr, "tarsnap-keymgmt %s\n", PACKAGE_VERSION); exit(0); GETOPT_MISSING_ARG: warn0("Missing argument to %s", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } } argc -= optind; argv += optind; /* We can't print ID and permissions at the same time. */ if ((print_key_id_file != NULL) && (print_key_permissions_file != NULL)) usage(); if ((print_key_id_file != NULL) || (print_key_permissions_file != NULL)) { /* We can't combine printing info with generating a new key. */ if (newkeyfile != NULL) usage(); /* We should have processed all arguments. */ if (argc != 0) usage(); /* Print info. */ if (print_key_id_file != NULL) print_id(print_key_id_file); if (print_key_permissions_file != NULL) print_permissions(print_key_permissions_file); } /* We should have an output key file. */ if (newkeyfile == NULL) usage(); /* * It doesn't make sense to specify --passphrase-mem or * --passphrase-time if we're not using a passphrase. */ if (((maxmem != 0) || (maxtime != 1.0)) && (passphrased == 0)) usage(); /* Warn the user if they're being silly. */ if (keyswanted == 0) { warn0("None of {-r, -w, -d, --nuke} options are specified." " This will create a key file with no keys, which is" " probably not what you intended."); } /* Read the specified key files. */ while (argc-- > 0) { /* * Suck in the key file. We could mask this to only load the * keys we want to copy, but there's no point really since we * export keys selectively. */ if (keyfile_read(argv[0], &kfmachinenum, ~0, 0, PASSPHRASE_TTY_STDIN, NULL)) { warnp("Cannot read key file: %s", argv[0]); exit(1); } /* * Check that we're not using key files which belong to * different machines. */ if (machinenum == (uint64_t)(-1)) { machinenum = kfmachinenum; } else if (machinenum != kfmachinenum) { warn0("Keys from %s do not belong to the " "same machine as earlier keys", argv[0]); exit(1); } /* Move on to the next file. */ argv++; } /* Make sure that we have the necessary keys. */ if ((missingkey = crypto_keys_missing(keyswanted)) != NULL) { warn0("The %s key is required but not in any input key files", missingkey); exit(1); } /* If the user wants to passphrase the keyfile, get the passphrase. */ if (passphrased != 0) { if (readpass(&passphrase, "Please enter passphrase for keyfile encryption", "Please confirm passphrase for keyfile encryption", 1)) { warnp("Error reading password"); exit(1); } } else { passphrase = NULL; } /* Write out new key file. */ if (keyfile_write(newkeyfile, machinenum, keyswanted, passphrase, (size_t)maxmem, maxtime)) exit(1); /* Success! */ return (0); } tarsnap-1.0.41/keymgmt/tarsnap-keymgmt.1-man000066400000000000000000000057061476705112600207210ustar00rootroot00000000000000.TH TARSNAP-KEYMGMT 1 "@DATE@" "" .SH NAME .ad l \fB\%tarsnap-keymgmt\fP \- generate subsets of \fBtarsnap\fP(1) key files .SH SYNOPSIS .ad l .br \fB\%tarsnap-keymgmt\fP \fB\--outkeyfile\fP \fInew-key-file\fP [\fB\-r\fP] [\fB\-w\fP] [\fB\-d\fP] [\fB\--nuke\fP] [\fB\--passphrased\fP] [\fB\--passphrase-mem\fP \fImaxmem\fP] [\fB\--passphrase-time\fP \fImaxtime\fP] \fIkey-file\fP ... .br \fB\%tarsnap-keymgmt\fP \fB\--print-key-id\fP \fIkey-file\fP .br \fB\%tarsnap-keymgmt\fP \fB\--print-key-permissions\fP \fIkey-file\fP .br \fB\%tarsnap-keymgmt\fP \fB\--version\fP .SH DESCRIPTION .ad l \fB\%tarsnap-keymgmt\fP reads the provided key files and writes a new key file (specified by \fB\--outkeyfile\fP \fInew-key-file\fP) containing only the keys required for the operations specified via the \fB\-r\fP (list and extract archives), \fB\-w\fP (write archives), \fB\-d\fP (delete archives), and \fB\--nuke\fP flags. Note that \fB\-d\fP implies \fB\-r\fP since it is impossible to delete an individual archive without being able to read it; while a key file generated with \fB\--nuke\fP can be used to delete all the archives stored, but not individual archives. .PP The following list shows which permissions are required for various \fBtarsnap\fP(1) .IR command modes. .RS 5 .TP .IR --recover requires either (1) \fB\-d\fP (archive deleting), (2) \fB\-w\fP (archive creating), or (3) \fB\--nuke\fP keys. .TP .IR --fsck requires either (1) both \fB\-w\fP (archive writing) and \fB\-r\fP (archive reading) keys, or (2) \fB\-d\fP (archive deleting) keys. .TP .IR --fsck-prune requires \fB\-d\fP (archive deleting) keys, since it needs to be able to delete corrupted archives. .RE .PP If the \fB\--passphrased\fP option is specified, the user will be prompted to enter a passphrase (twice) to be used to encrypt the key file. .PP If the \fB\--passphrase-mem\fP \fImaxmem\fP option is specified, a maximum of \fImaxmem\fP bytes of RAM will be used in the scrypt key derivation function to encrypt the key file; it may be necessary to set this option if a key file is being created on a system with far more RAM than the system on which the key file will be used. .PP If the \fB\--passphrase-time\fP \fImaxtime\fP option is specified, a maximum of approximately \fImaxtime\fP seconds will be used in the scrypt key derivation function to encrypt the key file. .PP Note that if none of the \fB\-w\fP, \fB\-r\fP, \fB\-d\fP, or \fB\--nuke\fP options are specified, a key file will be produced which does not contain any keys. This is probably not very useful. .PP The \fB\--print-key-id\fP \fIkey-file\fP option displays the 64-bit integer corresponding to the key's machine number. This may be useful for scripts or GUIs which manage a user's Tarsnap account, but is not likely to be helpful for command-line use. .PP The \fB\--print-key-permissions\fP \fIkey-file\fP option displays the permissions which the key possesses. .PP The \fB\--version\fP option prints the version number of \fB\%tarsnap-keymgmt\fP, then exits. tarsnap-1.0.41/keymgmt/tarsnap-keymgmt.1-mdoc000066400000000000000000000053351476705112600210660ustar00rootroot00000000000000.\" Copyright 2008-2025 Tarsnap Backup Inc. .\" All rights reserved. .\" .Dd @DATE@ .Dt TARSNAP-KEYMGMT 1 .Os .Sh NAME .Nm tarsnap-keymgmt .Nd generate subsets of .Xr tarsnap 1 key files .Sh SYNOPSIS .Nm .Fl -outkeyfile Ar new-key-file .Op Fl r .Op Fl w .Op Fl d .Op Fl -nuke .Op Fl -passphrased .Op Fl -passphrase-mem Ar maxmem .Op Fl -passphrase-time Ar maxtime .Ar key-file ... .Nm .Fl -print-key-id Ar key-file .Nm .Fl -print-key-permissions Ar key-file .Nm .Fl -version .Sh DESCRIPTION .Nm reads the provided key files and writes a new key file (specified by .Fl -outkeyfile Ar new-key-file ) containing only the keys required for the operations specified via the .Fl r (list and extract archives), .Fl w (write archives), .Fl d (delete archives), and .Fl -nuke flags. Note that .Fl d implies .Fl r since it is impossible to delete an individual archive without being able to read it; while a key file generated with .Fl -nuke can be used to delete all the archives stored, but not individual archives. .Pp The following list shows which permissions are required for various .Xr tarsnap 1 .Em command modes . .Bl -tag -width 4n -offset 4n .It Em --recover requires either (1) .Fl d (archive deleting), (2) .Fl w (archive creating), or (3) .Fl -nuke keys. .It Em --fsck requires either (1) both .Fl w (archive writing) and .Fl r (archive reading) keys, or (2) .Fl d (archive deleting) keys. .It Em --fsck-prune requires .Fl d (archive deleting) keys, since it needs to be able to delete corrupted archives. .El .Pp If the .Fl -passphrased option is specified, the user will be prompted to enter a passphrase (twice) to be used to encrypt the key file. .Pp If the .Fl -passphrase-mem Ar maxmem option is specified, a maximum of .Ar maxmem bytes of RAM will be used in the scrypt key derivation function to encrypt the key file; it may be necessary to set this option if a key file is being created on a system with far more RAM than the system on which the key file will be used. .Pp If the .Fl -passphrase-time Ar maxtime option is specified, a maximum of approximately .Ar maxtime seconds will be used in the scrypt key derivation function to encrypt the key file. .Pp Note that if none of the .Fl w , .Fl r , .Fl d , or .Fl -nuke options are specified, a key file will be produced which does not contain any keys. This is probably not very useful. .Pp The .Fl -print-key-id Ar key-file option displays the 64-bit integer corresponding to the key's machine number. This may be useful for scripts or GUIs which manage a user's Tarsnap account, but is not likely to be helpful for command-line use. .Pp The .Fl -print-key-permissions Ar key-file option displays the permissions which the key possesses. .Pp The .Fl -version option prints the version number of .Nm , then exits. tarsnap-1.0.41/keyregen/000077500000000000000000000000001476705112600150675ustar00rootroot00000000000000tarsnap-1.0.41/keyregen/keyregen.c000066400000000000000000000070331476705112600170470ustar00rootroot00000000000000#include "platform.h" #include #include #include #include #include #include #include #include #include #include #include "crypto.h" #include "getopt.h" #include "humansize.h" #include "keyfile.h" #include "keygen.h" #include "readpass.h" #include "sysendian.h" #include "tarsnap_opt.h" #include "tsnetwork.h" #include "warnp.h" static void usage(void); /* Be noisy about network errors while registering a machine. */ int tarsnap_opt_noisy_warnings = 1; static void usage(void) { fprintf(stderr, "usage: tarsnap-keyregen %s %s %s %s %s %s %s\n", "--keyfile key-file", "--oldkey old-key-file", "--user user-name", "--machine machine-name", "[--passphrased]", "[--passphrase-mem maxmem]", "[--passphrase-time maxtime]"); fprintf(stderr, " tarsnap-keyregen --version\n"); exit(1); /* NOTREACHED */ } int main(int argc, char **argv) { struct register_internal C; const char * keyfilename; const char * oldkeyfilename; int passphrased; uint64_t maxmem; double maxtime; const char * ch; WARNP_INIT; /* * We have no username, machine name, key filename, or old key * filename yet. */ C.user = C.name = NULL; keyfilename = NULL; oldkeyfilename = NULL; /* * So far we're not using a passphrase, have unlimited RAM, and allow * up to 1 second of CPU time. */ passphrased = 0; maxmem = 0; maxtime = 1.0; /* Parse arguments. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("--user"): if (C.user != NULL) usage(); C.user = optarg; break; GETOPT_OPTARG("--machine"): if (C.name != NULL) usage(); C.name = optarg; break; GETOPT_OPTARG("--keyfile"): if (keyfilename != NULL) usage(); keyfilename = optarg; break; GETOPT_OPTARG("--oldkey"): if (oldkeyfilename != NULL) usage(); oldkeyfilename = optarg; break; GETOPT_OPTARG("--passphrase-mem"): if (maxmem != 0) usage(); if (humansize_parse(optarg, &maxmem)) { warnp("Cannot parse --passphrase-mem" " argument: %s", optarg); exit(1); } break; GETOPT_OPTARG("--passphrase-time"): if (maxtime != 1.0) usage(); maxtime = strtod(optarg, NULL); if ((maxtime < 0.05) || (maxtime > 86400)) { warn0("Invalid --passphrase-time argument: %s", optarg); exit(1); } break; GETOPT_OPT("--passphrased"): if (passphrased != 0) usage(); passphrased = 1; break; GETOPT_OPT("--version"): fprintf(stderr, "tarsnap-keyregen %s\n", PACKAGE_VERSION); exit(0); GETOPT_MISSING_ARG: warn0("Missing argument to %s", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } } argc -= optind; argv += optind; /* We should have processed all the arguments. */ if (argc != 0) usage(); (void)argv; /* argv is not used beyond this point. */ /* * We must have a user name, machine name, key file, and old key * file specified. */ if ((C.user == NULL) || (C.name == NULL) || (keyfilename == NULL) || (oldkeyfilename == NULL)) usage(); /* * It doesn't make sense to specify --passphrase-mem or * --passphrase-time if we're not using a passphrase. */ if (((maxmem != 0) || (maxtime != 1.0)) && (passphrased == 0)) usage(); /* * Use shared code between keygen and keyregen for the actual * processing. */ if (keygen_actual(&C, keyfilename, passphrased, maxmem, maxtime, oldkeyfilename) != 0) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (1); } tarsnap-1.0.41/keyregen/tarsnap-keyregen.1-man000066400000000000000000000044571476705112600212130ustar00rootroot00000000000000.TH TARSNAP-KEYREGEN 1 "@DATE@" "" .SH NAME .ad l \fB\%tarsnap-keyregen\fP \- generate a key file for use with \fBtarsnap-recrypt\fP(1) .SH SYNOPSIS .ad l .br \fB\%tarsnap-keyregen\fP \fB\--keyfile\fP \fIkey-file\fP \fB\--oldkey\fP \fIold-key-file\fP \fB\--user\fP \fIuser-name\fP \fB\--machine\fP \fImachine-name\fP [\fB\--passphrased\fP] [\fB\--passphrase-mem\fP \fImaxmem\fP] [\fB\--passphrase-time\fP \fImaxtime\fP] .br \fB\%tarsnap-keyregen\fP \fB\--version\fP .SH DESCRIPTION .ad l \fB\%tarsnap-keyregen\fP generates a set of cryptographic keys which are compatible with an existing set of cryptographic keys, registers with the tarsnap server, and writes a key file for use with \fBtarsnap-recrypt\fP(1) and \fBtarsnap\fP(1). The term "compatible" here means that it is possible to re-encrypt archives stored with the first set of keys to be stored with the second set of keys. This is required because Tarsnap has some keys which need to stay the same when re-encrypting data; otherwise, existing archives will become unreadable and cannot be used for deduplication. .PP The \fB\--keyfile\fP \fIkey-file\fP option specifies the name of the file in which to write the newly-generated keys. The \fB\--oldkey\fP \fIold-key-file\fP option specifies the name of the file containing the old keys. The \fB\--user\fP \fIuser-name\fP option specifies the name (i.e. email address) of the Tarsnap account. The \fB\--machine\fP \fImachine-name\fP option specifies a name which will be displayed in accounting reports so that you can see how much data each machine is storing. .PP If the \fB\--passphrased\fP option is specified, the user will be prompted to enter a passphrase (twice) to be used to encrypt the key file. .PP If the \fB\--passphrase-mem\fP \fImaxmem\fP option is specified, a maximum of \fImaxmem\fP bytes of RAM will be used in the scrypt key derivation function to encrypt the key file; it may be necessary to set this option if a key file is being generated on a system with far more RAM than the system on which the key file will be used. .PP If the \fB\--passphrase-time\fP \fImaxtime\fP option is specified, a maximum of approximately \fImaxtime\fP seconds will be used in the scrypt key derivation function to encrypt the key file. .PP The \fB\--version\fP option prints the version number of \fB\%tarsnap-keyregen\fP, then exits. tarsnap-1.0.41/keyregen/tarsnap-keyregen.1-mdoc000066400000000000000000000042361476705112600213550ustar00rootroot00000000000000.\" Copyright 2011-2025 Tarsnap Backup Inc. .\" All rights reserved. .\" .Dd @DATE@ .Dt TARSNAP-KEYREGEN 1 .Os .Sh NAME .Nm tarsnap-keyregen .Nd generate a key file for use with .Xr tarsnap-recrypt 1 .Sh SYNOPSIS .Nm .Fl -keyfile Ar key-file .Fl -oldkey Ar old-key-file .Fl -user Ar user-name .Fl -machine Ar machine-name .Op Fl -passphrased .Op Fl -passphrase-mem Ar maxmem .Op Fl -passphrase-time Ar maxtime .Nm .Fl -version .Sh DESCRIPTION .Nm generates a set of cryptographic keys which are compatible with an existing set of cryptographic keys, registers with the tarsnap server, and writes a key file for use with .Xr tarsnap-recrypt 1 and .Xr tarsnap 1 . The term "compatible" here means that it is possible to re-encrypt archives stored with the first set of keys to be stored with the second set of keys. This is required because Tarsnap has some keys which need to stay the same when re-encrypting data; otherwise, existing archives will become unreadable and cannot be used for deduplication. .Pp The .Fl -keyfile Ar key-file option specifies the name of the file in which to write the newly-generated keys. The .Fl -oldkey Ar old-key-file option specifies the name of the file containing the old keys. The .Fl -user Ar user-name option specifies the name (i.e. email address) of the Tarsnap account. The .Fl -machine Ar machine-name option specifies a name which will be displayed in accounting reports so that you can see how much data each machine is storing. .Pp If the .Fl -passphrased option is specified, the user will be prompted to enter a passphrase (twice) to be used to encrypt the key file. .Pp If the .Fl -passphrase-mem Ar maxmem option is specified, a maximum of .Ar maxmem bytes of RAM will be used in the scrypt key derivation function to encrypt the key file; it may be necessary to set this option if a key file is being generated on a system with far more RAM than the system on which the key file will be used. .Pp If the .Fl -passphrase-time Ar maxtime option is specified, a maximum of approximately .Ar maxtime seconds will be used in the scrypt key derivation function to encrypt the key file. .Pp The .Fl -version option prints the version number of .Nm , then exits. tarsnap-1.0.41/lib-platform/000077500000000000000000000000001476705112600156465ustar00rootroot00000000000000tarsnap-1.0.41/lib-platform/crypto/000077500000000000000000000000001476705112600171665ustar00rootroot00000000000000tarsnap-1.0.41/lib-platform/crypto/crypto_scrypt.c000066400000000000000000000160171476705112600222630ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "platform.h" #include #include #include #include #include #include #include "cpusupport.h" #include "sha256.h" #include "warnp.h" #include "crypto_scrypt_smix.h" #include "crypto_scrypt_smix_sse2.h" #include "crypto_scrypt.h" static void (* smix_func)(uint8_t *, size_t, uint64_t, void *, void *) = NULL; /** * crypto_scrypt_internal(passwd, passwdlen, salt, saltlen, N, r, p, buf, * buflen, smix): * Perform the requested scrypt computation, using ${smix} as the smix routine. */ static int crypto_scrypt_internal(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, uint8_t * buf, size_t buflen, void (* smix)(uint8_t *, size_t, uint64_t, void *, void *)) { void * B0, * V0, * XY0; uint8_t * B; uint32_t * V; uint32_t * XY; size_t r = _r, p = _p; uint32_t i; /* Sanity-check parameters. */ if ((r == 0) || (p == 0)) { errno = EINVAL; goto err0; } #if SIZE_MAX > UINT32_MAX if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { errno = EFBIG; goto err0; } #endif if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { errno = EFBIG; goto err0; } if (((N & (N - 1)) != 0) || (N < 2)) { errno = EINVAL; goto err0; } if ((r > SIZE_MAX / 128 / p) || #if SIZE_MAX / 256 <= UINT32_MAX (r > (SIZE_MAX - 64) / 256) || #endif (N > SIZE_MAX / 128 / r)) { errno = ENOMEM; goto err0; } /* Allocate memory. */ #ifdef HAVE_POSIX_MEMALIGN if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) goto err0; B = (uint8_t *)(B0); if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) goto err1; XY = (uint32_t *)(XY0); #if !defined(MAP_ANON) || !defined(HAVE_MMAP) if ((errno = posix_memalign(&V0, 64, (size_t)(128 * r * N))) != 0) goto err2; V = (uint32_t *)(V0); #endif #else if ((B0 = malloc(128 * r * p + 63)) == NULL) goto err0; B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) goto err1; XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); #if !defined(MAP_ANON) || !defined(HAVE_MMAP) if ((V0 = malloc(128 * r * N + 63)) == NULL) goto err2; V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); #endif #endif #if defined(MAP_ANON) && defined(HAVE_MMAP) if ((V0 = mmap(NULL, (size_t)(128 * r * N), PROT_READ | PROT_WRITE, #ifdef MAP_NOCORE MAP_ANON | MAP_PRIVATE | MAP_NOCORE, #else MAP_ANON | MAP_PRIVATE, #endif -1, 0)) == MAP_FAILED) goto err2; V = (uint32_t *)(V0); #endif /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); /* 2: for i = 0 to p - 1 do */ for (i = 0; i < p; i++) { /* 3: B_i <-- MF(B_i, N) */ (smix)(&B[i * 128 * r], r, N, V, XY); } /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); /* Free memory. */ #if defined(MAP_ANON) && defined(HAVE_MMAP) if (munmap(V0, (size_t)(128 * r * N))) goto err2; #else free(V0); #endif free(XY0); free(B0); /* Success! */ return (0); err2: free(XY0); err1: free(B0); err0: /* Failure! */ return (-1); } #define TESTLEN 64 static struct scrypt_test { const char * passwd; const char * salt; uint64_t N; uint32_t r; uint32_t p; uint8_t result[TESTLEN]; } testcase = { .passwd = "pleaseletmein", .salt = "SodiumChloride", .N = 16, .r = 8, .p = 1, .result = { 0x25, 0xa9, 0xfa, 0x20, 0x7f, 0x87, 0xca, 0x09, 0xa4, 0xef, 0x8b, 0x9f, 0x77, 0x7a, 0xca, 0x16, 0xbe, 0xb7, 0x84, 0xae, 0x18, 0x30, 0xbf, 0xbf, 0xd3, 0x83, 0x25, 0xaa, 0xbb, 0x93, 0x77, 0xdf, 0x1b, 0xa7, 0x84, 0xd7, 0x46, 0xea, 0x27, 0x3b, 0xf5, 0x16, 0xa4, 0x6f, 0xbf, 0xac, 0xf5, 0x11, 0xc5, 0xbe, 0xba, 0x4c, 0x4a, 0xb3, 0xac, 0xc7, 0xfa, 0x6f, 0x46, 0x0b, 0x6c, 0x0f, 0x47, 0x7b, } }; static int testsmix(void (* smix)(uint8_t *, size_t, uint64_t, void *, void *)) { uint8_t hbuf[TESTLEN]; /* Perform the computation. */ if (crypto_scrypt_internal( (const uint8_t *)testcase.passwd, strlen(testcase.passwd), (const uint8_t *)testcase.salt, strlen(testcase.salt), testcase.N, testcase.r, testcase.p, hbuf, TESTLEN, smix)) return (-1); /* Does it match? */ return (memcmp(testcase.result, hbuf, TESTLEN)); } static void selectsmix(void) { #ifdef CPUSUPPORT_X86_SSE2 /* If we're running on an SSE2-capable CPU, try that code. */ if (cpusupport_x86_sse2()) { /* If SSE2ized smix works, use it. */ if (!testsmix(crypto_scrypt_smix_sse2)) { smix_func = crypto_scrypt_smix_sse2; return; } warn0("Disabling broken SSE2 scrypt support - please report bug!"); } #endif /* If generic smix works, use it. */ if (!testsmix(crypto_scrypt_smix)) { smix_func = crypto_scrypt_smix; return; } warn0("Generic scrypt code is broken - please report bug!"); /* If we get here, something really bad happened. */ abort(); } /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen * must satisfy 0 < r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter * N must be a power of 2 greater than 1. * * Return 0 on success; or -1 on error. */ int crypto_scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, uint8_t * buf, size_t buflen) { if (smix_func == NULL) selectsmix(); return (crypto_scrypt_internal(passwd, passwdlen, salt, saltlen, N, _r, _p, buf, buflen, smix_func)); } tarsnap-1.0.41/lib-platform/crypto/crypto_scrypt.h000066400000000000000000000041011476705112600222570ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef CRYPTO_SCRYPT_H_ #define CRYPTO_SCRYPT_H_ #include #include /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen * must satisfy 0 < r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter * N must be a power of 2 greater than 1. * * Return 0 on success; or -1 on error. */ int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, uint32_t, uint32_t, uint8_t *, size_t); #endif /* !CRYPTO_SCRYPT_H_ */ tarsnap-1.0.41/lib-platform/network/000077500000000000000000000000001476705112600173375ustar00rootroot00000000000000tarsnap-1.0.41/lib-platform/network/tsnetwork_cork.c000066400000000000000000000052151476705112600225640ustar00rootroot00000000000000#include "platform.h" #include #include #include #include #include #include "warnp.h" #include "tsnetwork_cork.h" /** * XXX Portability * XXX These functions serve two purposes: * XXX 1. To avoid wasting bandwidth, by ensuring that multiple small writes * XXX over a socket are aggregated into a single TCP/IP packet. * XXX 2. To avoid severe performance issues which would otherwise result * XXX from nagling, by allowing data to be "pushed" out once there are no * XXX more writes queued. * XXX * XXX POSIX defines TCP_NODELAY for purpose #2, although it does not require * XXX that implementations obey it; BSD and Linux respectively define * XXX TCP_NOPUSH and TCP_CORK for purpose #1. On OS X, TCP_NOPUSH is * XXX defined, but seems to be broken; we use autoconf to detect OS X and * XXX define BROKEN_TCP_NOPUSH. On Cygwin, TCP_NOPUSH is defined, but * XXX using it produces a ENOPROTOOPT error; we define BROKEN_TCP_NOPUSH * XXX in this case, too. On Minix, TCP_NODELAY fails with ENOSYS; since * XXX corking occurs for performance reasons only, we ignore this errno. */ /* Macro to simplify setting options. */ #define setopt(fd, opt, value, err0) do { \ int val; \ \ val = value; \ if (setsockopt(fd, IPPROTO_TCP, opt, &val, sizeof(int))) { \ if ((errno != ETIMEDOUT) && \ (errno != ECONNRESET) && \ (errno != ENOSYS)) { \ warnp("setsockopt(%s, %d)", #opt, val); \ goto err0; \ } \ } \ } while (0) /** * network_cork(fd): * Clear the TCP_NODELAY socket option, and set TCP_CORK or TCP_NOPUSH if * either is defined. */ int network_cork(int fd) { /* Clear TCP_NODELAY. */ setopt(fd, TCP_NODELAY, 0, err0); /* Set TCP_CORK or TCP_NOPUSH as appropriate. */ #ifdef TCP_CORK setopt(fd, TCP_CORK, 1, err0); #else #ifdef TCP_NOPUSH #ifndef BROKEN_TCP_NOPUSH setopt(fd, TCP_NOPUSH, 1, err0); #endif #endif #endif /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * network_uncork(fd): * Set the TCP_NODELAY socket option, and clear TCP_CORK or TCP_NOPUSH if * either is defined. */ int network_uncork(int fd) { /* Clear TCP_CORK or TCP_NOPUSH as appropriate. */ #ifdef TCP_CORK setopt(fd, TCP_CORK, 0, err0); #else #ifdef TCP_NOPUSH #ifndef BROKEN_TCP_NOPUSH setopt(fd, TCP_NOPUSH, 0, err0); #endif #endif #endif /* Set TCP_NODELAY. */ /* * For compatibility with Linux 2.4, this must be done after we * clear TCP_CORK; otherwise it will throw an EINVAL back at us. */ setopt(fd, TCP_NODELAY, 1, err0); /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib-platform/network/tsnetwork_cork.h000066400000000000000000000005751476705112600225750ustar00rootroot00000000000000#ifndef NETWORK_CORK_H_ #define NETWORK_CORK_H_ /** * network_cork(fd): * Clear the TCP_NODELAY socket option, and set TCP_CORK or TCP_NOPUSH if * either is defined. */ int network_cork(int); /** * network_uncork(fd): * Set the TCP_NODELAY socket option, and clear TCP_CORK or TCP_NOPUSH if * either is defined. */ int network_uncork(int); #endif /* !NETWORK_CORK_H_ */ tarsnap-1.0.41/lib-platform/platform.h000066400000000000000000000004301476705112600176400ustar00rootroot00000000000000#ifndef PLATFORM_H_ #define PLATFORM_H_ /* Ensure that we have a config file. */ #if defined(CONFIG_H_FILE) #include CONFIG_H_FILE #elif defined(HAVE_CONFIG_H) #include "config.h" #else #error Need either CONFIG_H_FILE or HAVE_CONFIG_H defined. #endif #endif /* !PLATFORM_H_ */ tarsnap-1.0.41/lib-platform/util/000077500000000000000000000000001476705112600166235ustar00rootroot00000000000000tarsnap-1.0.41/lib-platform/util/fileutil.c000066400000000000000000000032241476705112600206050ustar00rootroot00000000000000#include "platform.h" #include #include #include #include "warnp.h" #include "fileutil.h" /** * fileutil_open_noatime(path, flags, noatime): * Act the same as open(2), except that if the OS supports O_NOATIME and * ${noatime} is non-zero, attempt to open the path with that set. If the * O_NOATIME attempt fails, do not give any warnings, and attempt a normal * open(). */ int fileutil_open_noatime(const char * path, int flags, int noatime) { int fd = -1; #if defined(O_NOATIME) /* If requested, attempt to open with O_NOATIME. */ if (noatime) fd = open(path, flags | O_NOATIME); #else (void)noatime; /* UNUSED */ #endif /* If it failed (or wasn't supported / requested), open normally. */ if ((fd == -1) && ((fd = open(path, flags)) == -1)) goto err0; /* Success! */ return (fd); err0: /* Failure! */ return (fd); } /** * fileutil_fsync(fp, name): * Attempt to write the contents of ${fp} to disk. Do not close ${fp}. * * Caveat: "Disks lie" - Kirk McKusick. */ int fileutil_fsync(FILE * fp, const char * name) { int fd; if (fflush(fp)) { warnp("fflush(%s)", name); goto err0; } if ((fd = fileno(fp)) == -1) { warnp("fileno(%s)", name); goto err0; } if (fsync(fd)) { warnp("fsync(%s)", name); goto err0; } #ifdef F_FULLFSYNC /* * MacOS-specific "ask the drive to flush all buffered data". * Not supported on all filesystems. Even on supported filesystems, * some FireWire drives are known to ignore this request. As such, * don't pay attention to the return code from fcntl(). */ fcntl(fd, F_FULLFSYNC); #endif /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib-platform/util/fileutil.h000066400000000000000000000011601476705112600206070ustar00rootroot00000000000000#ifndef FILEUTIL_H_ #define FILEUTIL_H_ #include /** * fileutil_open_noatime(path, flags, noatime): * Act the same as open(2), except that if the OS supports O_NOATIME and * ${noatime} is non-zero, attempt to open the path with that set. If the * O_NOATIME attempt fails, do not give any warnings, and attempt a normal * open(). */ int fileutil_open_noatime(const char *, int, int); /** * fileutil_fsync(fp, name): * Attempt to write the contents of ${fp} to disk. Do not close ${fp}. * * Caveat: "Disks lie" - Kirk McKusick. */ int fileutil_fsync(FILE *, const char *); #endif /* !FILEUTIL_H_ */ tarsnap-1.0.41/lib-platform/util/memlimit.c000066400000000000000000000220551476705112600206100ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ /* We use non-POSIX functionality in this file. */ #undef _POSIX_C_SOURCE #undef _XOPEN_SOURCE #include "platform.h" #include #include #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_SYS_SYSINFO_H #include #endif #include #include #include #include #include #include #ifdef DEBUG #include #endif #include "memlimit.h" /* If we don't have CTL_HW, we can't use HW_USERMEM. */ #ifndef CTL_HW #undef HW_USERMEM #endif /* Is RLIMIT_DATA relevant on this platform? */ #if !defined(HAVE_MMAP) || defined(__OpenBSD__) #define USE_RLIMIT_DATA #endif #ifdef CTL_HW static int memlimit_sysctl_hw(size_t * memlimit, int mibleaf) { int mib[2]; uint8_t sysctlbuf[8]; size_t sysctlbuflen = 8; uint64_t sysctlval; /* Ask the kernel how much RAM we have. */ mib[0] = CTL_HW; mib[1] = mibleaf; if (sysctl(mib, 2, sysctlbuf, &sysctlbuflen, NULL, 0)) return (1); /* * If we read 8 bytes out, assume this is a system-endian uint64_t. * If we only read 4 bytes out, the OS is trying to give us a * uint32_t answer -- but given how many systems now have 4GB+ of RAM, * it's probably truncating, and we really can't trust the value we * have returned to us. */ if (sysctlbuflen == sizeof(uint64_t)) memcpy(&sysctlval, sysctlbuf, sizeof(uint64_t)); else if (sysctlbuflen == sizeof(uint32_t)) sysctlval = SIZE_MAX; else return (1); /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (sysctlval > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = (size_t)sysctlval; #else *memlimit = sysctlval; #endif /* Success! */ return (0); } #endif /* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */ #ifndef HAVE_STRUCT_SYSINFO #undef HAVE_SYSINFO #endif /* If we don't HAVE_STRUCT_SYSINFO_TOTALRAM, we can't use sysinfo. */ #ifndef HAVE_STRUCT_SYSINFO_TOTALRAM #undef HAVE_SYSINFO #endif #ifdef HAVE_SYSINFO static int memlimit_sysinfo(size_t * memlimit) { struct sysinfo info; uint64_t totalmem; /* Get information from the kernel. */ if (sysinfo(&info)) return (1); totalmem = info.totalram; /* If we're on a modern kernel, adjust based on mem_unit. */ #ifdef HAVE_STRUCT_SYSINFO_MEM_UNIT totalmem = totalmem * info.mem_unit; #endif /* Return the value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (totalmem > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = (size_t)totalmem; #else *memlimit = totalmem; #endif /* Success! */ return (0); } #endif /* HAVE_SYSINFO */ static int memlimit_rlimit(size_t * memlimit) { struct rlimit rl; uint64_t memrlimit; /* Find the least of... */ memrlimit = (uint64_t)(-1); /* ... RLIMIT_AS... */ #ifdef RLIMIT_AS if (getrlimit(RLIMIT_AS, &rl)) return (1); if ((rl.rlim_cur != RLIM_INFINITY) && ((uint64_t)rl.rlim_cur < memrlimit)) memrlimit = (uint64_t)rl.rlim_cur; #endif #ifdef USE_RLIMIT_DATA /* ... RLIMIT_DATA (if we're not using mmap)... */ if (getrlimit(RLIMIT_DATA, &rl)) return (1); if ((rl.rlim_cur != RLIM_INFINITY) && ((uint64_t)rl.rlim_cur < memrlimit)) memrlimit = (uint64_t)rl.rlim_cur; #endif /* ... and RLIMIT_RSS. */ #ifdef RLIMIT_RSS if (getrlimit(RLIMIT_RSS, &rl)) return (1); if ((rl.rlim_cur != RLIM_INFINITY) && ((uint64_t)rl.rlim_cur < memrlimit)) memrlimit = (uint64_t)rl.rlim_cur; #endif /* Return the value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (memrlimit > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = (size_t)memrlimit; #else *memlimit = memrlimit; #endif /* Success! */ return (0); } #ifdef _SC_PHYS_PAGES /* Some systems define _SC_PAGESIZE instead of _SC_PAGE_SIZE. */ #ifndef _SC_PAGE_SIZE #define _SC_PAGE_SIZE _SC_PAGESIZE #endif static int memlimit_sysconf(size_t * memlimit) { long pagesize; long physpages; uint64_t totalmem; /* Set errno to 0 in order to distinguish "no limit" from "error". */ errno = 0; /* Read the two limits. */ if (((pagesize = sysconf(_SC_PAGE_SIZE)) == -1) || ((physpages = sysconf(_SC_PHYS_PAGES)) == -1)) { /* * Did an error occur? OS X may return EINVAL due to not * supporting _SC_PHYS_PAGES in spite of defining it. */ if (errno != 0 && errno != EINVAL) return (1); /* If not, there is no limit. */ totalmem = (uint64_t)(-1); } else { /* Compute the limit. */ totalmem = (uint64_t)(pagesize) * (uint64_t)(physpages); } /* Return the value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (totalmem > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = (size_t)totalmem; #else *memlimit = totalmem; #endif /* Success! */ return (0); } #endif /** * memtouse(maxmem, maxmemfrac, memlimit): * Examine the system and return the amount of RAM which should be * used in ${memlimit}. This value should be the specified * ${maxmemfrac} fraction of available RAM, but no more than * ${maxmem} and no less than 1 MiB. ${maxmemfrac} must be larger than 0 * and <= 1.0. */ int memtouse(size_t maxmem, double maxmemfrac, size_t * memlimit) { size_t usermem_memlimit, memsize_memlimit; size_t sysinfo_memlimit, rlimit_memlimit; size_t sysconf_memlimit; size_t memlimit_min; size_t memavail; /* Check limit. */ assert((maxmemfrac > 0) && (maxmemfrac <= 1.0)); /* Get memory limits. */ #ifdef HW_USERMEM if (memlimit_sysctl_hw(&usermem_memlimit, HW_USERMEM)) return (1); #else usermem_memlimit = SIZE_MAX; #endif #ifdef HW_MEMSIZE if (memlimit_sysctl_hw(&memsize_memlimit, HW_MEMSIZE)) return (1); #else memsize_memlimit = SIZE_MAX; #endif #ifdef HAVE_SYSINFO if (memlimit_sysinfo(&sysinfo_memlimit)) return (1); #else sysinfo_memlimit = SIZE_MAX; #endif if (memlimit_rlimit(&rlimit_memlimit)) return (1); #ifdef _SC_PHYS_PAGES if (memlimit_sysconf(&sysconf_memlimit)) return (1); #else sysconf_memlimit = SIZE_MAX; #endif #ifdef DEBUG /* rlimit has two '\t' so that they line up. */ fprintf(stderr, "Memory limits are:\n\tusermem:\t%zu\n" "\tmemsize:\t%zu\n\tsysinfo:\t%zu\n\trlimit:\t\t%zu\n" "\tsysconf:\t%zu\n", usermem_memlimit, memsize_memlimit, sysinfo_memlimit, rlimit_memlimit, sysconf_memlimit); #endif /* * Some systems return bogus values for hw.usermem due to ZFS making * use of wired pages. Assume that at least 50% of physical pages * are available to userland on demand. */ if (sysconf_memlimit != SIZE_MAX) { if (usermem_memlimit < sysconf_memlimit / 2) usermem_memlimit = sysconf_memlimit / 2; } /* Find the smallest of them. */ memlimit_min = SIZE_MAX; if (memlimit_min > usermem_memlimit) memlimit_min = usermem_memlimit; if (memlimit_min > memsize_memlimit) memlimit_min = memsize_memlimit; if (memlimit_min > sysinfo_memlimit) memlimit_min = sysinfo_memlimit; if (memlimit_min > rlimit_memlimit) memlimit_min = rlimit_memlimit; if (memlimit_min > sysconf_memlimit) memlimit_min = sysconf_memlimit; #ifdef DEBUG /* This line is continued after the maxmemfrac calculation. */ fprintf(stderr, "Minimum is %zu;", memlimit_min); #endif /* Only use the specified fraction of the available memory. */ memavail = (size_t)(maxmemfrac * (double)memlimit_min); #ifdef DEBUG fprintf(stderr, " will apply maxmemfrac of %g\n", maxmemfrac); #endif /* Don't use more than the specified maximum. */ if ((maxmem > 0) && (memavail > maxmem)) memavail = maxmem; /* But always allow at least 1 MiB. */ if (memavail < 1048576) memavail = 1048576; #ifdef DEBUG fprintf(stderr, "Memory allowed:\t\t%zu\n", memavail); #endif /* Return limit via the provided pointer. */ *memlimit = memavail; return (0); } tarsnap-1.0.41/lib-platform/util/memlimit.h000066400000000000000000000036041476705112600206140ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef MEMLIMIT_H_ #define MEMLIMIT_H_ #include /** * memtouse(maxmem, maxmemfrac, memlimit): * Examine the system and return the amount of RAM which should be * used in ${memlimit}. This value should be the specified * ${maxmemfrac} fraction of available RAM, but no more than * ${maxmem} and no less than 1 MiB. ${maxmemfrac} must be larger than 0 * and <= 1.0. */ int memtouse(size_t, double, size_t *); #endif /* !MEMLIMIT_H_ */ tarsnap-1.0.41/lib-platform/util/ts_getfstype.c000066400000000000000000000141631476705112600215140ustar00rootroot00000000000000/* We use non-POSIX functionality in this file. */ #undef _POSIX_C_SOURCE #undef _XOPEN_SOURCE #include "platform.h" #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif #ifdef HAVE_LINUX_MAGIC_H #include #endif #include #include #include "ts_getfstype.h" /* Linux ugliness starts here. */ #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME #ifndef HAVE_STRUCT_STATVFS_F_BASETYPE #ifndef HAVE_STRUCT_STATVFS_F_FSTYPENAME #ifdef HAVE_STRUCT_STATFS_F_TYPE /* * Some important filesystem numbers are worth including here in case we * don't have or it is deficient. */ #ifndef DEVFS_SUPER_MAGIC #define DEVFS_SUPER_MAGIC 0x1373 #endif #ifndef DEVPTS_SUPER_MAGIC #define DEVPTS_SUPER_MAGIC 0x1cd1 #endif #ifndef SYSFS_MAGIC #define SYSFS_MAGIC 0x62656572 #endif #ifndef PROC_SUPER_MAGIC #define PROC_SUPER_MAGIC 0x9fa0 #endif #ifndef USBDEVICE_SUPER_MAGIC #define USBDEVICE_SUPER_MAGIC 0x9fa2 #endif #ifndef SECURITYFS_MAGIC #define SECURITYFS_MAGIC 0x73636673 #endif #ifndef SELINUX_MAGIC #define SELINUX_MAGIC 0xf97cff8c #endif static struct ftyperec { unsigned int f_type; const char * typename; } ftypes[] = { #ifdef ADFS_SUPER_MAGIC {ADFS_SUPER_MAGIC, "adfs"}, #endif #ifdef AFFS_SUPER_MAGIC {AFFS_SUPER_MAGIC, "affs"}, #endif #ifdef AFS_SUPER_MAGIC {AFS_SUPER_MAGIC, "afs"}, #endif #ifdef AUTOFS_SUPER_MAGIC {AUTOFS_SUPER_MAGIC, "autofs"}, #endif #ifdef CODA_SUPER_MAGIC {CODA_SUPER_MAGIC, "coda"}, #endif #ifdef CGROUP_SUPER_MAGIC {CGROUP_SUPER_MAGIC, "cgroup"}, #endif #ifdef DEBUGFS_SUPER_MAGIC {DEBUGFS_SUPER_MAGIC, "debugfs"}, #endif #ifdef EFS_SUPER_MAGIC {EFS_SUPER_MAGIC, "efs"}, #endif #ifdef EXT2_SUPER_MAGIC {EXT2_SUPER_MAGIC, "ext2"}, #endif #ifdef EXT3_SUPER_MAGIC {EXT3_SUPER_MAGIC, "ext3"}, #endif #ifdef EXT4_SUPER_MAGIC {EXT4_SUPER_MAGIC, "ext4"}, #endif #ifdef HTPFS_SUPER_MAGIC {HPFS_SUPER_MAGIC, "hpfs"}, #endif #ifdef ISOFS_SUPER_MAGIC {ISOFS_SUPER_MAGIC, "isofs"}, #endif #ifdef JFFS2_SUPER_MAGIC {JFFS2_SUPER_MAGIC, "jffs2"}, #endif #ifdef MINIX_SUPER_MAGIC {MINIX_SUPER_MAGIC, "minix"}, #endif #ifdef MINIX_SUPER_MAGIC2 {MINIX_SUPER_MAGIC2, "minix"}, #endif #ifdef MINIX2_SUPER_MAGIC {MINIX2_SUPER_MAGIC, "minix2"}, #endif #ifdef MINIX2_SUPER_MAGIC2 {MINIX2_SUPER_MAGIC2, "minix2"}, #endif #ifdef MINIX3_SUPER_MAGIC {MINIX3_SUPER_MAGIC, "minix3"}, #endif #ifdef MSDOS_SUPER_MAGIC {MSDOS_SUPER_MAGIC, "msdos"}, #endif #ifdef NCP_SUPER_MAGIC {NCP_SUPER_MAGIC, "ncp"}, #endif #ifdef NFS_SUPER_MAGIC {NFS_SUPER_MAGIC, "nfs"}, #endif #ifdef OPENPROM_SUPER_MAGIC {OPENPROM_SUPER_MAGIC, "openprom"}, #endif #ifdef QNX4_SUPER_MAGIC {QNX4_SUPER_MAGIC, "qnx4"}, #endif #ifdef REISERFS_SUPER_MAGIC {REISERFS_SUPER_MAGIC, "reiserfs"}, #endif #ifdef SMB_SUPER_MAGIC {SMB_SUPER_MAGIC, "smb"}, #endif #ifdef ANON_INODE_FS_MAGIC {ANON_INODE_FS_MAGIC, "anon_inode_fs"}, #endif #ifdef TMPFS_MAGIC {TMPFS_MAGIC, "tmpfs"}, #endif {DEVFS_SUPER_MAGIC, "devfs"}, {DEVPTS_SUPER_MAGIC, "devpts"}, {SYSFS_MAGIC, "sysfs"}, {PROC_SUPER_MAGIC, "proc"}, {USBDEVICE_SUPER_MAGIC, "usbdevfs"}, {SECURITYFS_MAGIC, "securityfs"}, {SELINUX_MAGIC, "selinux"}, {0, NULL} }; #endif /* HAVE_STRUCT_STATFS_F_TYPE */ #endif /* !HAVE_STRUCT_STATVFS_F_FSTYPENAME */ #endif /* !HAVE_STRUCT_STATVFS_F_BASETYPE */ #endif /* !HAVE_STRUCT_STATFS_F_FSTYPENAME */ /* Linux ugliness ends here. */ /* List of names of synthetic filesystem types. */ static const char * synthetic_filesystems[] = { "devfs", /* Many OSes */ "procfs", /* Many OSes */ "fdescfs", /* FreeBSD */ "linprocfs", /* Linux emulation on FreeBSD */ "linsysfs", /* Linux emulation on FreeBSD */ "proc", /* Linux */ "sysfs", /* Linux */ "devpts", /* Linux */ "usbdevfs", /* Linux */ "securityfs", /* Linux */ "selinux", /* Linux */ "kernfs", /* NetBSD */ "ptyfs", /* NetBSD */ "dev", /* Solaris */ "ctfs", /* Solaris */ "mntfs", /* Solaris */ "objfs", /* Solaris */ "sharefs", /* Solaris */ "fd", /* Solaris */ NULL }; /** * ts_getfstype(path): * Determine the type of filesystem on which ${path} resides, and return a * NUL-terminated malloced string. */ char * ts_getfstype(const char * path) { const char * fstype = "Unknown"; #if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) struct statfs sfs; #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE) struct statvfs svfs; #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) struct statvfs svfs; #elif defined(HAVE_STRUCT_STATFS_F_TYPE) struct statfs sfs; size_t i; #endif #if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) /* The fs type name is in f_fstypename after we call statfs(2). */ if (statfs(path, &sfs) == 0) fstype = sfs.f_fstypename; #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE) /* The fs type name is in f_basetype after we call statvfs(2). */ if (statvfs(path, &svfs) == 0) fstype = svfs.f_basetype; #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) /* The fs type name is in f_fstypename after we call fstatvfs(2). */ if (statvfs(path, &svfs) == 0) fstype = svfs.f_fstypename; #elif defined(HAVE_STRUCT_STATFS_F_TYPE) /* We need to call statfs(2) and interpret f_type values. */ if (statfs(path, &sfs) == 0) { for (i = 0; ftypes[i].typename != NULL; i++) { if (((unsigned int)sfs.f_type) == ftypes[i].f_type) { fstype = ftypes[i].typename; break; } } } #endif /* Pass back a malloc-allocated string. */ return (strdup(fstype)); } /** * ts_getfstype_issynthetic(fstype): * Return non-zero if the filesystem type ${fstype} is on a list of * "synthetic" filesystems (i.e., does not contain normal file data). */ int ts_getfstype_issynthetic(const char * fstype) { size_t i; /* * Look through the list of names of synthetic filesystems types, and * stop if we find one which matches the string we're given. */ for (i = 0; synthetic_filesystems[i] != NULL; i++) { if (!strcmp(fstype, synthetic_filesystems[i])) break; } /* If we reached the end, return 0; otherwise, return 1. */ if (synthetic_filesystems[i] == NULL) return (0); else return (1); } tarsnap-1.0.41/lib-platform/util/ts_getfstype.h000066400000000000000000000007441476705112600215210ustar00rootroot00000000000000#ifndef TS_GETFSTYPE_H_ #define TS_GETFSTYPE_H_ /** * ts_getfstype(path): * Determine the type of filesystem on which ${path} resides, and return a * NUL-terminated malloced string. */ char * ts_getfstype(const char *); /** * ts_getfstype_issynthetic(fstype): * Return non-zero if the filesystem type ${fstype} is on a list of * "synthetic" filesystems (i.e., does not contain normal file data). */ int ts_getfstype_issynthetic(const char *); #endif /* !TS_GETFSTYPE_H_ */ tarsnap-1.0.41/lib/000077500000000000000000000000001476705112600140245ustar00rootroot00000000000000tarsnap-1.0.41/lib/crypto/000077500000000000000000000000001476705112600153445ustar00rootroot00000000000000tarsnap-1.0.41/lib/crypto/crypto.h000066400000000000000000000220231476705112600170340ustar00rootroot00000000000000#ifndef CRYPTO_H_ #define CRYPTO_H_ #include #include /* Cryptographic keys held by user. */ #define CRYPTO_KEY_SIGN_PRIV 0 #define CRYPTO_KEY_SIGN_PUB 1 #define CRYPTO_KEY_ENCR_PRIV 2 #define CRYPTO_KEY_ENCR_PUB 3 #define CRYPTO_KEY_HMAC_FILE 4 #define CRYPTO_KEY_HMAC_CHUNK 5 #define CRYPTO_KEY_HMAC_NAME 6 #define CRYPTO_KEY_HMAC_CPARAMS 7 /* Cryptographic keys used in client-server protocol. */ /* 8 is reserved for the private part of the server root RSA key. */ #define CRYPTO_KEY_ROOT_PUB 9 #define CRYPTO_KEY_AUTH_PUT 10 #define CRYPTO_KEY_AUTH_GET 11 #define CRYPTO_KEY_AUTH_DELETE 12 /* * HMAC_FILE_WRITE is normally the same key as HMAC_FILE, but can be set to * a different value via a masked crypto_keys_import if we need to read from * one archive set and write to another (i.e., tarsnap-recrypt). Because it * is a duplicate key, HMAC_FILE_WRITE cannot be exported or generated. */ /* Keys #13--18 reserved for server code. */ #define CRYPTO_KEY_HMAC_FILE_WRITE 19 /* Fake HMAC "key" to represent "just SHA256 the data". */ #define CRYPTO_KEY_HMAC_SHA256 (-1) /* Bitmasks for use in representing multiple cryptographic keys. */ #define CRYPTO_KEYMASK_SIGN_PRIV (1 << CRYPTO_KEY_SIGN_PRIV) #define CRYPTO_KEYMASK_SIGN_PUB (1 << CRYPTO_KEY_SIGN_PUB) #define CRYPTO_KEYMASK_SIGN \ (CRYPTO_KEYMASK_SIGN_PRIV | CRYPTO_KEYMASK_SIGN_PUB) #define CRYPTO_KEYMASK_ENCR_PRIV (1 << CRYPTO_KEY_ENCR_PRIV) #define CRYPTO_KEYMASK_ENCR_PUB (1 << CRYPTO_KEY_ENCR_PUB) #define CRYPTO_KEYMASK_ENCR \ (CRYPTO_KEYMASK_ENCR_PRIV | CRYPTO_KEYMASK_ENCR_PUB) #define CRYPTO_KEYMASK_HMAC_FILE (1 << CRYPTO_KEY_HMAC_FILE) #define CRYPTO_KEYMASK_HMAC_FILE_WRITE (1 << CRYPTO_KEY_HMAC_FILE_WRITE) #define CRYPTO_KEYMASK_HMAC_CHUNK (1 << CRYPTO_KEY_HMAC_CHUNK) #define CRYPTO_KEYMASK_HMAC_NAME (1 << CRYPTO_KEY_HMAC_NAME) #define CRYPTO_KEYMASK_HMAC_CPARAMS (1 << CRYPTO_KEY_HMAC_CPARAMS) #define CRYPTO_KEYMASK_READ \ (CRYPTO_KEYMASK_ENCR_PRIV | CRYPTO_KEYMASK_SIGN_PUB | \ CRYPTO_KEYMASK_HMAC_FILE | CRYPTO_KEYMASK_HMAC_CHUNK | \ CRYPTO_KEYMASK_HMAC_NAME | CRYPTO_KEYMASK_AUTH_GET ) #define CRYPTO_KEYMASK_WRITE \ (CRYPTO_KEYMASK_SIGN_PRIV | CRYPTO_KEYMASK_ENCR_PUB | \ CRYPTO_KEYMASK_HMAC_FILE | CRYPTO_KEYMASK_HMAC_CHUNK | \ CRYPTO_KEYMASK_HMAC_NAME | CRYPTO_KEYMASK_HMAC_CPARAMS | \ CRYPTO_KEYMASK_AUTH_PUT ) #define CRYPTO_KEYMASK_ROOT_PUB (1 << CRYPTO_KEY_ROOT_PUB) #define CRYPTO_KEYMASK_AUTH_PUT (1 << CRYPTO_KEY_AUTH_PUT) #define CRYPTO_KEYMASK_AUTH_GET (1 << CRYPTO_KEY_AUTH_GET) #define CRYPTO_KEYMASK_AUTH_DELETE (1 << CRYPTO_KEY_AUTH_DELETE) /* Mask for all the cryptographic keys held by users. */ #define CRYPTO_KEYMASK_USER \ (CRYPTO_KEYMASK_SIGN_PRIV | CRYPTO_KEYMASK_SIGN_PUB | \ CRYPTO_KEYMASK_ENCR_PRIV | CRYPTO_KEYMASK_ENCR_PUB | \ CRYPTO_KEYMASK_HMAC_FILE | CRYPTO_KEYMASK_HMAC_CHUNK | \ CRYPTO_KEYMASK_HMAC_NAME | CRYPTO_KEYMASK_HMAC_CPARAMS | \ CRYPTO_KEYMASK_AUTH_PUT | CRYPTO_KEYMASK_AUTH_GET | \ CRYPTO_KEYMASK_AUTH_DELETE) /* Sizes of file encryption headers and trailers. */ #define CRYPTO_FILE_HLEN (256 + 8) #define CRYPTO_FILE_TLEN 32 /* * Sizes of Diffie-Hellman private, public, and exchanged keys. These are * defined in libcperciva/crypto/crypto_dh.h, but are also included here as * the sizes are used in places which don't actually do Diffie-Hellman. */ #define CRYPTO_DH_PRIVLEN 32 #define CRYPTO_DH_PUBLEN 256 #define CRYPTO_DH_KEYLEN 256 /* Structure for holding client-server protocol cryptographic state. */ typedef struct crypto_session_internal CRYPTO_SESSION; /** * crypto_keys_init(void): * Initialize cryptographic keys. */ int crypto_keys_init(void); /** * crypto_keys_import(buf, buflen, keys): * Import keys from the provided buffer into the key cache. Ignore any keys * not specified in the mask ${keys}. */ int crypto_keys_import(const uint8_t *, size_t, int); /** * crypto_keys_missing(keys): * Look for the specified keys. If they are all present, return NULL; if * not, return a pointer to the name of one of the keys. */ const char * crypto_keys_missing(int); /** * crypto_keys_export(keys, buf, buflen): * Export the ${keys} specified to a buffer allocated using malloc. */ int crypto_keys_export(int, uint8_t **, size_t *); /** * crypto_keys_generate(keys): * Create the ${keys} specified. */ int crypto_keys_generate(int); /** * crypto_keys_raw_export_auth(buf): * Write into the specified buffer the 32-byte write authorization key, * the 32-byte read authorization key, and the 32-byte delete authorization * key, in that order. */ int crypto_keys_raw_export_auth(uint8_t[96]); /** * crypto_hash_data_key(key, keylen, data, len, buf): * Hash the provided ${data} with the provided HMAC-SHA256 ${key}. */ void crypto_hash_data_key(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t[32]); /** * crypto_hash_data_key_2(key, keylen, data0, len0, data1, len1, buf): * Hash the concatenation of two buffers with the provided HMAC-SHA256 ${key}. */ void crypto_hash_data_key_2(const uint8_t *, size_t, const uint8_t *, size_t, const uint8_t *, size_t, uint8_t[32]); /** * crypto_hash_data(key, data, len, buf): * Hash the provided ${data} with the HMAC-SHA256 ${key} specified; or if * ${key} == CRYPTO_KEY_HMAC_SHA256, just SHA256 the data. */ int crypto_hash_data(int, const uint8_t *, size_t, uint8_t[32]); /** * crypto_hash_data_2(key, data0, len0, data1, len1, buf): * Hash the concatenation of two buffers, as in crypto_hash_data. */ int crypto_hash_data_2(int, const uint8_t *, size_t, const uint8_t *, size_t, uint8_t[32]); /** * crypto_rsa_sign(key, data, len, sig, siglen): * Sign the provided ${data} with the specified ${key}, writing the signature * into ${sig}. */ int crypto_rsa_sign(int, const uint8_t *, size_t, uint8_t *, size_t); /** * crypto_rsa_verify(key, data, len, sig, siglen): * Verify that the provided signature ${sig} matches the provided ${data}. * Return 0 if the signature is valid, 1 if the signature is invalid, or -1 * on error. */ int crypto_rsa_verify(int, const uint8_t *, size_t, const uint8_t *, size_t); /** * crypto_rsa_encrypt(key, data, len, out, outlen): * Encrypt the provided ${data} with the specified ${key}, writing the * ciphertext into ${out} (of length ${outlen}). */ int crypto_rsa_encrypt(int, const uint8_t *, size_t, uint8_t *, size_t); /** * crypto_rsa_decrypt(key, data, len, out, outlen): * Decrypt the provided ${data} with the specified ${key}, writing the * ciphertext into ${out} (of length ${*outlen}). Set ${*outlen} to the * length of the plaintext, and return 0 on success, 1 if the ciphertext is * invalid, or 1 on error. */ int crypto_rsa_decrypt(int, const uint8_t *, size_t, uint8_t *, size_t *); /** * crypto_file_enc(buf, len, filebuf): * Encrypt the buffer ${buf} of length ${len}, placing the result (including * encryption header and authentication trailer) into ${filebuf}. */ int crypto_file_enc(const uint8_t *, size_t, uint8_t *); /** * crypto_file_dec(filebuf, len, buf): * Decrypt the buffer ${filebuf}, removing the encryption header and * authentication trailer, and place the result into ${buf} of length ${len}. */ int crypto_file_dec(const uint8_t *, size_t, uint8_t *); /** * crypto_session_init(pub, priv, nonce, mkey, encr_write, auth_write, * encr_read, auth_read): * Compute K = ${pub}^(2^258 + ${priv}), mkey = MGF1(nonce || K, 48), and * return a CRYPTO_SESSION with encryption and authentication write and read * keys constructed from HMAC(mkey, (encr|auth)_(write|read)). */ CRYPTO_SESSION * crypto_session_init(uint8_t[CRYPTO_DH_PUBLEN], uint8_t[CRYPTO_DH_PRIVLEN], uint8_t[32], uint8_t[48], const char *, const char *, const char *, const char *); /** * crypto_session_encrypt(CS, inbuf, outbuf, buflen): * Encrypt ${inbuf} with the session write key and write ciphertext to * ${outbuf}. */ void crypto_session_encrypt(CRYPTO_SESSION *, const uint8_t *, uint8_t *, size_t); /** * crypto_session_decrypt(CS, inbuf, outbuf, buflen): * Decrypt ${inbuf} with the session read key and write plaintext to ${outbuf}. */ void crypto_session_decrypt(CRYPTO_SESSION *, const uint8_t *, uint8_t *, size_t); /** * crypto_session_sign(CS, buf, buflen, sig): * Generate sig = write_auth(buf). */ void crypto_session_sign(CRYPTO_SESSION *, const uint8_t *, size_t, uint8_t[32]); /** * crypto_session_verify(CS, buf, buflen, sig): * Verify that sig = read_auth(buf). Return non-zero if the signature * does not match. */ int crypto_session_verify(CRYPTO_SESSION *, const uint8_t *, size_t, const uint8_t[32]); /** * crypto_session_free(CS): * Free a CRYPTO_SESSION structure. */ void crypto_session_free(CRYPTO_SESSION *); /** * crypto_passwd_to_dh(passwd, salt, pub, priv): * Generate a Diffie-Hellman pair (${priv}, ${pub}), with ${pub} equal to * 2^(2^258 + ${priv}) modulo the group #14 modulus, and ${priv} equal to * HMAC(${salt}, ${passwd}), where ${passwd} is a NUL-terminated string. */ int crypto_passwd_to_dh(const char *, const uint8_t[32], uint8_t[CRYPTO_DH_PUBLEN], uint8_t[CRYPTO_DH_PRIVLEN]); #endif /* !CRYPTO_H_ */ tarsnap-1.0.41/lib/crypto/crypto_compat.c000066400000000000000000000144461476705112600204040ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "warnp.h" #include "crypto_compat.h" #ifndef OPENSSL_VERSION_NUMBER #error "OPENSSL_VERSION_NUMBER must be defined" #endif /* LibreSSL compatibility. */ #ifdef LIBRESSL_VERSION_NUMBER /* LibreSSL claims to be OpenSSL 2.0; ignore that. */ #undef OPENSSL_VERSION_NUMBER #if LIBRESSL_VERSION_NUMBER >= 0x2070000fL /* Compatibility for LibreSSL 2.7.0+: pretend to be OpenSSL 1.1.0. */ #define OPENSSL_VERSION_NUMBER 0x1010000fL #else /* Compatibility for LibreSSL before 2.7.0: pretend to be OpenSSL 1.0.1g. */ #define OPENSSL_VERSION_NUMBER 0x1000107fL #endif #endif /* LIBRESSL_VERSION_NUMBER */ /** * crypto_compat_RSA_valid_size(key): * Return nonzero if ${key} has a valid size, and zero for an invalid size. */ int crypto_compat_RSA_valid_size(const RSA * const key) { /* Sanity checks. */ assert(key != NULL); #if OPENSSL_VERSION_NUMBER < 0x10100000L assert(key->n != NULL); return ((RSA_size(key) == 256) && (BN_num_bits(key->n) == 2048)); #else return ((RSA_size(key) == 256) && (RSA_bits(key) == 2048)); #endif } /** * crypto_compat_RSA_import(key, n, e, d, p, q, dmp1, dmq1, iqmp): * Import the given BIGNUMs into the RSA ${key}. If this function fails, * free any any BIGNUMs which have not been imported into the ${key}, but do * not free the ${key} itself. */ int crypto_compat_RSA_import(RSA * key, BIGNUM * n, BIGNUM * e, BIGNUM * d, BIGNUM * p, BIGNUM * q, BIGNUM * dmp1, BIGNUM * dmq1, BIGNUM * iqmp) { /* Sanity checks. */ assert(key != NULL); assert((n != NULL) && (e != NULL)); /* All the private-key-related variables are NULL, or they're not. */ if (d == NULL) { assert((p == NULL) && (q == NULL) && (dmp1 == NULL) && (dmq1 == NULL) && (iqmp == NULL)); } else { assert((p != NULL) && (q != NULL) && (dmp1 != NULL) && (dmq1 != NULL) && (iqmp != NULL)); } /* Put values into RSA key. */ #if OPENSSL_VERSION_NUMBER < 0x10100000L key->n = n; key->e = e; if (d != NULL) { /* Private key. */ key->d = d; key->p = p; key->q = q; key->dmp1 = dmp1; key->dmq1 = dmq1; key->iqmp = iqmp; } #else /* Do we have a public key, or private key? */ if (d == NULL) { /* We could use d here, but using NULL makes it more clear. */ if (RSA_set0_key(key, n, e, NULL) != 1) goto err3; } else { /* Private key. */ if (RSA_set0_key(key, n, e, d) != 1) goto err3; if (RSA_set0_factors(key, p, q) != 1) goto err2; if (RSA_set0_crt_params(key, dmp1, dmq1, iqmp) != 1) goto err1; } #endif /* Success! */ return (0); #if OPENSSL_VERSION_NUMBER < 0x10100000L #else err3: BN_free(n); BN_free(e); BN_clear_free(d); err2: BN_clear_free(p); BN_clear_free(q); err1: BN_clear_free(dmp1); BN_clear_free(dmq1); BN_clear_free(iqmp); /* Failure! */ return (-1); #endif } /** * crypto_compat_RSA_export(key, n, e, d, p, q, dmp1, dmq1, iqmp): * Export values from the given RSA ${key} into the BIGNUMs. ${n} and ${e} * must be non-NULL; the other values may be NULL if desired, and will * therefore not be exported. */ int crypto_compat_RSA_export(RSA * key, const BIGNUM ** n, const BIGNUM ** e, const BIGNUM ** d, const BIGNUM ** p, const BIGNUM ** q, const BIGNUM ** dmp1, const BIGNUM ** dmq1, const BIGNUM ** iqmp) { /* Sanity checks. */ assert(key != NULL); assert((n != NULL) && (e != NULL)); /* All the private-key-related variables are NULL, or they're not. */ if (d == NULL) { assert((p == NULL) && (q == NULL) && (dmp1 == NULL) && (dmq1 == NULL) && (iqmp == NULL)); } else { assert((p != NULL) && (q != NULL) && (dmp1 != NULL) && (dmq1 != NULL) && (iqmp != NULL)); } /* Get values from RSA key. */ #if OPENSSL_VERSION_NUMBER < 0x10100000L *n = key->n; *e = key->e; if (d != NULL) { /* Private key. */ *d = key->d; *p = key->p; *q = key->q; *dmp1 = key->dmp1; *dmq1 = key->dmq1; *iqmp = key->iqmp; } #else /* Do we have a public key, or private key? */ if (d == NULL) { /* We could use d here, but using NULL makes it more clear. */ RSA_get0_key(key, n, e, NULL); } else { /* Private key. */ RSA_get0_key(key, n, e, d); RSA_get0_factors(key, p, q); RSA_get0_crt_params(key, dmp1, dmq1, iqmp); } #endif /* Success! */ return (0); } /** * crypto_compat_RSA_generate_key(void): * Generate a key pair. */ RSA * crypto_compat_RSA_generate_key(void) { RSA * key; #if OPENSSL_VERSION_NUMBER < 0x00908000L /* Generate key. */ if ((key = RSA_generate_key(2048, 65537, NULL, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Success! */ return (key); #else BIGNUM * e; /* Set up parameter. */ if ((e = BN_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } BN_set_word(e, 65537); /* Generate key. */ if ((key = RSA_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err1; } if (RSA_generate_key_ex(key, 2048, e, NULL) != 1) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err2; } /* Clean up. */ BN_free(e); /* Success! */ return (key); err2: RSA_free(key); err1: BN_free(e); #endif err0: /* Failure! */ return (NULL); } /** * crypto_compat_free(void): * Free the shared memory allocated by OpenSSL. */ void crypto_compat_free(void) { #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER > 0x1010000fL /* OpenSSL 1.1.0 and higher: do nothing; the library uses atexit(). */ #elif defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER > 0x3070100fL /* LibreSSL 3.7.1 and higher. */ OPENSSL_cleanup(); #elif defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER > 0x3060000fL /* LibreSSL 3.6.0 to 3.7.0. */ OPENSSL_cleanup(); CRYPTO_cleanup_all_ex_data(); #else /* Earlier versions of OpenSSL and LibreSSL. */ /* Free OpenSSL error queue. */ #if OPENSSL_VERSION_NUMBER < 0x10000000L ERR_remove_state(0); #elif OPENSSL_VERSION_NUMBER < 0x10100000L ERR_remove_thread_state(NULL); #endif /* Free OpenSSL error strings. */ ERR_free_strings(); #if LIBRESSL_VERSION_NUMBER >= 0x2070000fL /* Additional cleaning needed for LibreSSL 2.7.0 to 3.5.x. */ EVP_cleanup(); #endif /* A more general OpenSSL cleanup function. */ CRYPTO_cleanup_all_ex_data(); #endif } tarsnap-1.0.41/lib/crypto/crypto_compat.h000066400000000000000000000024711476705112600204040ustar00rootroot00000000000000#ifndef CRYPTO_COMPAT_H_ #define CRYPTO_COMPAT_H_ #include /** * crypto_compat_RSA_valid_size(key): * Return nonzero if ${key} has a valid size, and zero for an invalid size. */ int crypto_compat_RSA_valid_size(const RSA * const); /** * crypto_compat_RSA_import(key, n, e, d, p, q, dmp1, dmq1, iqmp): * Import the given BIGNUMs into the RSA ${key}. If this function fails, * free any any BIGNUMs which have not been imported into the ${key}, but do * not free the ${key} itself. */ int crypto_compat_RSA_import(RSA *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *); /** * crypto_compat_RSA_export(key, n, e, d, p, q, dmp1, dmq1, iqmp): * Export values from the given RSA ${key} into the BIGNUMs. ${n} and ${e} * must be non-NULL; the other values may be NULL if desired, and will * therefore not be exported. */ int crypto_compat_RSA_export(RSA *, const BIGNUM **, const BIGNUM **, const BIGNUM **, const BIGNUM **, const BIGNUM **, const BIGNUM **, const BIGNUM **, const BIGNUM **); /** * crypto_compat_RSA_generate_key(void): * Generate a key pair. */ RSA * crypto_compat_RSA_generate_key(void); /** * crypto_compat_free(void): * Free the shared memory allocated by OpenSSL. */ void crypto_compat_free(void); #endif /* !CRYPTO_COMPAT_H_ */ tarsnap-1.0.41/lib/crypto/crypto_file.c000066400000000000000000000137211476705112600200330ustar00rootroot00000000000000#include #include #include #include #include "crypto_aes.h" #include "crypto_aesctr.h" #include "crypto_entropy.h" #include "crypto_verify_bytes.h" #include "sysendian.h" #include "warnp.h" #include "crypto_internal.h" #include "rwhashtab.h" #include "crypto.h" struct crypto_file_aes_key { struct crypto_aes_key * key; /* Expanded key. */ uint64_t nonce; /* Nonce, used only for encryption. */ uint8_t key_encrypted[256]; /* AES key encrypted with encr_pub. */ }; static struct crypto_file_aes_key * encr_aes; static RWHASHTAB * decr_aes_cache; static int keygen(void); static void crypto_file_atexit(void); static int callback_free_rwhashtab_aes_key(void * rec, void * cookie); /** * crypto_file_init_keys(void): * Initialize the keys cached by crypto_file. */ int crypto_file_init_keys(void) { /* We don't have an encryption key. */ encr_aes = NULL; /* Create encrypted key -> AES key mapping table. */ if ((decr_aes_cache = rwhashtab_init(offsetof(struct crypto_file_aes_key, key_encrypted), 256)) == NULL) goto err0; if (atexit(crypto_file_atexit)) { warnp("Could not initialize atexit"); goto err1; } /* Success! */ return (0); err1: rwhashtab_free(decr_aes_cache); err0: /* Failure! */ return (-1); } static int callback_free_rwhashtab_aes_key(void * rec, void * cookie) { struct crypto_file_aes_key * key = rec; (void)cookie; /* UNUSED */ /* Free expanded AES key and encrypted key lookup record. */ crypto_aes_key_free(key->key); free(key); /* Success! */ return (0); } /** * crypto_file_atexit(void): * Free the keys cached by crypto_file. */ static void crypto_file_atexit(void) { /* Iterate through encrypted key -> AES key table freeing records. */ rwhashtab_foreach(decr_aes_cache, callback_free_rwhashtab_aes_key, NULL); /* Free decryption cache. */ rwhashtab_free(decr_aes_cache); /* Free encryption key, if we have one. */ if (encr_aes != NULL) { crypto_aes_key_free(encr_aes->key); free(encr_aes); } } /* Generate encr_aes. */ static int keygen(void) { uint8_t aeskey[32]; /* Allocate memory. */ if ((encr_aes = malloc(sizeof(struct crypto_file_aes_key))) == NULL) goto err0; /* Generate random key. */ if (crypto_entropy_read(aeskey, 32)) goto err1; /* Expand the key. */ if ((encr_aes->key = crypto_aes_key_expand(aeskey, 32)) == NULL) goto err1; /* We start with a nonce of zero. */ encr_aes->nonce = 0; /* RSA encrypt the key. */ if (crypto_rsa_encrypt(CRYPTO_KEY_ENCR_PUB, aeskey, 32, encr_aes->key_encrypted, 256)) goto err2; /* Success! */ return (0); err2: crypto_aes_key_free(encr_aes->key); err1: free(encr_aes); encr_aes = NULL; err0: /* Failure! */ return (-1); } /** * crypto_file_enc(buf, len, filebuf): * Encrypt the buffer ${buf} of length ${len}, placing the result (including * encryption header and authentication trailer) into ${filebuf}. */ int crypto_file_enc(const uint8_t * buf, size_t len, uint8_t * filebuf) { struct crypto_aesctr * stream; /* If we don't have a session AES key yet, generate one. */ if ((encr_aes == NULL) && keygen()) goto err0; /* Copy encrypted key into header. */ memcpy(filebuf, encr_aes->key_encrypted, 256); /* Store nonce. */ be64enc(filebuf + 256, encr_aes->nonce); /* Encrypt the data. */ if ((stream = crypto_aesctr_init(encr_aes->key, encr_aes->nonce++)) == NULL) goto err0; crypto_aesctr_stream(stream, buf, filebuf + CRYPTO_FILE_HLEN, len); crypto_aesctr_free(stream); /* Compute HMAC. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_FILE_WRITE, filebuf, CRYPTO_FILE_HLEN + len, filebuf + CRYPTO_FILE_HLEN + len)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_file_dec(filebuf, len, buf): * Decrypt the buffer ${filebuf}, removing the encryption header and * authentication trailer, and place the result into ${buf} of length ${len}. */ int crypto_file_dec(const uint8_t * filebuf, size_t len, uint8_t * buf) { uint8_t hash[CRYPTO_FILE_TLEN]; struct crypto_file_aes_key * key; struct crypto_aesctr * stream; uint64_t nonce; /* * The AES key is 32 bytes, but the buffer is larger in order * to properly detect and handle bogus encrypted keys (i.e., if * more than 32 bytes were encrypted). */ uint8_t aeskey[256]; size_t aeskeybuflen = 256; /* Compute HMAC. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_FILE, filebuf, CRYPTO_FILE_HLEN + len, hash)) goto err0; /* If the HMAC doesn't match, the file was corrupted. */ if (crypto_verify_bytes(hash, &filebuf[CRYPTO_FILE_HLEN + len], CRYPTO_FILE_TLEN)) goto bad0; /* Look up key in hash table. */ key = rwhashtab_read(decr_aes_cache, filebuf); /* If it's not in the hash table, construct it the hard way. */ if (key == NULL) { /* Allocate memory. */ if ((key = malloc(sizeof(struct crypto_file_aes_key))) == NULL) goto err0; /* RSA decrypt key. */ switch (crypto_rsa_decrypt(CRYPTO_KEY_ENCR_PRIV, filebuf, 256, aeskey, &aeskeybuflen)) { case -1: /* Something went wrong. */ goto err1; case 0: /* Decrypted. Is the key the correct length? */ if (aeskeybuflen == 32) break; /* FALLTHROUGH */ case 1: /* The ciphertext is corrupt. */ goto bad1; } /* Expand the AES key. */ if ((key->key = crypto_aes_key_expand(aeskey, 32)) == NULL) goto err1; /* Copy the encrypted AES key. */ memcpy(key->key_encrypted, filebuf, 256); /* Insert the key into the hash table. */ if (rwhashtab_insert(decr_aes_cache, key)) { warnp("error inserting key into aes cache"); goto err2; } } /* Read the nonce. */ nonce = be64dec(&filebuf[256]); /* Decrypt the data. */ if ((stream = crypto_aesctr_init(key->key, nonce)) == NULL) goto err0; crypto_aesctr_stream(stream, &filebuf[CRYPTO_FILE_HLEN], buf, len); crypto_aesctr_free(stream); /* Success! */ return (0); bad1: free(key); bad0: /* File is not authentic. */ return (1); err2: crypto_aes_key_free(key->key); err1: free(key); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/crypto/crypto_hash.c000066400000000000000000000042641476705112600200410ustar00rootroot00000000000000#include #include #include "crypto_internal.h" #include "sha256.h" #include "crypto.h" /** * crypto_hash_data_key(key, keylen, data, len, buf): * Hash the provided ${data} with the provided HMAC-SHA256 ${key}. */ void crypto_hash_data_key(const uint8_t * key, size_t keylen, const uint8_t * data, size_t len, uint8_t buf[32]) { /* Use crypto_hash_data_key_2 to do the work. */ crypto_hash_data_key_2(key, keylen, data, len, NULL, 0, buf); } /** * crypto_hash_data_key_2(key, keylen, data0, len0, data1, len1, buf): * Hash the concatenation of two buffers with the provided HMAC-SHA256 ${key}. */ void crypto_hash_data_key_2(const uint8_t * key, size_t keylen, const uint8_t * data0, size_t len0, const uint8_t * data1, size_t len1, uint8_t buf[32]) { HMAC_SHA256_CTX hctx; /* Do the hashing. */ HMAC_SHA256_Init(&hctx, key, keylen); HMAC_SHA256_Update(&hctx, data0, len0); HMAC_SHA256_Update(&hctx, data1, len1); HMAC_SHA256_Final(buf, &hctx); } /** * crypto_hash_data(key, data, len, buf): * Hash the provided ${data} with the HMAC-SHA256 ${key} specified; or if * ${key} == CRYPTO_KEY_HMAC_SHA256, just SHA256 the data. */ int crypto_hash_data(int key, const uint8_t * data, size_t len, uint8_t buf[32]) { /* Use crypto_hash_data_2 to do the work. */ return (crypto_hash_data_2(key, data, len, NULL, 0, buf)); } /** * crypto_hash_data_2(key, data0, len0, data1, len1, buf): * Hash the concatenation of two buffers, as in crypto_hash_data. */ int crypto_hash_data_2(int key, const uint8_t * data0, size_t len0, const uint8_t * data1, size_t len1, uint8_t buf[32]) { HMAC_SHA256_CTX hctx; SHA256_CTX ctx; struct crypto_hmac_key * hkey; if (key == CRYPTO_KEY_HMAC_SHA256) { /* Hash the data. */ SHA256_Init(&ctx); SHA256_Update(&ctx, data0, len0); SHA256_Update(&ctx, data1, len1); SHA256_Final(buf, &ctx); } else { if ((hkey = crypto_keys_lookup_HMAC(key)) == NULL) goto err0; /* Do the HMAC. */ HMAC_SHA256_Init(&hctx, hkey->key, hkey->len); HMAC_SHA256_Update(&hctx, data0, len0); HMAC_SHA256_Update(&hctx, data1, len1); HMAC_SHA256_Final(buf, &hctx); } /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/crypto/crypto_internal.h000066400000000000000000000052561476705112600207410ustar00rootroot00000000000000#ifndef CRYPTO_INTERNAL_H_ #define CRYPTO_INTERNAL_H_ #include struct crypto_hmac_key { size_t len; uint8_t * key; }; /** * crypto_keys_lookup_RSA(key): * Return the requested RSA key. */ void * crypto_keys_lookup_RSA(int); /** * crypto_keys_lookup_HMAC(key): * Return the requested HMAC key. */ struct crypto_hmac_key * crypto_keys_lookup_HMAC(int); /** * crypto_keys_server_import_root(void): * Import the public part of the server root key. */ int crypto_keys_server_import_root(void); /** * crypto_keys_subr_import_RSA_priv(key, buf, buflen): * Import the specified RSA private key from the provided buffer. */ int crypto_keys_subr_import_RSA_priv(void **, const uint8_t *, size_t); /** * crypto_keys_subr_import_RSA_pub(key, buf, buflen): * Import the specified RSA public key from the provided buffer. */ int crypto_keys_subr_import_RSA_pub(void **, const uint8_t *, size_t); /** * crypto_keys_subr_import_HMAC(key, buf, buflen): * Import the specified HMAC key from the provided buffer. */ int crypto_keys_subr_import_HMAC(struct crypto_hmac_key **, const uint8_t *, size_t); /** * crypto_keys_subr_export_RSA_priv(key, buf, buflen): * If buf != NULL, export the specified RSA private key. Return the key * length in bytes. */ uint32_t crypto_keys_subr_export_RSA_priv(void *, uint8_t *, size_t); /** * crypto_keys_subr_export_RSA_pub(key, buf, buflen): * If buf != NULL, export the specified RSA public key. Return the key * length in bytes. */ uint32_t crypto_keys_subr_export_RSA_pub(void *, uint8_t *, size_t); /** * crypto_keys_subr_export_HMAC(key, buf, buflen): * If buf != NULL, export the specified HMAC key. Return the key length * in bytes. */ uint32_t crypto_keys_subr_export_HMAC(struct crypto_hmac_key *, uint8_t *, size_t); /** * crypto_keys_subr_generate_RSA(priv, pub): * Generate an RSA key and store the private and public parts. */ int crypto_keys_subr_generate_RSA(void **, void **); /** * crypto_keys_subr_generate_HMAC(key): * Generate an HMAC key. */ int crypto_keys_subr_generate_HMAC(struct crypto_hmac_key **); /** * crypto_keys_subr_free_HMAC(key): * Free an HMAC key. */ void crypto_keys_subr_free_HMAC(struct crypto_hmac_key **); /** * crypto_file_init_keys(void): * Initialize the keys cached by crypto_file. */ int crypto_file_init_keys(void); /** * crypto_keys_init_keycache(void): * Initialize the key cache. */ int crypto_keys_init_keycache(void); /** * crypto_MGF1(seed, seedlen, buf, buflen): * The MGF1 mask generation function, as specified in RFC 3447 section B.2.1, * using SHA256 as the hash function. */ void crypto_MGF1(uint8_t *, size_t, uint8_t *, size_t); #endif /* !CRYPTO_INTERNAL_H_ */ tarsnap-1.0.41/lib/crypto/crypto_keys.c000066400000000000000000000422251476705112600200700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "crypto_compat.h" #include "crypto_entropy.h" #include "sysendian.h" #include "warnp.h" #include "crypto.h" #include "crypto_internal.h" static struct { RSA * sign_priv; RSA * sign_pub; RSA * encr_priv; RSA * encr_pub; RSA * root_pub; struct crypto_hmac_key * hmac_file; struct crypto_hmac_key * hmac_file_write; struct crypto_hmac_key * hmac_chunk; struct crypto_hmac_key * hmac_name; struct crypto_hmac_key * hmac_cparams; struct crypto_hmac_key * auth_put; struct crypto_hmac_key * auth_get; struct crypto_hmac_key * auth_delete; } keycache; static void crypto_keys_atexit(void); /* * External key data format: * 4 byte little-endian integer = length of key data * 1 byte = key type * N bytes = key data, in key-specific format */ #define KEYHEADER_OFFSET_LEN 0 #define KEYHEADER_OFFSET_TYPE 4 #define KEYHEADER_LEN 5 /** * export_key(key, buf, buflen): * If buf != NULL, export the specified key. Return the key length in bytes. */ static uint32_t export_key(int key, uint8_t * buf, size_t buflen) { uint32_t len; switch (key) { case CRYPTO_KEY_SIGN_PRIV: len = crypto_keys_subr_export_RSA_priv(keycache.sign_priv, buf, buflen); break; case CRYPTO_KEY_SIGN_PUB: len = crypto_keys_subr_export_RSA_pub(keycache.sign_pub, buf, buflen); break; case CRYPTO_KEY_ENCR_PRIV: len = crypto_keys_subr_export_RSA_priv(keycache.encr_priv, buf, buflen); break; case CRYPTO_KEY_ENCR_PUB: len = crypto_keys_subr_export_RSA_pub(keycache.encr_pub, buf, buflen); break; case CRYPTO_KEY_HMAC_FILE: len = crypto_keys_subr_export_HMAC(keycache.hmac_file, buf, buflen); break; case CRYPTO_KEY_HMAC_CHUNK: len = crypto_keys_subr_export_HMAC(keycache.hmac_chunk, buf, buflen); break; case CRYPTO_KEY_HMAC_NAME: len = crypto_keys_subr_export_HMAC(keycache.hmac_name, buf, buflen); break; case CRYPTO_KEY_HMAC_CPARAMS: len = crypto_keys_subr_export_HMAC(keycache.hmac_cparams, buf, buflen); break; case CRYPTO_KEY_AUTH_PUT: len = crypto_keys_subr_export_HMAC(keycache.auth_put, buf, buflen); break; case CRYPTO_KEY_AUTH_GET: len = crypto_keys_subr_export_HMAC(keycache.auth_get, buf, buflen); break; case CRYPTO_KEY_AUTH_DELETE: len = crypto_keys_subr_export_HMAC(keycache.auth_delete, buf, buflen); break; default: warn0("Unrecognized key type: %d", key); goto err0; } /* Did the key export fail? */ if (len == (uint32_t)(-1)) goto err0; /* Success! */ return (len); err0: /* Failure! */ return ((uint32_t)(-1)); } /** * crypto_keys_init_keycache(void): * Initialize the key cache. */ int crypto_keys_init_keycache(void) { /* * No keys yet. memset() is insufficient since NULL is not required * to be represented in memory by zeroes. */ keycache.sign_priv = NULL; keycache.sign_pub = NULL; keycache.encr_priv = NULL; keycache.encr_pub = NULL; keycache.root_pub = NULL; keycache.hmac_file = NULL; keycache.hmac_file_write = NULL; keycache.hmac_chunk = NULL; keycache.hmac_name = NULL; keycache.hmac_cparams = NULL; keycache.auth_put = NULL; keycache.auth_get = NULL; keycache.auth_delete = NULL; /* It's now safe to call crypto_keys_atexit() upon exit. */ if (atexit(crypto_keys_atexit)) { warnp("Could not initialize atexit"); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_keys_atexit(void): * Free the key cache. */ static void crypto_keys_atexit(void) { /* Free all RSA keys. */ RSA_free(keycache.sign_priv); RSA_free(keycache.sign_pub); RSA_free(keycache.encr_priv); RSA_free(keycache.encr_pub); RSA_free(keycache.root_pub); /* Free all HMAC keys. */ crypto_keys_subr_free_HMAC(&keycache.hmac_file); crypto_keys_subr_free_HMAC(&keycache.hmac_file_write); crypto_keys_subr_free_HMAC(&keycache.hmac_chunk); crypto_keys_subr_free_HMAC(&keycache.hmac_name); crypto_keys_subr_free_HMAC(&keycache.hmac_cparams); crypto_keys_subr_free_HMAC(&keycache.auth_put); crypto_keys_subr_free_HMAC(&keycache.auth_get); crypto_keys_subr_free_HMAC(&keycache.auth_delete); /* Free shared memory allocated by OpenSSL. */ crypto_compat_free(); } /** * crypto_keys_import(buf, buflen, keys): * Import keys from the provided buffer into the key cache. Ignore any keys * not specified in the mask ${keys}. */ int crypto_keys_import(const uint8_t * buf, size_t buflen, int keys) { const uint8_t * kh; uint32_t len; uint8_t type; /* Loop until we've processed all the provided data. */ while (buflen) { /* We must have at least a key header. */ if (buflen < KEYHEADER_LEN) { warn0("Unexpected EOF of key data"); goto err0; } /* Parse header. */ kh = buf; buf += KEYHEADER_LEN; buflen -= KEYHEADER_LEN; /* Sanity check length. */ len = le32dec(&kh[KEYHEADER_OFFSET_LEN]); if (len > buflen) { warn0("Unexpected EOF of key data"); goto err0; } /* Parse the key. */ type = kh[KEYHEADER_OFFSET_TYPE]; switch (type) { case CRYPTO_KEY_SIGN_PRIV: if ((keys & CRYPTO_KEYMASK_SIGN_PRIV) && crypto_keys_subr_import_RSA_priv( (void**)&keycache.sign_priv, buf, len)) goto err0; break; case CRYPTO_KEY_SIGN_PUB: if ((keys & CRYPTO_KEYMASK_SIGN_PUB) && crypto_keys_subr_import_RSA_pub( (void**)&keycache.sign_pub, buf, len)) goto err0; break; case CRYPTO_KEY_ENCR_PRIV: if ((keys & CRYPTO_KEYMASK_ENCR_PRIV) && crypto_keys_subr_import_RSA_priv( (void**)&keycache.encr_priv, buf, len)) goto err0; break; case CRYPTO_KEY_ENCR_PUB: if ((keys & CRYPTO_KEYMASK_ENCR_PUB) && crypto_keys_subr_import_RSA_pub( (void**)&keycache.encr_pub, buf, len)) goto err0; break; case CRYPTO_KEY_HMAC_FILE: if ((keys & CRYPTO_KEYMASK_HMAC_FILE) && crypto_keys_subr_import_HMAC( &keycache.hmac_file, buf, len)) goto err0; /* * There is normally only one "file hmac" key, used for * both signing blocks which are being written and * verifying blocks which are being read; but in * tarsnap-recrypt we download blocks from one machine * and verify them with one key before re-uploading * them signed with a different key. Consequently, the * tarsnap crypto code internally treats this as two * keys; and we set one or both to the key we're * reading from the key file depending on the flags we * were passed. */ if ((keys & CRYPTO_KEYMASK_HMAC_FILE_WRITE) && crypto_keys_subr_import_HMAC( &keycache.hmac_file_write, buf, len)) goto err0; break; case CRYPTO_KEY_HMAC_CHUNK: if ((keys & CRYPTO_KEYMASK_HMAC_CHUNK) && crypto_keys_subr_import_HMAC( &keycache.hmac_chunk, buf, len)) goto err0; break; case CRYPTO_KEY_HMAC_NAME: if ((keys & CRYPTO_KEYMASK_HMAC_NAME) && crypto_keys_subr_import_HMAC( &keycache.hmac_name, buf, len)) goto err0; break; case CRYPTO_KEY_HMAC_CPARAMS: if ((keys & CRYPTO_KEYMASK_HMAC_CPARAMS) && crypto_keys_subr_import_HMAC( &keycache.hmac_cparams, buf, len)) goto err0; break; case CRYPTO_KEY_ROOT_PUB: if ((keys & CRYPTO_KEYMASK_ROOT_PUB) && crypto_keys_subr_import_RSA_pub( (void**)&keycache.root_pub, buf, len)) goto err0; break; case CRYPTO_KEY_AUTH_PUT: if ((keys & CRYPTO_KEYMASK_AUTH_PUT) && crypto_keys_subr_import_HMAC( &keycache.auth_put, buf, len)) goto err0; break; case CRYPTO_KEY_AUTH_GET: if ((keys & CRYPTO_KEYMASK_AUTH_GET) && crypto_keys_subr_import_HMAC( &keycache.auth_get, buf, len)) goto err0; break; case CRYPTO_KEY_AUTH_DELETE: if ((keys & CRYPTO_KEYMASK_AUTH_DELETE) && crypto_keys_subr_import_HMAC( &keycache.auth_delete, buf, len)) goto err0; break; default: warn0("Unrecognized key type: %d", type); goto err0; } /* Move on to the next key. */ buf += len; buflen -= len; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_keys_missing(keys): * Look for the specified keys. If they are all present, return NULL; if * not, return a pointer to the name of one of the keys. */ const char * crypto_keys_missing(int keys) { const char * keyname = NULL; int key; /* * Go through all the keys we know about and determine if (a) the key * is in the provided mask; and (b) if we do not have it. */ for (key = 0; key < (int)(sizeof(int) * 8); key++) if ((keys >> key) & 1) { switch (key) { case CRYPTO_KEY_SIGN_PRIV: if (keycache.sign_priv == NULL) keyname = "archive signing"; break; case CRYPTO_KEY_SIGN_PUB: if (keycache.sign_pub == NULL) keyname = "archive signature verification"; break; case CRYPTO_KEY_ENCR_PRIV: if (keycache.encr_priv == NULL) keyname = "archive decryption"; break; case CRYPTO_KEY_ENCR_PUB: if (keycache.encr_pub == NULL) keyname = "archive encryption"; break; case CRYPTO_KEY_HMAC_FILE: if (keycache.hmac_file == NULL) keyname = "file HMAC"; break; case CRYPTO_KEY_HMAC_FILE_WRITE: if (keycache.hmac_file_write == NULL) keyname = "file write HMAC"; break; case CRYPTO_KEY_HMAC_CHUNK: if (keycache.hmac_chunk == NULL) keyname = "chunk HMAC"; break; case CRYPTO_KEY_HMAC_NAME: if (keycache.hmac_name == NULL) keyname = "archive name HMAC"; break; case CRYPTO_KEY_HMAC_CPARAMS: if (keycache.hmac_cparams == NULL) keyname = "chunk randomization"; break; case CRYPTO_KEY_ROOT_PUB: if (keycache.root_pub == NULL) keyname = "server root"; break; case CRYPTO_KEY_AUTH_PUT: if (keycache.auth_put == NULL) keyname = "write authorization"; break; case CRYPTO_KEY_AUTH_GET: if (keycache.auth_get == NULL) keyname = "read authorization"; break; case CRYPTO_KEY_AUTH_DELETE: if (keycache.auth_delete == NULL) keyname = "delete authorization"; break; } } /* Return the key name or NULL if we have everything. */ return (keyname); } /** * crypto_keys_export(keys, buf, buflen): * Export the ${keys} specified to a buffer allocated using malloc. */ int crypto_keys_export(int keys, uint8_t ** buf, size_t * buflen) { uint8_t * kh; size_t bufpos; uint32_t len; int key; /* Compute the necessary buffer length. */ *buflen = 0; for (key = 0; key < (int)(sizeof(int) * 8); key++) if ((keys >> key) & 1) { /* Determine the length needed for this key. */ len = export_key(key, NULL, 0); if (len == (uint32_t)(-1)) goto err0; /* Add to buffer length, making sure to avoid overflow. */ if (*buflen > *buflen + len) { errno = ENOMEM; goto err0; } *buflen += len; if (*buflen > *buflen + KEYHEADER_LEN) { errno = ENOMEM; goto err0; } *buflen += KEYHEADER_LEN; } /* Allocate memory. */ if ((*buf = malloc(*buflen)) == NULL) goto err0; /* Export keys. */ bufpos = 0; for (key = 0; key < (int)(sizeof(int) * 8); key++) if ((keys >> key) & 1) { /* Sanity check remaining buffer length. */ if (*buflen - bufpos < KEYHEADER_LEN) { warn0("Programmer error"); goto err1; } /* Export key. */ len = export_key(key, *buf + (bufpos + KEYHEADER_LEN), *buflen - (bufpos + KEYHEADER_LEN)); if (len == (uint32_t)(-1)) goto err1; /* Write key header. */ kh = *buf + bufpos; le32enc(&kh[KEYHEADER_OFFSET_LEN], len); kh[KEYHEADER_OFFSET_TYPE] = key & 0xff; /* Advance buffer position. */ bufpos += KEYHEADER_LEN + len; } /* Sanity-check -- we should have filled the buffer. */ if (bufpos != *buflen) { warn0("Programmer error"); goto err1; } /* Success! */ return (0); err1: free(*buf); err0: /* Failure! */ return (-1); } /** * crypto_keys_generate(keys): * Create the ${keys} specified. */ int crypto_keys_generate(int keys) { /* Archive signing RSA key. */ if (keys & CRYPTO_KEYMASK_SIGN_PRIV) { if ((keys & CRYPTO_KEYMASK_SIGN_PUB) == 0) { warn0("Cannot generate %s without %s", "private key", "public key"); goto err0; } if (crypto_keys_subr_generate_RSA((void *)&keycache.sign_priv, (void *)&keycache.sign_pub)) goto err0; keys &= ~CRYPTO_KEYMASK_SIGN_PRIV; keys &= ~CRYPTO_KEYMASK_SIGN_PUB; } if (keys & CRYPTO_KEYMASK_SIGN_PUB) { warn0("Cannot generate %s without %s", "public key", "private key"); goto err0; } /* Encryption RSA key. */ if (keys & CRYPTO_KEYMASK_ENCR_PRIV) { if ((keys & CRYPTO_KEYMASK_ENCR_PUB) == 0) { warn0("Cannot generate %s without %s", "private key", "public key"); goto err0; } if (crypto_keys_subr_generate_RSA((void *)&keycache.encr_priv, (void *)&keycache.encr_pub)) goto err0; keys &= ~CRYPTO_KEYMASK_ENCR_PRIV; keys &= ~CRYPTO_KEYMASK_ENCR_PUB; } if (keys & CRYPTO_KEYMASK_ENCR_PUB) { warn0("Cannot generate %s without %s", "public key", "private key"); goto err0; } /* File HMAC key. */ if (keys & CRYPTO_KEYMASK_HMAC_FILE) { if (crypto_keys_subr_generate_HMAC(&keycache.hmac_file)) goto err0; keys &= ~CRYPTO_KEYMASK_HMAC_FILE; } /* Chunk HMAC key. */ if (keys & CRYPTO_KEYMASK_HMAC_CHUNK) { if (crypto_keys_subr_generate_HMAC(&keycache.hmac_chunk)) goto err0; keys &= ~CRYPTO_KEYMASK_HMAC_CHUNK; } /* Name HMAC key. */ if (keys & CRYPTO_KEYMASK_HMAC_NAME) { if (crypto_keys_subr_generate_HMAC(&keycache.hmac_name)) goto err0; keys &= ~CRYPTO_KEYMASK_HMAC_NAME; } /* Chunkification parameters HMAC key. */ if (keys & CRYPTO_KEYMASK_HMAC_CPARAMS) { if (crypto_keys_subr_generate_HMAC(&keycache.hmac_cparams)) goto err0; keys &= ~CRYPTO_KEYMASK_HMAC_CPARAMS; } /* Write transaction authorization key. */ if (keys & CRYPTO_KEYMASK_AUTH_PUT) { if (crypto_keys_subr_generate_HMAC(&keycache.auth_put)) goto err0; keys &= ~CRYPTO_KEYMASK_AUTH_PUT; } /* Read transaction authorization key. */ if (keys & CRYPTO_KEYMASK_AUTH_GET) { if (crypto_keys_subr_generate_HMAC(&keycache.auth_get)) goto err0; keys &= ~CRYPTO_KEYMASK_AUTH_GET; } /* Delete transaction authorization key. */ if (keys & CRYPTO_KEYMASK_AUTH_DELETE) { if (crypto_keys_subr_generate_HMAC(&keycache.auth_delete)) goto err0; keys &= ~CRYPTO_KEYMASK_AUTH_DELETE; } /* Anything left? */ if (keys) { warn0("Unrecognized key types: %08x", keys); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_keys_raw_export_auth(buf): * Write into the specified buffer the 32-byte write authorization key, * the 32-byte read authorization key, and the 32-byte delete authorization * key, in that order. */ int crypto_keys_raw_export_auth(uint8_t buf[96]) { uint32_t len; len = export_key(CRYPTO_KEY_AUTH_PUT, buf, 32); if (len == (uint32_t)(-1)) goto err0; if (len != 32) { warn0("Programmer error: " "Incorrect HMAC key size: %u", (unsigned int)len); goto err0; } len = export_key(CRYPTO_KEY_AUTH_GET, buf + 32, 32); if (len == (uint32_t)(-1)) goto err0; if (len != 32) { warn0("Programmer error: " "Incorrect HMAC key size: %u", (unsigned int)len); goto err0; } len = export_key(CRYPTO_KEY_AUTH_DELETE, buf + 64, 32); if (len == (uint32_t)(-1)) goto err0; if (len != 32) { warn0("Programmer error: " "Incorrect HMAC key size: %u", (unsigned int)len); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_keys_lookup_RSA(key): * Return the requested RSA key. */ void * crypto_keys_lookup_RSA(int key) { RSA * rsa; /* Look up the key. */ switch (key) { case CRYPTO_KEY_SIGN_PRIV: rsa = keycache.sign_priv; break; case CRYPTO_KEY_SIGN_PUB: rsa = keycache.sign_pub; break; case CRYPTO_KEY_ENCR_PRIV: rsa = keycache.encr_priv; break; case CRYPTO_KEY_ENCR_PUB: rsa = keycache.encr_pub; break; case CRYPTO_KEY_ROOT_PUB: rsa = keycache.root_pub; break; default: warn0("Programmer error: " "invalid key (%d) in crypto_keys_lookup_RSA", key); goto err0; } /* Make sure that we have the key. */ if (rsa == NULL) { warn0("Programmer error: " "key %d not available in crypto_keys_lookup_RSA", key); goto err0; } /* Success! */ return (rsa); err0: /* Failure! */ return (NULL); } /** * crypto_keys_lookup_HMAC(key): * Return the requested HMAC key. */ struct crypto_hmac_key * crypto_keys_lookup_HMAC(int key) { struct crypto_hmac_key * hkey; /* Look up the key. */ switch (key) { case CRYPTO_KEY_HMAC_FILE: hkey = keycache.hmac_file; break; case CRYPTO_KEY_HMAC_FILE_WRITE: hkey = keycache.hmac_file_write; break; case CRYPTO_KEY_HMAC_CHUNK: hkey = keycache.hmac_chunk; break; case CRYPTO_KEY_HMAC_NAME: hkey = keycache.hmac_name; break; case CRYPTO_KEY_HMAC_CPARAMS: hkey = keycache.hmac_cparams; break; case CRYPTO_KEY_AUTH_PUT: hkey = keycache.auth_put; break; case CRYPTO_KEY_AUTH_GET: hkey = keycache.auth_get; break; case CRYPTO_KEY_AUTH_DELETE: hkey = keycache.auth_delete; break; default: warn0("Programmer error: " "invalid key (%d) in crypto_keys_lookup_HMAC", key); goto err0; } /* Make sure that we have the key. */ if (hkey == NULL) { warn0("Programmer error: " "key %d not available in crypto_keys_lookup_HMAC", key); goto err0; } /* Success! */ return (hkey); err0: /* Failure! */ return (NULL); } tarsnap-1.0.41/lib/crypto/crypto_keys_init.c000066400000000000000000000020411476705112600211030ustar00rootroot00000000000000#include #include #include #include "crypto_entropy.h" #include "warnp.h" #include "crypto.h" #include "crypto_internal.h" /* Amount of entropy to use for seeding OpenSSL. */ #define RANDBUFLEN 2048 /** * crypto_keys_init(void): * Initialize cryptographic keys. */ int crypto_keys_init(void) { uint8_t randbuf[RANDBUFLEN]; /* Initialize key cache. */ if (crypto_keys_init_keycache()) goto err0; /* Load OpenSSL error strings. */ ERR_load_crypto_strings(); /* Seed OpenSSL entropy pool. */ if (crypto_entropy_read(randbuf, RANDBUFLEN)) { warnp("Could not obtain sufficient entropy"); goto err0; } RAND_seed(randbuf, RANDBUFLEN); /* Load server root public key. */ if (crypto_keys_server_import_root()) { warn0("Could not import server root public key"); goto err0; } /* Initialize keys owned by crypto_file. */ if (crypto_file_init_keys()) { warn0("Could not initialize crypto_file keys"); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/crypto/crypto_keys_server.c000066400000000000000000000040641476705112600214550ustar00rootroot00000000000000/* DO NOT EDIT! This file is automatically generated. */ #include #include #include "crypto.h" #include "crypto_internal.h" static const size_t root_key_len = 272; static const uint8_t root_key[] = { 0x0b, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0xe5, 0x0e, 0xf8, 0x6b, 0x0b, 0xa9, 0xa6, 0xb8, 0x7d, 0x72, 0xf9, 0x9a, 0xa8, 0x7e, 0x42, 0x9e, 0x4b, 0xdf, 0x99, 0xb8, 0xcc, 0x91, 0x4b, 0x16, 0xad, 0x0f, 0xf2, 0x7d, 0x03, 0xe6, 0x6c, 0xb0, 0xa8, 0x32, 0xbe, 0x53, 0x03, 0xb6, 0x6f, 0x9b, 0x0f, 0xff, 0x0e, 0x63, 0xe3, 0x4d, 0x39, 0x0c, 0xb8, 0x58, 0x4d, 0x55, 0x8e, 0x6a, 0xff, 0x50, 0xc9, 0x00, 0xa3, 0xb8, 0x92, 0x97, 0xf8, 0xf6, 0x55, 0x4c, 0x63, 0x65, 0xc7, 0x15, 0xed, 0x1d, 0xdc, 0xfb, 0x5f, 0x98, 0xda, 0x02, 0xf1, 0x72, 0xf0, 0x13, 0xd7, 0x50, 0xee, 0xc4, 0x5c, 0x13, 0x23, 0x7e, 0xda, 0xc9, 0xb9, 0x9a, 0xa1, 0x84, 0x37, 0xa2, 0xf2, 0x56, 0xdc, 0x97, 0x1b, 0x65, 0xa7, 0xfb, 0xb5, 0xb2, 0x32, 0xa1, 0xca, 0x06, 0x84, 0xc5, 0x7b, 0x76, 0x00, 0x53, 0x8a, 0x62, 0xed, 0x35, 0x16, 0x32, 0xd4, 0xa2, 0xda, 0xed, 0x51, 0xe0, 0x48, 0x19, 0x77, 0x7a, 0xc6, 0x4a, 0x21, 0x02, 0xd6, 0xfb, 0xe3, 0x78, 0xd9, 0x70, 0x83, 0xd2, 0xa8, 0x07, 0xf2, 0xa6, 0x91, 0x85, 0x01, 0xc4, 0xe4, 0x75, 0x0f, 0x75, 0x4b, 0xbc, 0xa0, 0xfd, 0x57, 0xb5, 0x30, 0x31, 0x65, 0x21, 0x95, 0x3b, 0x4e, 0xe5, 0xd4, 0x07, 0xcd, 0x61, 0xb7, 0x77, 0x45, 0x0a, 0xec, 0xf2, 0xf6, 0xc2, 0x79, 0xda, 0xb6, 0x93, 0xa9, 0xd1, 0xd4, 0x39, 0x19, 0x8a, 0xb6, 0x46, 0xaa, 0x06, 0x43, 0xf6, 0x06, 0x7d, 0x20, 0xb2, 0xf6, 0x21, 0x91, 0x97, 0x6b, 0x31, 0x4f, 0xfb, 0x40, 0x5d, 0x5e, 0xdc, 0x53, 0xc5, 0xf4, 0x57, 0x6e, 0xe3, 0x2e, 0x4f, 0x95, 0xda, 0xca, 0x82, 0xf1, 0x7a, 0x8c, 0xde, 0xf2, 0xb7, 0x04, 0x5a, 0xc6, 0x7a, 0xf6, 0xdb, 0x8e, 0x6e, 0x81, 0x0e, 0x37, 0x32, 0x6b, 0xb1, 0x27, 0xe1, 0xc5, 0xe2, 0x70, 0x65, 0xc3, 0xb8, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01 }; /** * crypto_keys_server_import_root(void): * Import the public part of the server root key. */ int crypto_keys_server_import_root(void) { return (crypto_keys_import(root_key, root_key_len, ~0)); } tarsnap-1.0.41/lib/crypto/crypto_keys_subr.c000066400000000000000000000253121476705112600211210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "crypto_compat.h" #include "crypto_entropy.h" #include "sysendian.h" #include "warnp.h" #include "crypto_internal.h" /** * RSA private key data format: * n || e || d || p || q || (d mod (p-1)) || (d mod (q-1)) || (1/q mod p) * RSA public key data format: * n || e * All integers are stored in little-endian large integer format: * len || x[0] || x[1] ... x[len - 1] * where len is a 32-bit little-endian integer. */ /** * HMAC key data format: * x[0] || x[1] || x[2] ... x[31] */ static int import_BN(BIGNUM **, const uint8_t **, size_t *); static int export_BN(const BIGNUM *, uint8_t **, size_t *, uint32_t *); /** * import_BN(bn, buf, buflen): * Import a large integer from the provided buffer, advance the buffer * pointer, and adjust the remaining buffer length. */ static int import_BN(BIGNUM ** bn, const uint8_t ** buf, size_t * buflen) { uint32_t len; uint8_t * bnbuf; size_t i; /* Parse integer length. */ if (*buflen < sizeof(uint32_t)) { warn0("Unexpected EOF of key data"); goto err0; } len = le32dec(*buf); *buf += sizeof(uint32_t); *buflen -= sizeof(uint32_t); /* Sanity check. */ if (len > INT_MAX) { warn0("Unexpected key length"); goto err0; } /* Make sure there's enough data. */ if (*buflen < len) { warn0("Unexpected EOF of key data"); goto err0; } /* * OpenSSL's BN_bin2bn wants input in big-endian format, so we need * to use a temporary buffer to convert from le to be. */ if ((bnbuf = malloc(len)) == NULL) goto err0; for (i = 0; i < len; i++) bnbuf[len - 1 - i] = (*buf)[i]; if ((*bn = BN_bin2bn(bnbuf, (int)len, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err1; } free(bnbuf); /* Advance buffer pointer, adjust remaining buffer length. */ *buf += len; *buflen -= len; /* Success! */ return (0); err1: free(bnbuf); err0: /* Failure! */ return (-1); } /** * export_BN(bn, buf, buflen, len): * If ${*buf} != NULL, export the provided large integer into the buffer, * and adjust the buffer pointer and remaining buffer length appropriately. * Add the required storage length to ${len}. */ static int export_BN(const BIGNUM * bn, uint8_t ** buf, size_t * buflen, uint32_t * len) { size_t i; unsigned int bnlen; /* Figure out how much space we need. */ bnlen = (unsigned int)BN_num_bytes(bn); /* Add the required storage length to ${len}. */ if (*len + sizeof(uint32_t) < *len) { errno = ENOMEM; goto err0; } *len += sizeof(uint32_t); if (*len + bnlen < *len) { errno = ENOMEM; goto err0; } *len += bnlen; /* If ${*buf} == NULL, we're done. */ if (*buf == NULL) goto done; /* Export the length of the integer. */ if (*buflen < sizeof(uint32_t)) { warn0("Unexpected end of key buffer"); goto err0; } le32enc(*buf, bnlen); *buf += sizeof(uint32_t); *buflen -= sizeof(uint32_t); /* Export the key as a big-endian integer. */ if (*buflen < bnlen) { warn0("Unexpected end of key buffer"); goto err0; } BN_bn2bin(bn, *buf); /* Convert to little-endian format. */ for (i = 0; i < bnlen - 1 - i; i++) { (*buf)[i] ^= (*buf)[bnlen - 1 - i]; (*buf)[bnlen - 1 - i] ^= (*buf)[i]; (*buf)[i] ^= (*buf)[bnlen - 1 - i]; } /* Adjust buffer pointer and remaining buffer length. */ *buf += bnlen; *buflen -= bnlen; done: /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_keys_subr_import_RSA_priv(key, buf, buflen): * Import the specified RSA private key from the provided buffer. */ int crypto_keys_subr_import_RSA_priv(void ** key, const uint8_t * buf, size_t buflen) { BIGNUM * n, * e, * d, * p, * q, * dmp1, * dmq1, * iqmp; /* This simplifies the error path cleanup. */ n = e = d = p = q = dmp1 = dmq1 = iqmp = NULL; /* Free any existing key. */ if (*key != NULL) RSA_free(*key); *key = NULL; /* Create a new key. */ if ((*key = RSA_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Load values. */ if (import_BN(&n, &buf, &buflen)) goto err2; if (import_BN(&e, &buf, &buflen)) goto err2; if (import_BN(&d, &buf, &buflen)) goto err2; if (import_BN(&p, &buf, &buflen)) goto err2; if (import_BN(&q, &buf, &buflen)) goto err2; if (import_BN(&dmp1, &buf, &buflen)) goto err2; if (import_BN(&dmq1, &buf, &buflen)) goto err2; if (import_BN(&iqmp, &buf, &buflen)) goto err2; /* We should have no unprocessed data left. */ if (buflen) goto err2; /* Load values into the RSA key. */ if (crypto_compat_RSA_import(*key, n, e, d, p, q, dmp1, dmq1, iqmp)) goto err1; /* Success! */ return (0); err2: BN_free(n); BN_free(e); BN_clear_free(d); BN_clear_free(p); BN_clear_free(q); BN_clear_free(dmp1); BN_clear_free(dmq1); BN_clear_free(iqmp); err1: RSA_free(*key); *key = NULL; err0: /* Failure! */ return (-1); } /** * crypto_keys_subr_import_RSA_pub(key, buf, buflen): * Import the specified RSA public key from the provided buffer. */ int crypto_keys_subr_import_RSA_pub(void ** key, const uint8_t * buf, size_t buflen) { BIGNUM * n, * e; /* This simplifies the error path cleanup. */ n = e = NULL; /* Free any existing key. */ if (*key != NULL) RSA_free(*key); *key = NULL; /* Create a new key. */ if ((*key = RSA_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Load values. */ if (import_BN(&n, &buf, &buflen)) goto err2; if (import_BN(&e, &buf, &buflen)) goto err2; /* We should have no unprocessed data left. */ if (buflen) goto err2; /* Load values into the RSA key. */ if (crypto_compat_RSA_import(*key, n, e, NULL, NULL, NULL, NULL, NULL, NULL)) goto err1; /* Success! */ return (0); err2: BN_free(n); BN_free(e); err1: RSA_free(*key); *key = NULL; err0: /* Failure! */ return (-1); } /** * crypto_keys_subr_import_HMAC(key, buf, buflen): * Import the specified HMAC key from the provided buffer. */ int crypto_keys_subr_import_HMAC(struct crypto_hmac_key ** key, const uint8_t * buf, size_t buflen) { /* Free any existing key. */ if (*key != NULL) { free((*key)->key); free(*key); } *key = NULL; /* Make sure the buffer is the right length. */ if (buflen != 32) { warn0("Incorrect HMAC key size: %zu", buflen); goto err0; } /* Allocate key structure. */ if ((*key = malloc(sizeof(struct crypto_hmac_key))) == NULL) goto err0; /* Allocate key buffer. */ if (((*key)->key = malloc(buflen)) == NULL) goto err1; /* Copy key data and length. */ (*key)->len = buflen; memcpy((*key)->key, buf, buflen); /* Success! */ return (0); err1: free(*key); *key = NULL; err0: /* Failure! */ return (-1); } /** * crypto_keys_subr_export_RSA_priv(key, buf, buflen): * If buf != NULL, export the specified RSA private key. Return the key * length in bytes. */ uint32_t crypto_keys_subr_export_RSA_priv(void * key, uint8_t * buf, size_t buflen) { const BIGNUM * n, * e, * d, * p, * q, * dmp1, * dmq1, * iqmp; uint32_t len = 0; if (key == NULL) { warn0("Cannot export a key which we don't have!"); goto err0; } /* Get values from the RSA key. */ if (crypto_compat_RSA_export(key, &n, &e, &d, &p, &q, &dmp1, &dmq1, &iqmp)) goto err0; /* Each large integer gets exported. */ if (export_BN(n, &buf, &buflen, &len)) goto err0; if (export_BN(e, &buf, &buflen, &len)) goto err0; if (export_BN(d, &buf, &buflen, &len)) goto err0; if (export_BN(p, &buf, &buflen, &len)) goto err0; if (export_BN(q, &buf, &buflen, &len)) goto err0; if (export_BN(dmp1, &buf, &buflen, &len)) goto err0; if (export_BN(dmq1, &buf, &buflen, &len)) goto err0; if (export_BN(iqmp, &buf, &buflen, &len)) goto err0; /* Success! */ return (len); err0: /* Failure! */ return ((uint32_t)(-1)); } /** * crypto_keys_subr_export_RSA_pub(key, buf, buflen): * If buf != NULL, export the specified RSA public key. Return the key * length in bytes. */ uint32_t crypto_keys_subr_export_RSA_pub(void * key, uint8_t * buf, size_t buflen) { const BIGNUM * n, * e; uint32_t len = 0; if (key == NULL) { warn0("Cannot export a key which we don't have!"); goto err0; } /* Get values from the RSA key. */ if (crypto_compat_RSA_export(key, &n, &e, NULL, NULL, NULL, NULL, NULL, NULL)) goto err0; /* Each large integer gets exported. */ if (export_BN(n, &buf, &buflen, &len)) goto err0; if (export_BN(e, &buf, &buflen, &len)) goto err0; /* Success! */ return (len); err0: /* Failure! */ return ((uint32_t)(-1)); } /** * crypto_keys_subr_export_HMAC(key, buf, buflen): * If buf != NULL, export the specified HMAC key. Return the key length * in bytes. */ uint32_t crypto_keys_subr_export_HMAC(struct crypto_hmac_key * key, uint8_t * buf, size_t buflen) { if (key == NULL) { warn0("Cannot export a key which we don't have!"); goto err0; } /* Sanity check. (uint32_t)(-1) is reserved for errors. */ assert(key->len <= UINT32_MAX - 1); if (buf != NULL) { if (buflen < key->len) { warn0("Unexpected end of key buffer"); goto err0; } memcpy(buf, key->key, key->len); } /* Success! */ return ((uint32_t)(key->len)); err0: /* Failure! */ return ((uint32_t)(-1)); } /** * crypto_keys_subr_generate_RSA(priv, pub): * Generate an RSA key and store the private and public parts. */ int crypto_keys_subr_generate_RSA(void ** priv, void ** pub) { /* Free any existing keys. */ if (*priv != NULL) RSA_free(*priv); if (*pub != NULL) RSA_free(*pub); *priv = *pub = NULL; if ((*priv = crypto_compat_RSA_generate_key()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } if ((*pub = RSAPublicKey_dup(*priv)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err1; } /* Success! */ return (0); err1: RSA_free(*priv); *priv = NULL; err0: /* Failure! */ return (-1); } /** * crypto_keys_subr_generate_HMAC(key): * Generate an HMAC key. */ int crypto_keys_subr_generate_HMAC(struct crypto_hmac_key ** key) { /* Free any existing key. */ if (*key != NULL) { free((*key)->key); free(*key); } /* Allocate memory. */ if ((*key = malloc(sizeof(struct crypto_hmac_key))) == NULL) goto err0; if (((*key)->key = malloc(32)) == NULL) goto err1; /* Store key length. */ (*key)->len = 32; /* Generate key. */ if (crypto_entropy_read((*key)->key, 32)) { warnp("Could not obtain sufficient entropy"); goto err2; } /* Success! */ return (0); err2: free((*key)->key); err1: free(*key); err0: /* Failure! */ return (-1); } /** * crypto_keys_subr_free_HMAC(key): * Free an HMAC key. */ void crypto_keys_subr_free_HMAC(struct crypto_hmac_key ** key) { if (*key != NULL) { free((*key)->key); free(*key); } *key = NULL; } tarsnap-1.0.41/lib/crypto/crypto_passwd_to_dh.c000066400000000000000000000016241476705112600215710ustar00rootroot00000000000000#include #include #include "crypto_dh.h" #include "ctassert.h" #include "crypto.h" /* We use HMAC-SHA256 to generate a DH private key; so the size must match. */ CTASSERT(CRYPTO_DH_PRIVLEN == 32); /** * crypto_passwd_to_dh(passwd, salt, pub, priv): * Generate a Diffie-Hellman pair (${priv}, ${pub}), with ${pub} equal to * 2^(2^258 + ${priv}) modulo the group #14 modulus, and ${priv} equal to * HMAC(${salt}, ${passwd}), where ${passwd} is a NUL-terminated string. */ int crypto_passwd_to_dh(const char * passwd, const uint8_t salt[32], uint8_t pub[CRYPTO_DH_PUBLEN], uint8_t priv[CRYPTO_DH_PRIVLEN]) { /* Generate private key via HMAC. */ crypto_hash_data_key(salt, 32, (const uint8_t *)passwd, strlen(passwd), priv); /* Generate public part. */ if (crypto_dh_generate_pub(pub, priv)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/crypto/crypto_rsa.c000066400000000000000000000307641476705112600177070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "crypto_compat.h" #include "crypto_entropy.h" #include "crypto_verify_bytes.h" #include "sysendian.h" #include "warnp.h" #include "crypto_internal.h" #include "crypto.h" /** * crypto_MGF1(seed, seedlen, buf, buflen): * The MGF1 mask generation function, as specified in RFC 3447 section B.2.1, * using SHA256 as the hash function. */ void crypto_MGF1(uint8_t * seed, size_t seedlen, uint8_t * buf, size_t buflen) { uint8_t hbuf[32]; size_t pos; uint32_t i; uint8_t C[4]; /* Sanity check for I2OSP function. */ assert(((buflen - 1) / 32) <= UINT32_MAX); /* Iterate through the buffer. */ for (pos = 0; pos < buflen; pos += 32) { /* The ith block starts at position i * 32. */ i = (uint32_t)(pos / 32); /* Convert counter to big-endian format. */ be32enc(C, i); /* Compute the hash of (seed || C). */ if (crypto_hash_data_2(CRYPTO_KEY_HMAC_SHA256, seed, seedlen, C, 4, hbuf)) { warn0("Programmer error: " "SHA256 should never fail"); abort(); } /* Copy as much data as needed. */ if (buflen - pos > 32) memcpy(buf + pos, hbuf, 32); else memcpy(buf + pos, hbuf, buflen - pos); } } /** * crypto_rsa_sign(key, data, len, sig, siglen): * Sign the provided ${data} with the specified ${key}, writing the signature * into ${sig}. */ int crypto_rsa_sign(int key, const uint8_t * data, size_t len, uint8_t * sig, size_t siglen) { RSA * rsa; /* RSA key used for signing. */ uint8_t mHash[32]; uint8_t salt[32]; uint8_t Mprime[72]; uint8_t H[32]; uint8_t DB[223]; uint8_t dbMask[223]; uint8_t maskedDB[223]; uint8_t EM[256]; size_t i; /* Find the required key. */ if ((rsa = crypto_keys_lookup_RSA(key)) == NULL) goto err0; /* Make sure the key and signature buffer are the correct size. */ if (!crypto_compat_RSA_valid_size(rsa)) { warn0("RSA key is incorrect size"); goto err0; } if (siglen != 256) { warn0("Programmer error: " "signature buffer is incorrect length"); goto err0; } /* Generate mHash as specified in EMSA-PSS-ENCODE from RFC 3447. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, data, len, mHash)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* Generate random salt. */ if (crypto_entropy_read(salt, 32)) { warnp("Could not obtain sufficient entropy"); goto err0; } /* Construct M'. */ memset(Mprime, 0, 8); memcpy(Mprime + 8, mHash, 32); memcpy(Mprime + 40, salt, 32); /* Construct H. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, Mprime, 72, H)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* Construct DB. */ memset(DB, 0, 190); memset(DB + 190, 1, 1); memcpy(DB + 191, salt, 32); /* Construct dbMask and maskedDB. */ crypto_MGF1(H, 32, dbMask, 223); for (i = 0; i < 223; i++) maskedDB[i] = DB[i] ^ dbMask[i]; maskedDB[0] &= 0x7f; /* Construct EM. */ memcpy(EM, maskedDB, 223); memcpy(EM + 223, H, 32); memset(EM + 255, 0xbc, 1); /* Convert EM to a signature, via RSA. */ if (RSA_private_encrypt(256, EM, sig, rsa, RSA_NO_PADDING) != 256) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_rsa_verify(key, data, len, sig, siglen): * Verify that the provided signature ${sig} matches the provided ${data}. * Return 0 if the signature is valid, 1 if the signature is invalid, or -1 * on error. */ int crypto_rsa_verify(int key, const uint8_t * data, size_t len, const uint8_t * sig, size_t siglen) { RSA * rsa; uint8_t EM[256]; uint8_t mHash[32]; uint8_t maskedDB[223]; uint8_t H[32]; uint8_t dbMask[223]; uint8_t DB[223]; uint8_t salt[32]; uint8_t Mprime[72]; uint8_t Hprime[32]; size_t i; unsigned long rsaerr; /* Sanity check. */ assert(siglen < INT_MAX); /* Find the required key. */ if ((rsa = crypto_keys_lookup_RSA(key)) == NULL) goto err0; /* Make sure the key and signature buffer are the correct size. */ if (!crypto_compat_RSA_valid_size(rsa)) { warn0("RSA key is incorrect size"); goto err0; } if (siglen != 256) { warn0("Programmer error: " "signature buffer is incorrect length"); goto err0; } /* Convert the signature to EM, via RSA. */ if (RSA_public_decrypt((int)siglen, sig, EM, rsa, RSA_NO_PADDING) != 256) { /* * We can only distinguish between a bad signature and an * internal error in OpenSSL by looking at the error code. */ rsaerr = ERR_get_error(); if (rsaerr == RSA_R_DATA_TOO_LARGE_FOR_MODULUS) goto bad; /* Anything else is an internal error in OpenSSL. */ warn0("%s", ERR_error_string(rsaerr, NULL)); goto err0; } /* Generate mHash as specified in EMSA-PSS-VERIFY from RFC 3447. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, data, len, mHash)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* Verify rightmost octet of EM. */ if (EM[255] != 0xbc) goto bad; /* Construct maskedDB and H. */ memcpy(maskedDB, EM, 223); memcpy(H, EM + 223, 32); /* Verify high bit of leftmost octet of maskedDB. */ if (maskedDB[0] & 0x80) goto bad; /* Construct dbMask and DB. */ crypto_MGF1(H, 32, dbMask, 223); for (i = 0; i < 223; i++) DB[i] = maskedDB[i] ^ dbMask[i]; /* Set high bit of leftmost octet of DB to zero. */ DB[0] &= 0x7f; /* Verify padding in DB. */ for (i = 0; i < 190; i++) if (DB[i] != 0) goto bad; if (DB[190] != 1) goto bad; /* Construct salt. */ memcpy(salt, DB + 191, 32); /* Construct M'. */ memset(Mprime, 0, 8); memcpy(Mprime + 8, mHash, 32); memcpy(Mprime + 40, salt, 32); /* Construct H'. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, Mprime, 72, Hprime)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* Verify that H' == H. */ if (crypto_verify_bytes(H, Hprime, 32)) goto bad; /* Success! */ return (0); bad: /* Bad signature. */ return (1); err0: /* Failure! */ return (-1); } /** * crypto_rsa_encrypt(key, data, len, out, outlen): * Encrypt the provided ${data} with the specified ${key}, writing the * ciphertext into ${out} (of length ${outlen}). */ int crypto_rsa_encrypt(int key, const uint8_t * data, size_t len, uint8_t * out, size_t outlen) { RSA * rsa; uint8_t lHash[32]; uint8_t DB[223]; uint8_t seed[32]; uint8_t dbMask[223]; uint8_t maskedDB[223]; uint8_t seedMask[32]; uint8_t maskedSeed[32]; uint8_t EM[256]; size_t i; /* Find the required key. */ if ((rsa = crypto_keys_lookup_RSA(key)) == NULL) goto err0; /* Make sure the key and ciphertext buffer are the correct size. */ if (!crypto_compat_RSA_valid_size(rsa)) { warn0("RSA key is incorrect size"); goto err0; } if (outlen != 256) { warn0("Programmer error: " "ciphertext buffer is incorrect length"); goto err0; } /* Make sure the input is not too long. */ if (len > 190) { warn0("Programmer error: " "input to crypto_rsa_encrypt is too long"); goto err0; } /* Construct lHash as specified in RSAES-OAEP-ENCRYPT in RFC 3447. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, NULL, 0, lHash)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* Construct DB. */ memcpy(DB, lHash, 32); memset(DB + 32, 0, 190 - len); memset(DB + 222 - len, 1, 1); memcpy(DB + 223 - len, data, len); /* Generate random seed. */ if (crypto_entropy_read(seed, 32)) { warnp("Could not obtain sufficient entropy"); goto err0; } /* Construct dbMask and maskedDB. */ crypto_MGF1(seed, 32, dbMask, 223); for (i = 0; i < 223; i++) maskedDB[i] = DB[i] ^ dbMask[i]; /* Construct seedMask and maskedSeed. */ crypto_MGF1(maskedDB, 223, seedMask, 32); for (i = 0; i < 32; i++) maskedSeed[i] = seed[i] ^ seedMask[i]; /* Construct EM. */ memset(EM, 0, 1); memcpy(EM + 1, maskedSeed, 32); memcpy(EM + 33, maskedDB, 223); /* Convert EM to ciphertext, via RSA. */ if (RSA_public_encrypt(256, EM, out, rsa, RSA_NO_PADDING) != 256) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_rsa_decrypt(key, data, len, out, outlen): * Decrypt the provided ${data} with the specified ${key}, writing the * ciphertext into ${out} (of length ${*outlen}). Set ${*outlen} to the * length of the plaintext, and return 0 on success, 1 if the ciphertext is * invalid, or 1 on error. */ int crypto_rsa_decrypt(int key, const uint8_t * data, size_t len, uint8_t * out, size_t * outlen) { RSA * rsa; uint8_t EM[256]; uint8_t lHash[32]; uint8_t baddata, paddingmask; uint8_t maskedSeed[32]; uint8_t maskedDB[223]; uint8_t seedMask[32]; uint8_t seed[32]; uint8_t dbMask[223]; uint8_t DB[223]; size_t msglen; size_t i; unsigned long rsaerr; /* Sanity check. */ assert(len < INT_MAX); /* Find the required key. */ if ((rsa = crypto_keys_lookup_RSA(key)) == NULL) goto err0; /* Make sure the key and ciphertext buffer are the correct size. */ if (!crypto_compat_RSA_valid_size(rsa)) { warn0("RSA key is incorrect size"); goto err0; } if (len != 256) { warn0("Programmer error: " "ciphertext buffer is incorrect length"); goto err0; } /* Make sure the plaintext buffer is large enough. */ if (*outlen < 256) { warn0("Programmer error: " "plaintext buffer is too small"); goto err0; } /* Convert the ciphertext to EM, via RSA. */ if (RSA_private_decrypt((int)len, data, EM, rsa, RSA_NO_PADDING) != 256) { /* * We can only distinguish between bad ciphertext and an * internal error in OpenSSL by looking at the error code. */ rsaerr = ERR_get_error(); if (rsaerr == RSA_R_DATA_TOO_LARGE_FOR_MODULUS) goto bad; /* Anything else is an internal error in OpenSSL. */ warn0("%s", ERR_error_string(rsaerr, NULL)); goto err0; } /* Construct lHash as specified in RSAES-OAEP-DECRYPT in RFC 3447. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, NULL, 0, lHash)) { warn0("Programmer error: " "SHA256 should never fail"); goto err0; } /* * The high byte of EM must be zero. We test this later to avoid * timing side channel attacks. */ baddata = EM[0]; /* Construct maskedSeed and maskedDB. */ memcpy(maskedSeed, EM + 1, 32); memcpy(maskedDB, EM + 33, 223); /* Construct seedMask and seed. */ crypto_MGF1(maskedDB, 223, seedMask, 32); for (i = 0; i < 32; i++) seed[i] = maskedSeed[i] ^ seedMask[i]; /* Construct dbMask and DB. */ crypto_MGF1(seed, 32, dbMask, 223); for (i = 0; i < 223; i++) DB[i] = maskedDB[i] ^ dbMask[i]; /* * The leading 32 bytes of DB must be equal to lHash. Test them all * at once, simultaneous with other tests, in order to avoid timing * side channel attacks. */ baddata = baddata | crypto_verify_bytes(DB, lHash, 32); /* * Bytes 33 -- 223 of DB must be zero bytes followed by a one byte * followed by the real data. The following code will set baddata * to a non-zero value if there are non-{0, 1} bytes which are not * separated from the start by a 1 byte. */ paddingmask = 0xff; msglen = 191; for (i = 32; i < 223; i++) { /* If we're still doing padding, DB[i] should be 0 or 1. */ baddata = baddata | (paddingmask & DB[i] & 0xfe); /* * If baddata is still 0, paddingmask is either 0xff or 0x00 * depending upon whether the current byte is padding or not. * Treating it as a signed integer and adding it to msglen * will result in msglen holding the length of the message * after the padding is removed. */ msglen += (size_t)((int8_t)(paddingmask)); /*- * If baddata is still 0, there are 3 cases: * 1. We're no longer looking at padding, and paddingmask is * 0x00, so &ing it with something won't change it. * 2. We're looking at a 0 byte of padding, paddingmask is * 0xff, and we want it to remain 0xff. * 3. We're looking at a 1 byte of padding, paddingmask is * 0xff, and we want it to become 0x00. * In all three cases, &ing the byte minus one does what we * want. */ paddingmask = paddingmask & (DB[i] - 1); } /* Once we hit the end, the padding should be over. */ baddata = baddata | paddingmask; /* Is the data bad? */ if (baddata) goto bad; /* Sanity check the message length. */ if (msglen > *outlen) { warn0("Programmer error: " "decrypted message length is insane"); goto err0; } /* Copy the message into the output buffer. */ memcpy(out, DB + 223 - msglen, msglen); *outlen = msglen; /* Success! */ return (0); bad: /* Bad signature. */ return (1); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/crypto/crypto_scrypt_smix.c000066400000000000000000000136241476705112600215020ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include #include #include "sysendian.h" #include "crypto_scrypt_smix.h" static void blkcpy(uint32_t *, const uint32_t *, size_t); static void blkxor(uint32_t *, const uint32_t *, size_t); static void salsa20_8(uint32_t[16]); static void blockmix_salsa8(const uint32_t *, uint32_t *, uint32_t *, size_t); static uint64_t integerify(const uint32_t *, size_t); static void blkcpy(uint32_t * dest, const uint32_t * src, size_t len) { memcpy(dest, src, len); } static void blkxor(uint32_t * dest, const uint32_t * src, size_t len) { size_t i; for (i = 0; i < len / 4; i++) dest[i] ^= src[i]; } /** * salsa20_8(B): * Apply the salsa20/8 core to the provided block. */ static void salsa20_8(uint32_t B[16]) { uint32_t x[16]; size_t i; blkcpy(x, B, 64); for (i = 0; i < 8; i += 2) { #define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) /* Operate on columns. */ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); /* Operate on rows. */ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); #undef R } for (i = 0; i < 16; i++) B[i] += x[i]; } /** * blockmix_salsa8(Bin, Bout, X, r): * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r * bytes in length; the output Bout must also be the same size. The * temporary space X must be 64 bytes. */ static void blockmix_salsa8(const uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) { size_t i; /* 1: X <-- B_{2r - 1} */ blkcpy(X, &Bin[(2 * r - 1) * 16], 64); /* 2: for i = 0 to 2r - 1 do */ for (i = 0; i < 2 * r; i += 2) { /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 16], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 8], X, 64); /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 16 + 16], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 8 + r * 16], X, 64); } } /** * integerify(B, r): * Return the result of parsing B_{2r-1} as a little-endian integer. */ static uint64_t integerify(const uint32_t * B, size_t r) { const uint32_t * X = B + (2 * r - 1) * 16; return (((uint64_t)(X[1]) << 32) + X[0]); } /** * crypto_scrypt_smix(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. */ void crypto_scrypt_smix(uint8_t * B, size_t r, uint64_t N, void * _v, void * XY) { uint32_t * X = XY; uint32_t * Y = (void *)((uint8_t *)(XY) + 128 * r); uint32_t * Z = (void *)((uint8_t *)(XY) + 256 * r); uint32_t * V = _v; uint64_t i; uint64_t j; size_t k; /* 1: X <-- B */ for (k = 0; k < 32 * r; k++) X[k] = le32dec(&B[4 * k]); /* 2: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 3: V_i <-- X */ blkcpy(&V[i * (32 * r)], X, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(X, Y, Z, r); /* 3: V_i <-- X */ blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(Y, X, Z, r); } /* 6: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 7: j <-- Integerify(X) mod N */ j = integerify(X, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(X, &V[j * (32 * r)], 128 * r); blockmix_salsa8(X, Y, Z, r); /* 7: j <-- Integerify(X) mod N */ j = integerify(Y, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(Y, &V[j * (32 * r)], 128 * r); blockmix_salsa8(Y, X, Z, r); } /* 10: B' <-- X */ for (k = 0; k < 32 * r; k++) le32enc(&B[4 * k], X[k]); } tarsnap-1.0.41/lib/crypto/crypto_scrypt_smix.h000066400000000000000000000010751476705112600215040ustar00rootroot00000000000000#ifndef CRYPTO_SCRYPT_SMIX_H_ #define CRYPTO_SCRYPT_SMIX_H_ #include #include /** * crypto_scrypt_smix(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. */ void crypto_scrypt_smix(uint8_t *, size_t, uint64_t, void *, void *); #endif /* !CRYPTO_SCRYPT_SMIX_H_ */ tarsnap-1.0.41/lib/crypto/crypto_scrypt_smix_sse2.c000066400000000000000000000157711476705112600224430ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "cpusupport.h" #ifdef CPUSUPPORT_X86_SSE2 #include #include #include "sysendian.h" #include "crypto_scrypt_smix_sse2.h" static void blkcpy(__m128i *, const __m128i *, size_t); static void blkxor(__m128i *, const __m128i *, size_t); static void salsa20_8(__m128i[4]); static void blockmix_salsa8(const __m128i *, __m128i *, __m128i *, size_t); static uint64_t integerify(const __m128i *, size_t); static void blkcpy(__m128i * dest, const __m128i * src, size_t len) { size_t L = len / 16; size_t i; for (i = 0; i < L; i++) dest[i] = src[i]; } static void blkxor(__m128i * dest, const __m128i * src, size_t len) { size_t L = len / 16; size_t i; for (i = 0; i < L; i++) dest[i] = _mm_xor_si128(dest[i], src[i]); } /** * salsa20_8(B): * Apply the salsa20/8 core to the provided block. */ static void salsa20_8(__m128i B[4]) { __m128i X0, X1, X2, X3; __m128i T; size_t i; X0 = B[0]; X1 = B[1]; X2 = B[2]; X3 = B[3]; for (i = 0; i < 8; i += 2) { /* Operate on "columns". */ T = _mm_add_epi32(X0, X3); X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); T = _mm_add_epi32(X1, X0); X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); T = _mm_add_epi32(X2, X1); X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13)); X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); T = _mm_add_epi32(X3, X2); X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); /* Rearrange data. */ X1 = _mm_shuffle_epi32(X1, 0x93); X2 = _mm_shuffle_epi32(X2, 0x4E); X3 = _mm_shuffle_epi32(X3, 0x39); /* Operate on "rows". */ T = _mm_add_epi32(X0, X1); X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7)); X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); T = _mm_add_epi32(X3, X0); X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); T = _mm_add_epi32(X2, X3); X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13)); X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); T = _mm_add_epi32(X1, X2); X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); /* Rearrange data. */ X1 = _mm_shuffle_epi32(X1, 0x39); X2 = _mm_shuffle_epi32(X2, 0x4E); X3 = _mm_shuffle_epi32(X3, 0x93); } B[0] = _mm_add_epi32(B[0], X0); B[1] = _mm_add_epi32(B[1], X1); B[2] = _mm_add_epi32(B[2], X2); B[3] = _mm_add_epi32(B[3], X3); } /** * blockmix_salsa8(Bin, Bout, X, r): * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r * bytes in length; the output Bout must also be the same size. The * temporary space X must be 64 bytes. */ static void blockmix_salsa8(const __m128i * Bin, __m128i * Bout, __m128i * X, size_t r) { size_t i; /* 1: X <-- B_{2r - 1} */ blkcpy(X, &Bin[8 * r - 4], 64); /* 2: for i = 0 to 2r - 1 do */ for (i = 0; i < r; i++) { /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 8], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 4], X, 64); /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 8 + 4], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[(r + i) * 4], X, 64); } } /** * integerify(B, r): * Return the result of parsing B_{2r-1} as a little-endian integer. * Note that B's layout is permuted compared to the generic implementation. */ static uint64_t integerify(const __m128i * B, size_t r) { const __m128i * X = B + (2*r - 1) * 4; uint32_t X0, X13; /* Get the first 32-bit element in X[0]. */ X0 = (uint32_t)_mm_cvtsi128_si32(X[0]); /* Get the second 32-bit element in X[3]. */ X13 = (uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(X[3], 4)); return (((uint64_t)(X13) << 32) + X0); } /** * crypto_scrypt_smix_sse2(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. * * Use SSE2 instructions. */ void crypto_scrypt_smix_sse2(uint8_t * B, size_t r, uint64_t N, void * V, void * XY) { __m128i * X = XY; __m128i * Y = (void *)((uintptr_t)(XY) + 128 * r); __m128i * Z = (void *)((uintptr_t)(XY) + 256 * r); uint32_t * X32 = (void *)X; uint64_t i, j; size_t k; /* 1: X <-- B */ for (k = 0; k < 2 * r; k++) { for (i = 0; i < 16; i++) { X32[k * 16 + i] = le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]); } } /* 2: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 3: V_i <-- X */ blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(X, Y, Z, r); /* 3: V_i <-- X */ blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r), Y, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(Y, X, Z, r); } /* 6: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 7: j <-- Integerify(X) mod N */ j = integerify(X, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); blockmix_salsa8(X, Y, Z, r); /* 7: j <-- Integerify(X) mod N */ j = integerify(Y, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); blockmix_salsa8(Y, X, Z, r); } /* 10: B' <-- X */ for (k = 0; k < 2 * r; k++) { for (i = 0; i < 16; i++) { le32enc(&B[(k * 16 + (i * 5 % 16)) * 4], X32[k * 16 + i]); } } } #endif /* CPUSUPPORT_X86_SSE2 */ tarsnap-1.0.41/lib/crypto/crypto_scrypt_smix_sse2.h000066400000000000000000000011631476705112600224360ustar00rootroot00000000000000#ifndef CRYPTO_SCRYPT_SMIX_SSE2_H_ #define CRYPTO_SCRYPT_SMIX_SSE2_H_ #include #include /** * crypto_scrypt_smix_sse2(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. * * Use SSE2 instructions. */ void crypto_scrypt_smix_sse2(uint8_t *, size_t, uint64_t, void *, void *); #endif /* !CRYPTO_SCRYPT_SMIX_SSE2_H_ */ tarsnap-1.0.41/lib/crypto/crypto_session.c000066400000000000000000000114751476705112600206030ustar00rootroot00000000000000#include #include #include #include "crypto_aes.h" #include "crypto_aesctr.h" #include "crypto_dh.h" #include "crypto_verify_bytes.h" #include "sysendian.h" #include "crypto_internal.h" #include "crypto.h" struct crypto_session_internal { struct crypto_aesctr * encr_write_stream; struct crypto_aes_key * encr_write; uint8_t auth_write[32]; uint64_t auth_write_nonce; struct crypto_aesctr * encr_read_stream; struct crypto_aes_key * encr_read; uint8_t auth_read[32]; uint64_t auth_read_nonce; }; /** * crypto_session_init(pub, priv, nonce, mkey, encr_write, auth_write, * encr_read, auth_read): * Compute K = ${pub}^(2^258 + ${priv}), mkey = MGF1(nonce || K, 48), and * return a CRYPTO_SESSION with encryption and authentication write and read * keys constructed from HMAC(mkey, (encr|auth)_(write|read)). */ CRYPTO_SESSION * crypto_session_init(uint8_t pub[CRYPTO_DH_PUBLEN], uint8_t priv[CRYPTO_DH_PRIVLEN], uint8_t nonce[32], uint8_t mkey[48], const char * encr_write, const char * auth_write, const char * encr_read, const char * auth_read) { struct crypto_session_internal * CS; uint8_t K[CRYPTO_DH_PUBLEN]; uint8_t MGFbuf[32 + CRYPTO_DH_PUBLEN]; uint8_t aes_write[32]; uint8_t aes_read[32]; /* Compute K = 2^(xy) mod p. */ if (crypto_dh_compute(pub, priv, K)) goto err0; /* Shared key is MGF1(nonce || K, 48). */ memcpy(MGFbuf, nonce, 32); memcpy(MGFbuf + 32, K, CRYPTO_DH_PUBLEN); crypto_MGF1(MGFbuf, 32 + CRYPTO_DH_PUBLEN, mkey, 48); /* Allocate space for session key structure. */ if ((CS = malloc(sizeof(struct crypto_session_internal))) == NULL) goto err0; /* Generate raw keys. */ crypto_hash_data_key(mkey, 48, (const uint8_t *)encr_write, strlen(encr_write), aes_write); crypto_hash_data_key(mkey, 48, (const uint8_t *)auth_write, strlen(auth_write), CS->auth_write); crypto_hash_data_key(mkey, 48, (const uint8_t *)encr_read, strlen(encr_read), aes_read); crypto_hash_data_key(mkey, 48, (const uint8_t *)auth_read, strlen(auth_read), CS->auth_read); /* Expand AES keys and set up streams. */ if ((CS->encr_write = crypto_aes_key_expand(aes_write, 32)) == NULL) goto err1; if ((CS->encr_read = crypto_aes_key_expand(aes_read, 32)) == NULL) goto err2; if ((CS->encr_write_stream = crypto_aesctr_init(CS->encr_write, 0)) == NULL) goto err3; if ((CS->encr_read_stream = crypto_aesctr_init(CS->encr_read, 0)) == NULL) goto err4; /* Initialize parameters. */ CS->auth_write_nonce = CS->auth_read_nonce = 0; /* Success! */ return (CS); err4: crypto_aesctr_free(CS->encr_write_stream); err3: crypto_aes_key_free(CS->encr_read); err2: crypto_aes_key_free(CS->encr_write); err1: free(CS); err0: /* Failure! */ return (NULL); } /** * crypto_session_encrypt(CS, inbuf, outbuf, buflen): * Encrypt ${inbuf} with the session write key and write ciphertext to * ${outbuf}. */ void crypto_session_encrypt(CRYPTO_SESSION * CS, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { crypto_aesctr_stream(CS->encr_write_stream, inbuf, outbuf, buflen); } /** * crypto_session_decrypt(CS, inbuf, outbuf, buflen): * Decrypt ${inbuf} with the session read key and write plaintext to ${outbuf}. */ void crypto_session_decrypt(CRYPTO_SESSION * CS, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { crypto_aesctr_stream(CS->encr_read_stream, inbuf, outbuf, buflen); } /** * crypto_session_sign(CS, buf, buflen, sig): * Generate sig = write_auth(buf). */ void crypto_session_sign(CRYPTO_SESSION * CS, const uint8_t * buf, size_t buflen, uint8_t sig[32]) { uint8_t nonce[8]; /* Convert nonce to 8-byte big-endian format, and increment. */ be64enc(nonce, CS->auth_write_nonce); CS->auth_write_nonce += 1; /* Generate hash. */ crypto_hash_data_key_2(CS->auth_write, 32, nonce, 8, buf, buflen, sig); } /** * crypto_session_verify(CS, buf, buflen, sig): * Verify that sig = read_auth(buf). Return non-zero if the signature * does not match. */ int crypto_session_verify(CRYPTO_SESSION * CS, const uint8_t * buf, size_t buflen, const uint8_t sig[32]) { uint8_t nonce[8]; uint8_t sig_actual[32]; /* Convert nonce to 8-byte big-endian format, and increment. */ be64enc(nonce, CS->auth_read_nonce); CS->auth_read_nonce += 1; /* Generate hash. */ crypto_hash_data_key_2(CS->auth_read, 32, nonce, 8, buf, buflen, sig_actual); /* Determine if the signatures match. */ if (crypto_verify_bytes(sig, sig_actual, 32)) return (1); else return (0); } /** * crypto_session_free(CS): * Free a CRYPTO_SESSION structure. */ void crypto_session_free(CRYPTO_SESSION * CS) { /* Behave consistently with free(NULL). */ if (CS == NULL) return; crypto_aesctr_free(CS->encr_write_stream); crypto_aesctr_free(CS->encr_read_stream); crypto_aes_key_free(CS->encr_write); crypto_aes_key_free(CS->encr_read); free(CS); } tarsnap-1.0.41/lib/datastruct/000077500000000000000000000000001476705112600162025ustar00rootroot00000000000000tarsnap-1.0.41/lib/datastruct/patricia.c000066400000000000000000000323401476705112600201440ustar00rootroot00000000000000#include /* size_t */ #include /* uint8_t */ #include /* malloc, free */ #include /* memcpy */ #include "patricia.h" /** * Our Patricia tree structure can be thought of as operating on strings of * 9-bit bytes, where 0x00 -- 0xFF are mapped to 0x100 -- 0x1FF and 0x00 * represents the end-of-string character (note that NUL can occur inside * keys). The field (struct pnode).mask is either 0 or a power of 2; if 0, * the left child, if non-NULL, is a pointer to the record associated with * the key thus far. For example, the strings "hello", "hello colin", * "hello world", and "wednesday", each associated with pointers to * themselves, are stored in the following tree: * * [0x10, 0x60, 0, ""] * | | * [0x00, 0x00, 5, "hello"] [0x00, 0x00, 9, "wednesday"] * | | | | * "hello" [0x10, 0x60, 1, " "] "wednesday" NULL * | | * [0x00, 0x00, 5, "colin"] [0x00, 0x00, 5, "world"] * | | | | * "hello colin" NULL "hello world" NULL * */ /* Structure used to store a Patricia tree node. */ struct pnode { struct pnode * left; /* Left child. */ struct pnode * right; /* Right child. */ uint8_t mask; /* Critical bit mask. */ uint8_t high; /* High bits of this node. */ uint8_t slen; /* Length of s[]. */ uint8_t s[]; /* Bytes since parent's s[]. */ }; /** * The use of a uint8_t to store the number of bytes in (struct pnode).s[] * means that we cannot store more than 255 bytes in each node; since many * memory allocators can perform allocations of 256 bytes far more * efficiently than they can perform allocations of slightly more than 256 * bytes, we artificially limit the number of bytes stored in each node so * that a node is never larger than 256 bytes in total. * * In some applications it may be preferable to eliminate variable-length * memory allocations entirely and provide the same size of memory allocation * to each node; if this is done, it would almost certainly be desirable to * reduce MAXSLEN further, e.g., to (2 * sizeof(void *) - 3) so that each * node would be of size (4 * sizeof(void *)). */ /* Maximum number of key bytes stored in a node. */ #ifndef MAXSLEN #define MAXSLEN (256 - sizeof(struct pnode)) #endif /* * Structure used to store Patricia tree. The maximum key length is stored * in order to simplify buffer handling in the tree traversal code. */ struct patricia_internal { struct pnode * root; /* Root node of tree. */ size_t maxkey; /* Longest key length. */ }; static struct pnode * node_alloc(uint8_t, const uint8_t *); static struct pnode * node_dup(const struct pnode *, uint8_t, const uint8_t *); static int compare(const struct pnode *, const uint8_t *, size_t, uint8_t *, uint8_t *); static int foreach_internal(struct pnode *, int(void *, uint8_t *, size_t, void *), void *, uint8_t *, size_t); static void free_internal(struct pnode *); /* * Create a node with no children, mask = high = 0, and the provided slen * and s[]. */ static struct pnode * node_alloc(uint8_t slen, const uint8_t * s) { struct pnode * n; /* Allocate. */ if ((n = malloc(sizeof(struct pnode) + slen)) == NULL) return (NULL); /* No children, mask, or high bits. */ n->left = n->right = NULL; n->mask = n->high = 0; /* Insert provided bytes of key. */ n->slen = slen; memcpy(n->s, s, slen); /* Success! */ return (n); } /* * Create a duplicate of a node but with different slen and s[]. */ static struct pnode * node_dup(const struct pnode * n0, uint8_t slen, const uint8_t * s) { struct pnode * n; /* Allocate. */ if ((n = malloc(sizeof(struct pnode) + slen)) == NULL) return (NULL); /* Copy children, mask, and high bits. */ n->left = n0->left; n->right = n0->right; n->mask = n0->mask; n->high = n0->high; /* Insert provided bytes of key. */ n->slen = slen; memcpy(n->s, s, slen); /* Success! */ return (n); } /* * Compare the given key to the given node. If they match (i.e., the node is * a prefix of the key), return zero; otherwise, return non-zero and set the * values mlen and mask to the number of matching bytes and the bitmask where * there is a mismatch (where mask == 0 means that the key is a prefix of the * node). */ static int compare(const struct pnode * n, const uint8_t * key, size_t keylen, uint8_t * mlen, uint8_t * mask) { uint8_t i; uint8_t mm; /* Scan through the complete bytes in the node. */ for (i = 0; i < n->slen; i++) { /* Is the key a prefix of the node? */ if (keylen == i) { *mlen = i; *mask = 0; return (1); } /* Compute how the bytes differ. */ mm = n->s[i] ^ key[i]; /* If the ith bytes match, move on to the next position. */ if (mm == 0) continue; /* Figure out which bit they mismatch at. */ for (*mask = 0x80; *mask != 0; *mask >>= 1) { if (mm & *mask) break; } /* There are i matching bytes. */ *mlen = i; /* The key doesn't match the node. */ return (1); } /* If the node splits on the 9th bit, it is a prefix of the key. */ if (n->mask == 0) return (0); /* Otherwise, consider the high bits stored in the node. */ /* Is the key a prefix of the node? */ if (keylen == n->slen) { *mlen = n->slen; *mask = 0; return (1); } /* Compute how the top bits differ. */ mm = (n->high ^ key[i]) & ((- n->mask) << 1); /* If the top bits match, the node is a prefix of the key. */ if (mm == 0) return (0); /* Figure out which bit they mismatch at. */ for (*mask = 0x80; *mask != 0; *mask >>= 1) { if (mm & *mask) break; } /* There are n->slen matching bytes. */ *mlen = n->slen; /* The key doesn't match this node. */ return (1); } /* * Recursively call func(cookie, key, keylen, rec) on all records under the * node n; the first keypos bytes of keybuf hold the key prefix generated * from ancestor nodes. */ static int foreach_internal(struct pnode * n, int func(void *, uint8_t *, size_t, void *), void * cookie, uint8_t * keybuf, size_t keypos) { int rc = 0; /* Add bytes to the key buffer. */ memcpy(keybuf + keypos, n->s, n->slen); keypos += n->slen; /* Left child. */ if (n->left != NULL) { if (n->mask == 0) { rc = func(cookie, keybuf, keypos, n->left); } else { rc = foreach_internal(n->left, func, cookie, keybuf, keypos); } } /* Return non-zero status if necessary. */ if (rc) return (rc); /* Right child. */ if (n->right != NULL) rc = foreach_internal(n->right, func, cookie, keybuf, keypos); /* Return status. */ return (rc); } /* * Recursively free the tree. */ static void free_internal(struct pnode * n) { /* Left child. */ if ((n->mask != 0) && (n->left != NULL)) free_internal(n->left); /* Right child. */ if (n->right != NULL) free_internal(n->right); /* Free this node. */ free(n); } /** * patricia_init(void): * Create a Patricia tree to be used for mapping arbitrary-length keys to * records. Return NULL on failure. */ PATRICIA * patricia_init(void) { PATRICIA * P; /* Allocate memory, or return failure. */ if ((P = malloc(sizeof(PATRICIA))) == NULL) return (NULL); /* All keys so far have zero length, and we have no nodes. */ P->root = NULL; P->maxkey = 0; /* Success! */ return (P); } /** * patricia_insert(tree, key, keylen, rec): * Associate the provided ${key} of length ${keylen} bytes with the pointer * ${rec}, which must be non-NULL. Return (-1) on error, 0 on success, and 1 * if the key already exists. */ int patricia_insert(PATRICIA * P, const uint8_t * key, size_t keylen, void * rec) { struct pnode ** np = &P->root; struct pnode * pnew; struct pnode * pnew2; size_t slen; uint8_t mlen; uint8_t mask; /* Update maximum key length. */ if (P->maxkey < keylen) P->maxkey = keylen; /* * To understand this code, first read the code for patricia_lookup. * This follows essentially the same approach, except that we keep * an extra level of indirection so that we can insert a new node * into the tree _above_ the node which we are considering at any * particular point. */ do { /* Have we fallen off the bottom of the tree? */ if (*np == NULL) { /* * Create a new node with up to MAXSLEN bytes of the * key, and add it at the current point. Then keep * on going (and move down into the newly added node). */ /* Figure out how much key goes into this node. */ slen = keylen; if (slen > MAXSLEN) slen = MAXSLEN; /* Create the node or error out. */ if ((pnew = node_alloc((uint8_t)slen, key)) == NULL) return (-1); /* Add the new node into the tree. */ *np = pnew; } /* Is the node not a prefix of the key? */ if (compare(*np, key, keylen, &mlen, &mask)) { /* * Split the node *np after mlen bytes and a number * of bits based on mask. Leave *np pointing to the * upper of the two nodes (because we will continue * by traversing into the so-far-nonexistent child * of the new node). */ /* Create the lower of the new nodes. */ slen = (*np)->slen - mlen; pnew2 = node_dup(*np, (uint8_t)slen, (*np)->s + mlen); if (pnew2 == NULL) return (-1); /* Create the upper of the new nodes. */ if ((pnew = node_alloc(mlen, key)) == NULL) { free(pnew2); return (-1); } pnew->mask = mask; /* Handle splitting on bit 9 differently. */ if (mask == 0) { pnew->high = 0; pnew->right = pnew2; } else { pnew->high = key[mlen] & ((- mask) << 1); /* * This looks wrong, but it actually works: * mask is the bit where key[mlen] and * (*np)->s[mlen] differ, so if key[mlen] * has a 1 bit, (*np)->s[mlen] has a 0 bit * and belongs on the left (and vice versa). */ if (key[mlen] & mask) pnew->left = pnew2; else pnew->right = pnew2; } /* Free the node which we are replacing. */ free(*np); /* Reattach this branch to the tree. */ *np = pnew; } /* Strip off the matching part of the key. */ key += (*np)->slen; keylen -= (*np)->slen; /* Handle splitting on the 9th bit specially. */ if ((*np)->mask == 0) { /* Have we found the key? */ if (keylen == 0) { /* Add the record or return 1. */ if ((*np)->left == NULL) { (*np)->left = rec; return (0); } else { return (1); } } /* The key continues; traverse to right child. */ np = &(*np)->right; continue; } /* Take left or right child depending upon critical bit. */ if (key[0] & (*np)->mask) np = &(*np)->right; else np = &(*np)->left; } while (1); } /** * patricia_lookup(tree, key, keylen): * Look up the provided ${key} of length ${keylen} bytes. Return a pointer to * the associated _record pointer_ if the key is present in the tree (this can * be used to change the record pointer associated with the key); or NULL * otherwise. * * Note that a record can be deleted from a Patricia tree as follows: * void ** recp = patricia_lookup(tree, key, keylen); * if (recp != NULL) * *recp = NULL; * but this does not reduce the memory used by the tree as one might expect * from reducing its size. */ void ** patricia_lookup(PATRICIA * P, const uint8_t * key, size_t keylen) { struct pnode * n = P->root; uint8_t t0, t1; /* Garbage variables. */ /* Traverse the tree until we find the key or give up. */ do { /* Have we fallen off the bottom of the tree? */ if (n == NULL) return (NULL); /* Is the node not a prefix of the key? */ if (compare(n, key, keylen, &t0, &t1)) return (NULL); /* Strip off the matching part of the key. */ key += n->slen; keylen -= n->slen; /* Handle splitting on the 9th bit specially. */ if (n->mask == 0) { /* Have we found the key? */ if (keylen == 0) { /* Is there a record here? */ if (n->left != NULL) return ((void **)(void *)&n->left); else return (NULL); } /* The key continues; traverse to right child. */ n = n->right; continue; } /* Take left or right child depending upon critical bit. */ if (key[0] & n->mask) n = n->right; else n = n->left; } while (1); } /** * patricia_foreach(tree, func, cookie): * Traverse the ${tree} in lexicographical order of stored keys, and call * ${func(cookie, key, keylen, rec)} for each (key, record) pair. Stop the * traversal early if ${func} returns a non-zero value; return zero, the * non-zero value returned by ${func}, or (-1) if an error occurs in the * tree traversal. */ int patricia_foreach(PATRICIA * P, int func(void *, uint8_t *, size_t, void *), void * cookie) { uint8_t * keybuf; int rc; /* Allocate buffer to store keys generated during traversal. */ keybuf = malloc(P->maxkey); if ((keybuf == NULL) && (P->maxkey > 0)) return (-1); /* Call a recursive function to do all the work. */ if (P->root != NULL) rc = foreach_internal(P->root, func, cookie, keybuf, 0); else rc = 0; /* Free temporary buffer. */ free(keybuf); /* Return status from func calls. */ return (rc); } /** * patricia_free(tree): * Free the ${tree}. */ void patricia_free(PATRICIA * P) { /* Behave consistently with free(NULL). */ if (P == NULL) return; /* Call a recursive function to free all the nodes. */ if (P->root != NULL) free_internal(P->root); /* Free the tree structure. */ free(P); } tarsnap-1.0.41/lib/datastruct/patricia.h000066400000000000000000000035211476705112600201500ustar00rootroot00000000000000#ifndef PATRICIA_H_ #define PATRICIA_H_ #include /* size_t */ #include /* uint8_t */ /** * Structure used to store Patricia tree. */ typedef struct patricia_internal PATRICIA; /** * patricia_init(void): * Create a Patricia tree to be used for mapping arbitrary-length keys to * records. Return NULL on failure. */ PATRICIA * patricia_init(void); /** * patricia_insert(tree, key, keylen, rec): * Associate the provided ${key} of length ${keylen} bytes with the pointer * ${rec}, which must be non-NULL. Return (-1) on error, 0 on success, and 1 * if the key already exists. */ int patricia_insert(PATRICIA *, const uint8_t *, size_t, void *); /** * patricia_lookup(tree, key, keylen): * Look up the provided ${key} of length ${keylen} bytes. Return a pointer to * the associated _record pointer_ if the key is present in the tree (this can * be used to change the record pointer associated with the key); or NULL * otherwise. * * Note that a record can be deleted from a Patricia tree as follows: * void ** recp = patricia_lookup(tree, key, keylen); * if (recp != NULL) * *recp = NULL; * but this does not reduce the memory used by the tree as one might expect * from reducing its size. */ void ** patricia_lookup(PATRICIA *, const uint8_t *, size_t); /** * patricia_foreach(tree, func, cookie): * Traverse the ${tree} in lexicographical order of stored keys, and call * ${func(cookie, key, keylen, rec)} for each (key, record) pair. Stop the * traversal early if ${func} returns a non-zero value; return zero, the * non-zero value returned by ${func}, or (-1) if an error occurs in the * tree traversal. */ int patricia_foreach(PATRICIA *, int(void *, uint8_t *, size_t, void *), void *); /** * patricia_free(tree): * Free the ${tree}. */ void patricia_free(PATRICIA *); #endif /* !PATRICIA_H_ */ tarsnap-1.0.41/lib/datastruct/rwhashtab.c000066400000000000000000000142261476705112600203360ustar00rootroot00000000000000#include #include #include #include #include "crypto_entropy.h" #include "ctassert.h" #include "sysendian.h" #include "warnp.h" #include "crypto.h" #include "rwhashtab.h" /* * We use le64dec to generate a size_t from an array of bytes; make sure * that this is enough. */ CTASSERT(sizeof(size_t) <= sizeof(uint64_t)); /** * Structure used to store RW hash table state. */ struct rwhashtab_internal { /* Hash table parameters */ size_t cursize; /* Size of table; must be a power of 2 */ size_t numentries; /* Number of entries, <= 0.75 * cursize */ void ** ht; /* Table of cursize pointers to records */ /* Where to find keys within records */ size_t keyoffset; size_t keylength; /* Used for hashing */ uint8_t randbuf[32]; /* Prefix used for hashing */ }; static int rwhashtab_enlarge(RWHASHTAB * H); static size_t rwhashtab_search(RWHASHTAB * H, const uint8_t * key); /* Enlarge the table by a factor of 2, and rehash. */ static int rwhashtab_enlarge(RWHASHTAB * H) { void ** htnew, ** htold; void * rec; size_t newsize, oldsize; size_t htposold, htposnew; /* * Compute new size, and make sure the new table size in bytes * doesn't overflow. */ newsize = H->cursize * 2; if (newsize > SIZE_MAX / sizeof(void *)) { errno = ENOMEM; return (-1); } /* Allocate and zero the new space. */ if ((htnew = malloc(newsize * sizeof(void *))) == NULL) return (-1); for (htposnew = 0; htposnew < newsize; htposnew++) htnew[htposnew] = NULL; /* Attach new space to hash table. */ htold = H->ht; H->ht = htnew; oldsize = H->cursize; H->cursize = newsize; /* Rehash into new table. */ for (htposold = 0; htposold < oldsize; htposold++) { rec = htold[htposold]; if (rec != NULL) { htposnew = rwhashtab_search(H, (uint8_t *)rec + H->keyoffset); H->ht[htposnew] = rec; } } /* Free now-unused memory. */ free(htold); /* Success! */ return (0); } /* * Search for a record within ht[size], using hashing parameters from H. * Return the position of the record or of the first NULL found. */ static size_t rwhashtab_search(RWHASHTAB * H, const uint8_t * key) { size_t htpos; uint8_t hashbuf[32]; /* Compute the hash of the record key. */ if (crypto_hash_data_2(CRYPTO_KEY_HMAC_SHA256, H->randbuf, 32, key, H->keylength, hashbuf)) { warn0("Programmer error: " "SHA256 should never fail"); abort(); } /* Compute starting hash location. */ htpos = le64dec(hashbuf) & (H->cursize - 1); /* * Search. This is not an endless loop since the table isn't * allowed to be full. */ do { /* Is the space empty? */ if (H->ht[htpos] == NULL) return (htpos); /* Do we have the right key? */ if (memcmp((uint8_t *)(H->ht[htpos]) + H->keyoffset, key, H->keylength) == 0) return (htpos); /* Move to the next table entry. */ htpos = (htpos + 1) & (H->cursize - 1); } while (1); } /** * rwhashtab_init(keyoffset, keylength): * Create an empty hash ${table} for storing records which contain keys of * length ${keylength} bytes starting at offset ${keyoffset} from the start * of each record. Return NULL on failure. */ RWHASHTAB * rwhashtab_init(size_t keyoffset, size_t keylength) { RWHASHTAB * H; size_t i; /* Sanity check. */ if (keylength == 0) return (NULL); H = malloc(sizeof(*H)); if (H == NULL) return (NULL); H->cursize = 4; H->numentries = 0; H->keyoffset = keyoffset; H->keylength = keylength; /* Get some entropy for the keyed hash function. */ if (crypto_entropy_read(H->randbuf, 32)) { free(H); return (NULL); } /* Allocate space for pointers to records. */ H->ht = malloc(H->cursize * sizeof(void *)); if (H->ht == NULL) { free(H); return (NULL); } /* All of the entries are empty. */ for (i = 0; i < H->cursize; i++) H->ht[i] = NULL; return (H); } /** * rwhashtab_getsize(table): * Return the number of entries in the ${table}. */ size_t rwhashtab_getsize(RWHASHTAB * H) { /* Just extract the value and return it. */ return (H->numentries); } /** * rwhashtab_insert(table, record): * Insert the provided ${record} into the hash ${table}. Return (-1) on error, * 0 on success, and 1 if the table already contains a record with the same * key. */ int rwhashtab_insert(RWHASHTAB * H, void * rec) { int rc; size_t htpos; /* * Does the table need to be enlarged? Technically we should check * this after searching to see if the key is already in the table; * but then we'd need to search again to find the new insertion * location after enlarging, which would add unnecessary complexity. * Doing it this way just means that we might enlarge the table one * insert sooner than necessary. */ if (H->numentries >= H->cursize - (H->cursize >> 2)) { rc = rwhashtab_enlarge(H); if (rc) return (rc); } /* * Search for the record, to see if it is already present and/or * where it should be inserted. */ htpos = rwhashtab_search(H, (uint8_t *)rec + H->keyoffset); /* Already present? */ if (H->ht[htpos] != NULL) return (1); /* Insert the record. */ H->ht[htpos] = rec; H->numentries += 1; /* Success! */ return (0); } /** * rwhashtab_read(table, key): * Return a pointer to the record in the ${table} with the specified ${key}, or * NULL if no such record exists. */ void * rwhashtab_read(RWHASHTAB * H, const uint8_t * key) { size_t htpos; /* Search. */ htpos = rwhashtab_search(H, key); /* Return the record, or NULL if not present. */ return (H->ht[htpos]); } /** * rwhashtab_foreach(table, func, cookie): * Call ${func(record, cookie)} for each ${record} in the hash ${table}. Stop * the iteration early if ${func} returns a non-zero value; return 0 or the * non-zero value returned by ${func}. */ int rwhashtab_foreach(RWHASHTAB * H, int func(void *, void *), void * cookie) { size_t htpos; int rc; for (htpos = 0; htpos < H->cursize; htpos++) { if (H->ht[htpos] != NULL) { rc = func(H->ht[htpos], cookie); if (rc) return (rc); } } /* Success! */ return (0); } /** * rwhashtab_free(table): * Free the hash ${table}. */ void rwhashtab_free(RWHASHTAB * H) { /* Behave consistently with free(NULL). */ if (H == NULL) return; /* Free everything. */ free(H->ht); free(H); } tarsnap-1.0.41/lib/datastruct/rwhashtab.h000066400000000000000000000027611476705112600203440ustar00rootroot00000000000000#ifndef RWHASHTAB_H_ #define RWHASHTAB_H_ #include /* size_t */ #include /* uint8_t */ /** * Structure used to store RW hash table state. */ typedef struct rwhashtab_internal RWHASHTAB; /** * rwhashtab_init(keyoffset, keylength): * Create an empty hash ${table} for storing records which contain keys of * length ${keylength} bytes starting at offset ${keyoffset} from the start * of each record. Return NULL on failure. */ RWHASHTAB * rwhashtab_init(size_t, size_t); /** * rwhashtab_getsize(table): * Return the number of entries in the ${table}. */ size_t rwhashtab_getsize(RWHASHTAB *); /** * rwhashtab_insert(table, record): * Insert the provided ${record} into the hash ${table}. Return (-1) on error, * 0 on success, and 1 if the table already contains a record with the same * key. */ int rwhashtab_insert(RWHASHTAB *, void *); /** * rwhashtab_read(table, key): * Return a pointer to the record in the ${table} with the specified ${key}, or * NULL if no such record exists. */ void * rwhashtab_read(RWHASHTAB *, const uint8_t *); /** * rwhashtab_foreach(table, func, cookie): * Call ${func(record, cookie)} for each ${record} in the hash ${table}. Stop * the iteration early if ${func} returns a non-zero value; return 0 or the * non-zero value returned by ${func}. */ int rwhashtab_foreach(RWHASHTAB *, int(void *, void *), void *); /** * rwhashtab_free(table): * Free the hash ${table}. */ void rwhashtab_free(RWHASHTAB *); #endif /* !RWHASHTAB_H_ */ tarsnap-1.0.41/lib/keyfile/000077500000000000000000000000001476705112600154545ustar00rootroot00000000000000tarsnap-1.0.41/lib/keyfile/keyfile.c000066400000000000000000000410601476705112600172510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "asprintf.h" #include "b64encode.h" #include "crypto.h" #include "insecure_memzero.h" #include "passphrase_entry.h" #include "readpass.h" #include "scryptenc.h" #include "sysendian.h" #include "warnp.h" #include "keyfile.h" /** * Key file format: * keyfile == (rawkeyfile | textkeyfile) * textkeyfile == line* * line == blankline | commentline | base64line * blankline == EOL * commentline == "#" char* EOL * base64line == [a-zA-Z0-9+/=]+ EOL * EOL = "\n" | "\r" | "\r\n" * * After base-64 decoding, a base64line becomes a rawline. * rawline == rawlinedata rawlinechecksum * rawlinedata == byte+ * rawlinechecksum == byte{6} * where rawlinechecksum is the first 6 bytes of SHA256(rawlinedata). * * After ignoring any blanklines and commentlines, converting base64lines to * rawlinedatas, and concatenating them together, a textkeyfile becomes a * tarsnapkeyfile. * * tarsnapkeyfile == scryptkeyfile | cookedkeyfile * scryptkeyfile == scrypt(cookedkeyfile) * cookedkeyfile == "tarsnap\0" rawkeyfile * rawkeyfile == machinenum keys * machinenum == big-endian-uint64_t * and keys are in the format used by crypto_keys_(im|ex)port. * * Put simply, there are three key formats: * 1. A raw key file (for historical reasons only). * 2. A base64-encoded key file. * 3. A base64-encoded encrypted key file. */ static int read_raw(const uint8_t *, size_t, uint64_t *, const char *, int); static int read_plaintext(const uint8_t *, size_t, uint64_t *, const char *, int); static int read_encrypted(const uint8_t *, size_t, uint64_t *, const char *, int, int, enum passphrase_entry, const char *); static int read_base256(const uint8_t *, size_t, uint64_t *, const char *, int, int, enum passphrase_entry, const char *); static int read_base64(const char *, size_t, uint64_t *, const char *, int, int, enum passphrase_entry, const char *); static int read_raw(const uint8_t * keybuf, size_t keylen, uint64_t * machinenum, const char * filename, int keys) { /* Sanity-check size. */ if (keylen < 8) { warn0("Key file is corrupt or truncated: %s", filename); goto err0; } /* Parse machine number from the first 8 bytes. */ *machinenum = be64dec(keybuf); /* Parse keys from the remaining buffer. */ if (crypto_keys_import(&keybuf[8], keylen - 8, keys)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } static int read_plaintext(const uint8_t * keybuf, size_t keylen, uint64_t * machinenum, const char * filename, int keys) { /* Sanity-check size. */ if (keylen < 8) { warn0("Key file is corrupt or truncated: %s", filename); goto err0; } /* Plaintext key files start with "tarsnap\0". */ if (memcmp(keybuf, "tarsnap\0", 8)) { warn0("Key file is corrupt: %s", filename); goto err0; } /* The rest of the buffer is raw key data. */ return (read_raw(&keybuf[8], keylen - 8, machinenum, filename, keys)); err0: /* Failure! */ return (-1); } static int read_encrypted(const uint8_t * keybuf, size_t keylen, uint64_t * machinenum, const char * filename, int keys, int force, enum passphrase_entry passphrase_entry, const char * passphrase_arg) { char * pwprompt; char * passwd; uint8_t * deckeybuf; size_t deckeylen; int rc; struct scryptenc_params params = {0, 0.5, 86400.0, 0, 0, 0}; /* The caller must pass a file name to be read. */ assert(filename != NULL); /* Sanity-check size. */ if (keylen == 0) { warn0("Key file is corrupt or truncated: %s", filename); goto err0; } /* Prompt the user for a password. */ if (asprintf(&pwprompt, "Please enter passphrase for keyfile %s", filename) == -1) goto err0; if (passphrase_entry_readpass(&passwd, passphrase_entry, passphrase_arg, pwprompt, NULL, 1)) { warnp("Error reading passphrase"); goto err1; } /* * Allocate a buffer to hold the decrypted key. At the present time * (2009-06-01) this buffer only needs to be keylen-128 bytes long, * since the only encrypted format we support has 128B of overhead; * but in the future the scrypt code might support other formats with * less overhead (but never less than zero bytes). */ if ((deckeybuf = malloc(keylen)) == NULL) goto err2; /* Decrypt the key file. */ rc = scryptdec_buf(keybuf, keylen, deckeybuf, &deckeylen, (const uint8_t *)passwd, strlen(passwd), ¶ms, 0, force); if (rc != SCRYPT_OK) { switch (rc) { case SCRYPT_ELIMIT: warnp("Error determining amount of available memory"); break; case SCRYPT_ECLOCK: warnp("Error reading clocks"); break; case SCRYPT_EKEY: warnp("Error computing derived key"); break; case SCRYPT_EOPENSSL: warnp("OpenSSL error"); break; case SCRYPT_ENOMEM: /* malloc failure */ break; case SCRYPT_EINVAL: warn0("Input is not valid scrypt-encrypted block"); break; case SCRYPT_EVERSION: warn0("Unrecognized scrypt format version"); break; case SCRYPT_ETOOBIG: warn0("Decrypting file would require too much memory"); break; case SCRYPT_ETOOSLOW: warn0("Decrypting file would take too much CPU time"); break; case SCRYPT_EBIGSLOW: warn0("Decrypting file would take too much CPU time and memory"); break; case SCRYPT_EPASS: warn0("Passphrase is incorrect"); break; default: warn0("Programmer error: " "Impossible error returned by scryptdec_buf"); break; } warnp("Error decrypting key file: %s", filename); goto err3; } /* * Don't need this any more. To simplify error handling, we zero * this here but free it later. */ insecure_memzero(passwd, strlen(passwd)); /* Process the decrypted key file. */ if (read_plaintext(deckeybuf, deckeylen, machinenum, filename, keys)) goto err3; /* Clean up. We only used the first deckeylen values of deckeybuf. */ insecure_memzero(deckeybuf, deckeylen); free(deckeybuf); free(passwd); free(pwprompt); /* Success! */ return (0); err3: /* * Depending on the error, we might not know how much data was written * to deckeybuf, so we play safe by zeroing the entire allocated array. */ insecure_memzero(deckeybuf, keylen); free(deckeybuf); err2: insecure_memzero(passwd, strlen(passwd)); free(passwd); err1: free(pwprompt); err0: /* Failure! */ return (-1); } static int read_base256(const uint8_t * keybuf, size_t keylen, uint64_t * machinenum, const char * filename, int keys, int force, enum passphrase_entry passphrase_entry, const char * passphrase_arg) { /* Sanity-check size. */ if (keylen < 6) { warn0("Key file is corrupt or truncated: %s", filename); goto err0; } /* Is this encrypted? */ if (memcmp(keybuf, "scrypt", 6) == 0) return (read_encrypted(keybuf, keylen, machinenum, filename, keys, force, passphrase_entry, passphrase_arg)); /* Parse this as a plaintext keyfile. */ return (read_plaintext(keybuf, keylen, machinenum, filename, keys)); err0: /* Failure! */ return (-1); } static int read_base64(const char * keybuf, size_t keylen, uint64_t * machinenum, const char * filename, int keys, int force, enum passphrase_entry passphrase_entry, const char * passphrase_arg) { uint8_t * decbuf; size_t decbuflen; size_t decpos; size_t lnum; size_t llen; size_t len; uint8_t hbuf[32]; /* Sanity-check size. */ if (keylen < 4) { warn0("Key file is corrupt or truncated: %s", filename); goto err0; } /* * Allocate space for base64-decoded bytes. The most space we can * possibly require for the decoded bytes is 3/4 of the base64 * encoded length. */ decbuflen = (keylen / 4) * 3; if ((decbuf = malloc(decbuflen)) == NULL) goto err0; decpos = 0; /* Handle one line at once. */ for (lnum = 1; keylen > 0; lnum++) { /* Look for an EOL character. */ for (llen = 0; llen < keylen; llen++) { if ((keybuf[llen] == '\r') || (keybuf[llen] == '\n')) break; } /* If this isn't a comment or blank line, base-64 decode it. */ if ((llen > 0) && (keybuf[0] != '#')) { if (b64decode(keybuf, llen, &decbuf[decpos], &len)) goto err2; /* We should have at least 7 bytes... */ if (len < 7) goto err2; /* ... because SHA256(line - last 6 bytes)... */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, &decbuf[decpos], len - 6, hbuf)) { warn0("Programmer error: " "SHA256 should never fail"); goto err1; } /* ... should equal the last 6 bytes of the line. */ if (memcmp(hbuf, &decbuf[decpos + len - 6], 6)) goto err2; /* This line is good; advance the pointer. */ decpos += len - 6; } /* Skip past this line. */ keybuf += llen; keylen -= llen; /* Skip past the EOL if we're not at EOF. */ if ((keylen > 1) && (keybuf[0] == '\r') && (keybuf[1] == '\n')) { keybuf += 2; keylen -= 2; } else if (keylen) { keybuf += 1; keylen -= 1; } } /* Process the decoded key file. */ if (read_base256(decbuf, decpos, machinenum, filename, keys, force, passphrase_entry, passphrase_arg)) goto err1; /* Zero and free memory. */ insecure_memzero(decbuf, decbuflen); free(decbuf); /* Success! */ return (0); err2: warn0("Key file is corrupt on line %zu: %s", lnum, filename); err1: insecure_memzero(decbuf, decbuflen); free(decbuf); err0: /* Failure! */ return (-1); } /** * keyfile_read(filename, machinenum, keys, force, passphrase_entry, * passphrase_arg): * Read keys from a tarsnap key file; and return the machine # via the * provided pointer. Ignore any keys not specified in the ${keys} mask. * If ${force} is 1, do not check whether decryption will exceed * the estimated available memory or time. Use the ${passphrase_entry} * method to read the passphrase, using ${passphrase_arg} if applicable. */ int keyfile_read(const char * filename, uint64_t * machinenum, int keys, int force, enum passphrase_entry passphrase_entry, const char * passphrase_arg) { struct stat sb; uint8_t * keybuf; FILE * f; size_t keyfilelen; /* Open the file. */ if ((f = fopen(filename, "r")) == NULL) { warnp("fopen(%s)", filename); goto err0; } /* Stat the file. */ if (fstat(fileno(f), &sb)) { warnp("stat(%s)", filename); goto err1; } /* Validate keyfile size. */ if ((sb.st_size == 0) || (sb.st_size > 1000000)) { warn0("Key file has unreasonable size: %s", filename); goto err1; } keyfilelen = (size_t)(sb.st_size); /* Allocate memory. */ if ((keybuf = malloc(keyfilelen)) == NULL) goto err1; /* Read the file. */ if (fread(keybuf, keyfilelen, 1, f) != 1) { warnp("fread(%s)", filename); goto err2; } if (fclose(f)) { warnp("fclose(%s)", filename); f = NULL; goto err2; } f = NULL; /* If this is a raw key file, process it. */ if ((keybuf[0] == 0x00) || (keybuf[0] == 0xff)) { if (read_raw(keybuf, keyfilelen, machinenum, filename, keys)) { if (errno) warnp("Error parsing key file: %s", filename); goto err2; } } else { /* Otherwise, try to base64 decode it. */ if (read_base64((const char *)keybuf, keyfilelen, machinenum, filename, keys, force, passphrase_entry, passphrase_arg)) { if (errno) warnp("Error parsing key file: %s", filename); goto err2; } } /* Zero and free memory. */ insecure_memzero(keybuf, keyfilelen); free(keybuf); /* Success! */ return (0); err2: insecure_memzero(keybuf, keyfilelen); free(keybuf); err1: if (f != NULL) fclose(f); err0: /* Failure! */ return (-1); } /** * keyfile_write(filename, machinenum, keys, passphrase, maxmem, cputime): * Write a key file for the specified machine containing the specified keys. * If ${passphrase} is non-NULL, use up to ${cputime} seconds and ${maxmem} * bytes of memory to encrypt the key file. */ int keyfile_write(const char * filename, uint64_t machinenum, int keys, char * passphrase, size_t maxmem, double cputime) { FILE * f; /* Create key file. */ if ((f = keyfile_write_open(filename)) == NULL) { warnp("Cannot create %s", filename); goto err0; } /* Write keys. */ if (keyfile_write_file(f, machinenum, keys, passphrase, maxmem, cputime)) goto err2; /* Close the key file. */ if (fclose(f)) { warnp("Error closing key file"); goto err1; } /* Success! */ return (0); err2: fclose(f); err1: unlink(filename); err0: /* Failure! */ return (-1); } /** * keyfile_write_open(filename): * Open a key file for writing. Avoid race conditions. Return a FILE *. */ FILE * keyfile_write_open(const char * filename) { FILE * f; int fd; int saved_errno; /* Attempt to create the file. */ if ((fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) { /* Special error message for EEXIST. */ if (errno == EEXIST) warn0("Key file already exists, not overwriting: %s", filename); goto err0; } /* Wrap the fd into a FILE. */ if ((f = fdopen(fd, "w")) == NULL) { saved_errno = errno; goto err1; } /* Success! */ return (f); err1: unlink(filename); if (close(fd)) warnp("close"); errno = saved_errno; err0: /* Failure! */ return (NULL); } /** * keyfile_write_file(f, machinenum, keys, passphrase, maxmem, cputime): * Write a key file for the specified machine containing the specified keys. * If ${passphrase} is non-NULL, use up to ${cputime} seconds and ${maxmem} * bytes of memory to encrypt the key file. */ int keyfile_write_file(FILE * f, uint64_t machinenum, int keys, char * passphrase, size_t maxmem, double cputime) { uint8_t * keybuf; size_t keybuflen; uint8_t * tskeybuf; size_t tskeylen; uint8_t * encrbuf; int rc; uint8_t linebuf256[54]; char linebuf64[73]; size_t writepos; size_t linelen; uint8_t hbuf[32]; double maxmemfrac = (maxmem != 0) ? 0.5 : 0.125; struct scryptenc_params params = {maxmem, maxmemfrac, cputime, 0, 0, 0}; /* Export keys. */ if (crypto_keys_export(keys, &keybuf, &keybuflen)) { warnp("Error exporting keys"); goto err0; } /* Construct "cooked" key file. */ tskeylen = keybuflen + 16; if ((tskeybuf = malloc(tskeylen)) == NULL) goto err1; memcpy(tskeybuf, "tarsnap\0", 8); be64enc(&tskeybuf[8], machinenum); memcpy(&tskeybuf[16], keybuf, keybuflen); /* * Don't need this any more. To simplify error handling, we zero * this here but free it later. */ insecure_memzero(keybuf, keybuflen); /* If we have a passphrase, we want to encrypt. */ if (passphrase != NULL) { /* Allocate space for encrypted buffer. */ if ((encrbuf = malloc(tskeylen + 128)) == NULL) goto err2; /* Encrypt. */ switch ((rc = scryptenc_buf(tskeybuf, tskeylen, encrbuf, (uint8_t *)passphrase, strlen(passphrase), ¶ms, 0, 0))) { case SCRYPT_OK: /* Success! */ break; case SCRYPT_ELIMIT: warnp("Error determining amount of available memory"); break; case SCRYPT_ECLOCK: warnp("Error reading clocks"); break; case SCRYPT_EKEY: warnp("Error computing derived key"); break; case SCRYPT_ESALT: warnp("Error reading salt"); break; case SCRYPT_EOPENSSL: warnp("OpenSSL error"); break; case SCRYPT_ENOMEM: warnp("Error allocating memory"); break; default: warn0("Programmer error: " "Impossible error returned by scryptenc_buf"); break; } /* Error out if the encryption failed. */ if (rc != SCRYPT_OK) { insecure_memzero(encrbuf, tskeylen + 128); free(encrbuf); goto err2; } /* Switch key buffers. */ insecure_memzero(tskeybuf, tskeylen); free(tskeybuf); tskeylen = tskeylen + 128; tskeybuf = encrbuf; } /* Base64-encode the buffer, writing it out as we go. */ if (fprintf(f, "# START OF TARSNAP KEY FILE\n") < 0) { warnp("Error writing key file"); goto err2; } for (writepos = 0; writepos < tskeylen; writepos += linelen) { linelen = 48; if (writepos + linelen > tskeylen) linelen = tskeylen - writepos; /* Copy bytes into line buffer. */ memcpy(linebuf256, &tskeybuf[writepos], linelen); /* Append 6 bytes of SHA256 hash. */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, linebuf256, linelen, hbuf)) { warn0("Programmer error: " "SHA256 should never fail"); goto err2; } memcpy(&linebuf256[linelen], hbuf, 6); /* Base64-encode. */ b64encode(linebuf256, linebuf64, linelen + 6); /* Write out the line. */ if (fprintf(f, "%s\n", linebuf64) < 0) { warnp("Error writing key file"); goto err2; } } if (fprintf(f, "# END OF TARSNAP KEY FILE\n") < 0) { warnp("Error writing key file"); goto err2; } /* Zero and free key buffers. */ insecure_memzero(tskeybuf, tskeylen); free(tskeybuf); free(keybuf); /* Success! */ return (0); err2: insecure_memzero(tskeybuf, tskeylen); free(tskeybuf); err1: insecure_memzero(keybuf, keybuflen); free(keybuf); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/keyfile/keyfile.h000066400000000000000000000030121476705112600172510ustar00rootroot00000000000000#ifndef KEYFILE_H_ #define KEYFILE_H_ #include #include #include "passphrase_entry.h" /** * keyfile_read(filename, machinenum, keys, force, passphrase_entry, * passphrase_arg): * Read keys from a tarsnap key file; and return the machine # via the * provided pointer. Ignore any keys not specified in the ${keys} mask. * If ${force} is 1, do not check whether decryption will exceed * the estimated available memory or time. Use the ${passphrase_entry} * method to read the passphrase, using ${passphrase_arg} if applicable. */ int keyfile_read(const char *, uint64_t *, int, int, enum passphrase_entry, const char *); /** * keyfile_write(filename, machinenum, keys, passphrase, maxmem, cputime): * Write a key file for the specified machine containing the specified keys. * If ${passphrase} is non-NULL, use up to ${cputime} seconds and ${maxmem} * bytes of memory to encrypt the key file. */ int keyfile_write(const char *, uint64_t, int, char *, size_t, double); /** * keyfile_write_open(filename): * Open a key file for writing. Avoid race conditions. Return a FILE *. */ FILE * keyfile_write_open(const char *); /** * keyfile_write_file(f, machinenum, keys, passphrase, maxmem, cputime): * Write a key file for the specified machine containing the specified keys. * If ${passphrase} is non-NULL, use up to ${cputime} seconds and ${maxmem} * bytes of memory to encrypt the key file. */ int keyfile_write_file(FILE *, uint64_t, int, char *, size_t, double); #endif /* !KEYFILE_H_ */ tarsnap-1.0.41/lib/netpacket/000077500000000000000000000000001476705112600160025ustar00rootroot00000000000000tarsnap-1.0.41/lib/netpacket/netpacket.h000066400000000000000000000213411476705112600201320ustar00rootroot00000000000000#ifndef NETPACKET_H_ #define NETPACKET_H_ #include #include "crypto.h" #include "netproto.h" /* Internal netpacket cookie. */ typedef struct netpacket_internal NETPACKET_CONNECTION; /* Function for sending a request once a connection is established. */ typedef int sendpacket_callback(void *, NETPACKET_CONNECTION *); /* Function for handling a response packet. */ typedef int handlepacket_callback(void *, NETPACKET_CONNECTION *, int, uint8_t, const uint8_t *, size_t); /* packet types. */ #define NETPACKET_REGISTER_REQUEST 0x00 #define NETPACKET_REGISTER_CHALLENGE 0x80 #define NETPACKET_REGISTER_CHA_RESPONSE 0x01 #define NETPACKET_REGISTER_RESPONSE 0x81 #define NETPACKET_TRANSACTION_GETNONCE 0x10 #define NETPACKET_TRANSACTION_GETNONCE_RESPONSE 0x90 #define NETPACKET_TRANSACTION_START 0x11 #define NETPACKET_TRANSACTION_START_RESPONSE 0x91 #define NETPACKET_TRANSACTION_COMMIT 0x12 #define NETPACKET_TRANSACTION_COMMIT_RESPONSE 0x92 #define NETPACKET_TRANSACTION_CHECKPOINT 0x13 #define NETPACKET_TRANSACTION_CHECKPOINT_RESPONSE 0x93 #define NETPACKET_TRANSACTION_CANCEL 0x14 #define NETPACKET_TRANSACTION_CANCEL_RESPONSE 0x94 #define NETPACKET_TRANSACTION_TRYCOMMIT 0x15 #define NETPACKET_TRANSACTION_TRYCOMMIT_RESPONSE 0x95 #define NETPACKET_TRANSACTION_ISCHECKPOINTED 0x16 #define NETPACKET_TRANSACTION_ISCHECKPOINTED_RESPONSE 0x96 #define NETPACKET_WRITE_FEXIST 0x20 #define NETPACKET_WRITE_FEXIST_RESPONSE 0xa0 #define NETPACKET_WRITE_FILE 0x21 #define NETPACKET_WRITE_FILE_RESPONSE 0xa1 #define NETPACKET_DELETE_FILE 0x30 #define NETPACKET_DELETE_FILE_RESPONSE 0xb0 #define NETPACKET_READ_FILE 0x40 #define NETPACKET_READ_FILE_RESPONSE 0xc0 #define NETPACKET_DIRECTORY 0x50 #define NETPACKET_DIRECTORY_D 0x51 #define NETPACKET_DIRECTORY_RESPONSE 0xd0 /* Maximum number of files listed in a NETPACKET_DIRECTORY_RESPONSE packet. */ #define NETPACKET_DIRECTORY_RESPONSE_MAXFILES 8000 /** * netpacket_hmac_verify(type, nonce, packetbuf, pos, key): * Verify that HMAC(type || nonce || packetbuf[0 .. pos - 1]) using the * specified key matches packetbuf[pos .. pos + 31]. If nonce is NULL, omit * it from the data being HMACed as appropriate. Return -1 on error, 0 on * success, or 1 if the hash does not match. */ int netpacket_hmac_verify(uint8_t, const uint8_t[32], const uint8_t *, size_t, int); /** * netpacket_register_request(NPC, user, callback): * Construct and send a NETPACKET_REGISTER_REQUEST packet asking to register * a new machine belonging to the specified user. */ int netpacket_register_request(NETPACKET_CONNECTION *, const char *, handlepacket_callback *); /** * netpacket_register_cha_response(NPC, keys, name, register_key, callback): * Construct and send a NETPACKET_REGISTER_CHA_RESPONSE packet providing the * given access ${keys} and user-friendly ${name}, signed using the shared key * ${register_key} computed by hashing the Diffie-Hellman shared secret K. */ int netpacket_register_cha_response(NETPACKET_CONNECTION *, const uint8_t[96], const char *, const uint8_t[32], handlepacket_callback *); /** * netpacket_transaction_getnonce(NPC, machinenum, callback): * Construct and send a NETPACKET_TRANSACTION_GETNONCE packet asking to get * a transaction server nonce. */ int netpacket_transaction_getnonce(NETPACKET_CONNECTION *, uint64_t, handlepacket_callback *); /** * netpacket_transaction_start(NPC, machinenum, operation, snonce, cnonce, * state, callback): * Construct and send a NETPACKET_TRANSACTION_START packet asking to * start a transaction; the transaction is a write transaction if * ${operation} is 0, a delete transaction if ${operation} is 1, a fsck * transaction with the delete key if ${operation} is 2, or a fsck * transaction with the write key if ${operation} is 3. */ int netpacket_transaction_start(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t[32], const uint8_t[32], handlepacket_callback *); /** * netpacket_transaction_commit(NPC, machinenum, whichkey, nonce, callback): * Construct and send a NETPACKET_TRANSACTION_COMMIT packet asking to commit * a transaction; the packet is signed with the write access key if * ${whichkey} is 0, and with the delete access key if ${whichkey} is 1. */ int netpacket_transaction_commit(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], handlepacket_callback *); /** * netpacket_transaction_checkpoint(NPC, machinenum, whichkey, ckptnonce, * nonce, callback): * Construct and send a NETPACKET_TRANSACTION_CHECKPOINT packet asking to * create a checkpoint in a write transaction. */ int netpacket_transaction_checkpoint(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t[32], handlepacket_callback *); /** * netpacket_transaction_cancel(NPC, machinenum, whichkey, snonce, cnonce, * state, callback): * Construct and send a NETPACKET_TRANSACTION_CANCEL packet asking to cancel * a pending transaction if the state is correct. */ int netpacket_transaction_cancel(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t[32], const uint8_t[32], handlepacket_callback *); /** * netpacket_transaction_trycommit(NPC, machinenum, whichkey, nonce, * callback): * Construct and send a NETPACKET_TRANSACTION_TRYCOMMIT packet asking to * commit a transaction; the packet is signed with the write access key if * ${whichkey} is 0, and with the delete access key if ${whichkey} is 1. */ int netpacket_transaction_trycommit(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], handlepacket_callback *); /** * netpacket_transaction_ischeckpointed(NPC, machinenum, whichkey, nonce, * callback): * Construct and send a NETPACKET_TRANSACTION_ISCHECKPOINTED packet asking if * a checkpointed write transaction is in progress; the packet is signed with * the write access key if ${whichkey} is 0, and with the delete access key * if ${whichkey} is 1. */ int netpacket_transaction_ischeckpointed(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], handlepacket_callback *); /** * netpacket_write_fexist(NPC, machinenum, class, name, nonce, callback): * Construct and send a NETPACKET_WRITE_FEXIST packet asking if the * specified file exists. */ int netpacket_write_fexist(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t[32], handlepacket_callback *); /** * netpacket_write_file(NPC, machinenum, class, name, buf, buflen, * nonce, callback): * Construct and send a NETPACKET_WRITE_FILE packet asking to write the * specified file. */ int netpacket_write_file(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t *, size_t, const uint8_t[32], handlepacket_callback *); /** * netpacket_delete_file(NPC, machinenum, class, name, nonce, callback): * Construct and send a NETPACKET_DELETE_FILE packet asking to delete the * specified file. */ int netpacket_delete_file(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t[32], handlepacket_callback *); /** * netpacket_read_file(NPC, machinenum, class, name, size, callback): * Construct and send a NETPACKET_READ_FILE packet asking to read the * specified file, which should be ${size} (<= 262144) bytes long if ${size} * is not (uint32_t)(-1). */ int netpacket_read_file(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], uint32_t, handlepacket_callback *); /** * netpacket_directory(NPC, machinenum, class, start, snonce, cnonce, key, * callback): * Construct and send a NETPACKET_DIRECTORY packet (if key == 0) or * NETPACKET_DIRECTORY_D packet (otherwise) asking for a list of files * of the specified class starting from the specified position. */ int netpacket_directory(NETPACKET_CONNECTION *, uint64_t, uint8_t, const uint8_t[32], const uint8_t[32], const uint8_t[32], int, handlepacket_callback *); /** * netpacket_directory_readmore(NPC, callback): * Read more NETPACKET_DIRECTORY_RESPONSE packets. */ int netpacket_directory_readmore(NETPACKET_CONNECTION *, handlepacket_callback *); /** * netpacket_open(useragent): * Return a netpacket connection cookie. */ NETPACKET_CONNECTION * netpacket_open(const char *); /** * netpacket_op(NPC, writepacket, cookie): * Call ${writepacket} to send a request to the server over the provided * netpacket connection. */ int netpacket_op(NETPACKET_CONNECTION *, sendpacket_callback *, void *); /** * netpacket_getstats(NPC, in, out, queued): * Obtain the number of bytes received and sent via the connection, and the * number of bytes ${queued} to be written. */ void netpacket_getstats(NETPACKET_CONNECTION *, uint64_t *, uint64_t *, uint64_t *); /** * netpacket_close(NPC): * Close a netpacket connection. */ int netpacket_close(NETPACKET_CONNECTION *); #endif /* !NETPACKET_H_ */ tarsnap-1.0.41/lib/netpacket/netpacket_delete.c000066400000000000000000000021711476705112600214470ustar00rootroot00000000000000#include #include #include "crypto.h" #include "netpacket_internal.h" #include "netproto.h" #include "sysendian.h" #include "netpacket.h" /** * netpacket_delete_file(NPC, machinenum, class, name, nonce, callback): * Construct and send a NETPACKET_DELETE_FILE packet asking to delete the * specified file. */ int netpacket_delete_file(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t class, const uint8_t name[32], const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t packetbuf[105]; /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = class; memcpy(&packetbuf[9], name, 32); memcpy(&packetbuf[41], nonce, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_DELETE_FILE, packetbuf, 73, CRYPTO_KEY_AUTH_DELETE)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_DELETE_FILE, packetbuf, 105, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/netpacket_directory.c000066400000000000000000000034161476705112600222140ustar00rootroot00000000000000#include #include #include "crypto.h" #include "netpacket_internal.h" #include "netproto.h" #include "sysendian.h" #include "netpacket.h" /** * netpacket_directory(NPC, machinenum, class, start, snonce, cnonce, key, * callback): * Construct and send a NETPACKET_DIRECTORY packet (if key == 0) or * NETPACKET_DIRECTORY_D packet (otherwise) asking for a list of files * of the specified class starting from the specified position. */ int netpacket_directory(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t class, const uint8_t start[32], const uint8_t snonce[32], const uint8_t cnonce[32], int key, handlepacket_callback * callback) { uint8_t packetbuf[137]; /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = class; memcpy(&packetbuf[9], start, 32); memcpy(&packetbuf[41], snonce, 32); memcpy(&packetbuf[73], cnonce, 32); /* Append hmac. */ if (netpacket_hmac_append( (key == 0) ? NETPACKET_DIRECTORY : NETPACKET_DIRECTORY_D, packetbuf, 105, (key == 0) ? CRYPTO_KEY_AUTH_GET : CRYPTO_KEY_AUTH_DELETE)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, (key == 0) ? NETPACKET_DIRECTORY : NETPACKET_DIRECTORY_D, packetbuf, 137, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_directory_readmore(NPC, callback): * Read more NETPACKET_DIRECTORY_RESPONSE packets. */ int netpacket_directory_readmore(NETPACKET_CONNECTION * NPC, handlepacket_callback * callback) { /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); } tarsnap-1.0.41/lib/netpacket/netpacket_hmac.c000066400000000000000000000027371476705112600211250ustar00rootroot00000000000000#include #include #include "crypto_verify_bytes.h" #include "crypto.h" #include "netpacket.h" #include "netpacket_internal.h" /** * netpacket_hmac_append(type, packetbuf, len, key): * HMAC (type || packetbuf[0 .. len - 1]) using the specified key and write * the result into packetbuf[len .. len + 31]. */ int netpacket_hmac_append(uint8_t type, uint8_t * packetbuf, size_t len, int key) { return (crypto_hash_data_2(key, &type, 1, packetbuf, len, &packetbuf[len])); } /** * netpacket_hmac_verify(type, nonce, packetbuf, pos, key): * Verify that HMAC(type || nonce || packetbuf[0 .. pos - 1]) using the * specified key matches packetbuf[pos .. pos + 31]. If nonce is NULL, omit * it from the data being HMACed as appropriate. Return -1 on error, 0 on * success, or 1 if the hash does not match. */ int netpacket_hmac_verify(uint8_t type, const uint8_t nonce[32], const uint8_t * packetbuf, size_t pos, int key) { uint8_t hmac_actual[32]; uint8_t prefixbuf[33]; size_t prefixlen; /* Compute the correct HMAC. */ prefixbuf[0] = type; prefixlen = 1; if (nonce != NULL) { memcpy(&prefixbuf[prefixlen], nonce, 32); prefixlen += 32; } if (crypto_hash_data_2(key, prefixbuf, prefixlen, packetbuf, pos, hmac_actual)) goto err0; /* Compare. */ if (crypto_verify_bytes(&packetbuf[pos], hmac_actual, 32)) goto badhmac; /* Success! */ return (0); badhmac: /* HMAC doesn't match. */ return (1); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/netpacket_internal.h000066400000000000000000000030421476705112600220240ustar00rootroot00000000000000#ifndef NETPACKET_INTERNAL_H_ #define NETPACKET_INTERNAL_H_ #include #include "netpacket.h" struct netpacket_op { sendpacket_callback * writepacket; int (* getbuf)(void *, uint8_t, uint8_t **, size_t); handlepacket_callback * handlepacket; void * cookie; /* Linked list. */ struct netpacket_op * next; }; struct netpacket_internal { /* Network protocol data. */ char * useragent; NETPROTO_CONNECTION * NC; uint8_t packettype; size_t packetlen; uint8_t * packetbuf; /* State: 0 = disconnected, 1 = connecting, 2 = connected. */ int state; /* Number of lost connections since the last successful operation. */ int ndrops; /* Has a 'connection lost' message been printed? */ int connlostmsgprinted; /* Non-zero if we have ever successfully connected. */ int serveralive; /* Non-zero if a netproto_readpacket call is pending. */ int reading; /* Bandwidth totals from dead connections. */ uint64_t bytesin; uint64_t bytesout; /* Queue of uncompleted operations. */ struct netpacket_op * pending_head; struct netpacket_op * pending_tail; struct netpacket_op * pending_current; }; /** * netpacket_hmac_append(type, packetbuf, len, key): * HMAC (type || packetbuf[0 .. len - 1]) using the specified key and write * the result into packetbuf[len .. len + 31]. */ int netpacket_hmac_append(uint8_t, uint8_t *, size_t, int); /** * netpacket_op_packetsent(cookie, status): * Callback for packet send completion via netpacket_op interface. */ int netpacket_op_packetsent(void *, int); #endif /* !NETPACKET_INTERNAL_H_ */ tarsnap-1.0.41/lib/netpacket/netpacket_op.c000066400000000000000000000322001476705112600206170ustar00rootroot00000000000000#include #include #include #include #include #include #include "crypto.h" #include "monoclock.h" #include "netpacket_internal.h" #include "netproto.h" #include "sysendian.h" #include "tarsnap_opt.h" #include "tsnetwork.h" #include "warnp.h" #include "netpacket.h" static int reconnect(NETPACKET_CONNECTION * NPC); static network_callback callback_connect; static network_callback callback_reconnect; static int callback_getbuf(void *, uint8_t, uint8_t **, size_t); static network_callback callback_packetreceived; /* Maximum number of times we'll try to reconnect. */ #define MAXRECONNECTS 10 /* As above, except the server doesn't seem to be around at all. */ #define MAXRECONNECTS_AWOL 3 /* Time to wait between each attempt. */ static int reconnect_wait[MAXRECONNECTS + 1] = { 0, 0, 1, 2, 4, 8, 15, 30, 60, 90, 90 }; /* Global tarsnap option declared in tarsnap_opt.h. */ int tarsnap_opt_retry_forever = 0; int tarsnap_opt_debug_network_stats = 0; /* Time before which we shouldn't print a "connection lost" warning. */ static struct timeval next_connlost_warning = { 0, 0}; /** * netpacket_open(useragent): * Return a netpacket connection cookie. */ NETPACKET_CONNECTION * netpacket_open(const char * useragent) { struct netpacket_internal * NPC; /* Construct cookie structure. */ if ((NPC = malloc(sizeof(struct netpacket_internal))) == NULL) goto err0; if ((NPC->useragent = strdup(useragent)) == NULL) goto err1; NPC->NC = NULL; NPC->packetbuf = NULL; /* We're not connected yet. */ NPC->state = 0; /* We haven't lost any connections yet. */ NPC->ndrops = 0; /* We haven't printed a connection lost message yet. */ NPC->connlostmsgprinted = 0; /* We've never successfully connected to the server. */ NPC->serveralive = 0; /* We're not reading a packet yet. */ NPC->reading = 0; /* We haven't used any bandwidth yet. */ NPC->bytesin = NPC->bytesout = 0; /* The queue is empty. */ NPC->pending_head = NPC->pending_tail = NPC->pending_current = NULL; /* Success! */ return (NPC); err1: free(NPC); err0: /* Failure! */ return (NULL); } static int callback_connect(void * cookie, int status) { struct netpacket_internal * NPC = cookie; /* If we're being cancelled, return. */ if (status == NETWORK_STATUS_CANCEL) goto done; /* Did we successfully connect? */ if (status == NETWORK_STATUS_OK) { /* We're connected. */ NPC->state = 2; NPC->serveralive = 1; /* If there are pending operation(s), do them now. */ for (NPC->pending_current = NPC->pending_head; NPC->pending_current != NULL; NPC->pending_current = NPC->pending_current->next) { if ((NPC->pending_current->writepacket)( NPC->pending_current->cookie, NPC)) goto err0; } } else { /* Try again... */ if (reconnect(NPC)) goto err0; } done: /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_op(NPC, writepacket, cookie): * Call ${writepacket} to send a request to the server over the provided * netpacket connection. */ int netpacket_op(NETPACKET_CONNECTION * NPC, sendpacket_callback * writepacket, void * cookie) { struct netpacket_op * op; /* Allocate memory for cookie. */ if ((op = malloc(sizeof(struct netpacket_op))) == NULL) goto err0; /* Store parameters for request, including default getbuf callback. */ op->writepacket = writepacket; op->getbuf = callback_getbuf; op->cookie = cookie; /* Add operation to queue. */ op->next = NULL; if (NPC->pending_tail != NULL) { NPC->pending_tail->next = op; NPC->pending_tail = op; } else { NPC->pending_head = op; NPC->pending_tail = op; } switch (NPC->state) { case 0: /* We need to connect to the server. */ if ((NPC->NC = netproto_connect(NPC->useragent, callback_connect, NPC)) == NULL) goto err0; NPC->state = 1; break; case 1: /* * Do nothing: The packet will be sent from callback_connect * once the connection is established. */ break; case 2: /* We're already connected: Send the packet. */ NPC->pending_current = op; if ((op->writepacket)(op->cookie, NPC)) goto err0; break; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } static int callback_reconnect(void * cookie, int status) { struct netpacket_internal * NPC = cookie; uint64_t in, out, queued; /* If we're being cancelled, return. */ if (status == NETWORK_STATUS_CANCEL) goto done; /* The status should be NETWORK_STATUS_TIMEOUT. */ if (status != NETWORK_STATUS_TIMEOUT) { warn0("Bad status in callback_reconnect: %d", status); goto err0; } /* Add the bandwidth used by the connection to our running totals. */ netproto_getstats(NPC->NC, &in, &out, &queued); NPC->bytesin += in; NPC->bytesout += out; /* Close the (dead) connection. */ if (netproto_close(NPC->NC)) goto err1; NPC->NC = NULL; /* Open a new connection. */ if ((NPC->NC = netproto_connect(NPC->useragent, callback_connect, NPC)) == NULL) goto err0; done: /* Success! */ return (0); err1: NPC->NC = NULL; err0: /* Failure! */ return (-1); } static int reconnect(NETPACKET_CONNECTION * NPC) { struct timeval tp; int nseconds; /* Flush any pending activity on the socket. */ if (netproto_flush(NPC->NC)) goto err0; /* We're trying to reconnect. */ NPC->state = 1; /* We're not reading a packet any more, if we ever were. */ NPC->reading = 0; /* Have we lost our connection / failed to connect too many times? */ NPC->ndrops += 1; if (tarsnap_opt_retry_forever && NPC->ndrops > MAXRECONNECTS) NPC->ndrops = MAXRECONNECTS; if ((NPC->ndrops > MAXRECONNECTS) || (NPC->serveralive == 0 && NPC->ndrops > MAXRECONNECTS_AWOL)) { warn0("Too many network failures"); goto err0; } /* Figure out how long we ought to wait before reconnecting. */ nseconds = reconnect_wait[NPC->ndrops]; /* * Warn the user that we're waiting, if we haven't already printed a * warning message recently. */ if (monoclock_get(&tp)) goto err0; if ((nseconds >= (tarsnap_opt_noisy_warnings ? 1 : 30)) && ((tp.tv_sec > next_connlost_warning.tv_sec) || ((tp.tv_sec == next_connlost_warning.tv_sec) && (tp.tv_usec > next_connlost_warning.tv_usec)))) { warn0("Connection lost, " "waiting %d seconds before reconnecting", nseconds); next_connlost_warning.tv_sec = tp.tv_sec + nseconds; next_connlost_warning.tv_usec = tp.tv_usec; /* * Record that we printed a 'connection lost' warning for * this connection. */ NPC->connlostmsgprinted = 1; } /* Set a callback to reconnect. */ if (netproto_sleep(NPC->NC, nseconds, callback_reconnect, NPC)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_op_packetsent(cookie, status): * Callback for packet send completion via netpacket_op interface. */ int netpacket_op_packetsent(void * cookie, int status) { struct netpacket_internal * NPC = cookie; int rc = 0; /* If we're being cancelled, return. */ if (status == NETWORK_STATUS_CANCEL) goto done; /* Try to reconnect if there was an error. */ if (status != NETWORK_STATUS_OK) { if (reconnect(NPC)) goto err0; goto done; } /* We want to read a response packet if we're not already doing so. */ if (NPC->reading == 0) { if (netproto_readpacket(NPC->NC, NPC->pending_head->getbuf, callback_packetreceived, NPC)) goto err0; NPC->reading = 1; } done: /* Return success or the return code from the callback. */ return (rc); err0: /* Failure! */ return (-1); } static int callback_getbuf(void * cookie, uint8_t type, uint8_t ** buf, size_t buflen) { struct netpacket_internal * NPC = cookie; int status = NETWORK_STATUS_OK; /* Store packet type and length for future reference. */ NPC->packettype = type; NPC->packetlen = buflen; /* Sanity-check the type and length. */ switch (type) { case NETPACKET_REGISTER_CHALLENGE: if (buflen != 32 + CRYPTO_DH_PUBLEN) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_REGISTER_RESPONSE: if (buflen != 41) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_TRANSACTION_START_RESPONSE: case NETPACKET_TRANSACTION_CANCEL_RESPONSE: case NETPACKET_TRANSACTION_TRYCOMMIT_RESPONSE: if (buflen != 33) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_TRANSACTION_GETNONCE_RESPONSE: case NETPACKET_TRANSACTION_COMMIT_RESPONSE: if (buflen != 32) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_TRANSACTION_CHECKPOINT_RESPONSE: case NETPACKET_TRANSACTION_ISCHECKPOINTED_RESPONSE: if (buflen != 65) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_WRITE_FEXIST_RESPONSE: case NETPACKET_WRITE_FILE_RESPONSE: case NETPACKET_DELETE_FILE_RESPONSE: if (buflen != 66) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_READ_FILE_RESPONSE: if ((buflen < 70) || (buflen > 70 + 262144)) status = NETPROTO_STATUS_PROTERR; break; case NETPACKET_DIRECTORY_RESPONSE: if ((buflen < 70) || (buflen > 70 + 8000 * 32) || (buflen % 32 != 70 % 32)) status = NETPROTO_STATUS_PROTERR; break; default: /* Unknown packet type. */ status = NETPROTO_STATUS_PROTERR; break; } /* If the length is valid, we need to allocate a buffer. */ if (status == NETWORK_STATUS_OK) { *buf = NPC->packetbuf = malloc(buflen); if (*buf == NULL) status = NETWORK_STATUS_ERR; } return (status); } static int callback_packetreceived(void * cookie, int status) { struct netpacket_internal * NPC = cookie; struct netpacket_op * head = NPC->pending_head; handlepacket_callback * handlepacket = head->handlepacket; int rc = 0; /* If we're being cancelled, return. */ if (status == NETWORK_STATUS_CANCEL) goto done; /* On non-protocol errors, try to reconnect. */ if ((status != NETWORK_STATUS_OK) && (status != NETPROTO_STATUS_PROTERR)) { if (reconnect(NPC)) goto err0; goto done; } /* Call upstream callback. */ head->handlepacket = NULL; NPC->pending_current = head; rc = handlepacket(head->cookie, NPC, status, NPC->packettype, NPC->packetbuf, NPC->packetlen); /* If we didn't send a follow-up packet, this operation is done. */ if (head->handlepacket == NULL) { if (NPC->pending_tail == head) NPC->pending_tail = NULL; NPC->pending_head = head->next; free(head); /* We have successfully performed an operation. */ NPC->ndrops = 0; /* * If a 'connection lost' message was printed for this * connection, tell the user that the connection has been * re-established. We don't do this earlier because * obtaining a TCP connection is useless if we can't actually * manage to send a request and get a response back through * it. */ if (NPC->connlostmsgprinted) { warn0("Connection re-established"); /* Don't print this again. */ NPC->connlostmsgprinted = 0; } } /* Read another packet if appropriate. */ if ((NPC->pending_head != NULL) && (NPC->pending_head->handlepacket != NULL)) { if (netproto_readpacket(NPC->NC, NPC->pending_head->getbuf, callback_packetreceived, NPC)) goto err0; } else { NPC->reading = 0; } done: /* Free the packet buffer. */ free(NPC->packetbuf); NPC->packetbuf = NULL; /* Return value from callback. */ return (rc); err0: /* Failure! */ return (-1); } /** * netpacket_getstats(NPC, in, out, queued): * Obtain the number of bytes received and sent via the connection, and the * number of bytes ${queued} to be written. */ void netpacket_getstats(NETPACKET_CONNECTION * NPC, uint64_t * in, uint64_t * out, uint64_t * queued) { /* Get statistics from the current connection if one exists. */ if (NPC->NC != NULL) netproto_getstats(NPC->NC, in, out, queued); else *in = *out = *queued = 0; /* Add statistics from past connections. */ *in += NPC->bytesin; *out += NPC->bytesout; } /* Print statistics about network usage. */ static void netpacket_printstats(NETPACKET_CONNECTION * NPC) { uint64_t in, out, queued; /* Get & print the network traffic from this netpacket connection. */ netpacket_getstats(NPC, &in, &out, &queued); /* Print warning if there's any queued data. */ if (queued > 0) warnp("Connection ended with queued:\t" PRIu64, queued); /* Print in/out network usage. */ fprintf(stderr, "Connection ended with in / out:\t" "%" PRIu64 "\t%" PRIu64 "\n", in, out); } /** * netpacket_close(NPC): * Close a netpacket connection. */ int netpacket_close(NETPACKET_CONNECTION * NPC) { struct netpacket_op * next; /* Close the network protocol layer connection if we have one. */ if (NPC->NC != NULL) if (netproto_close(NPC->NC)) goto err1; /* Free any queued operations. */ NPC->pending_current = NPC->pending_head; while (NPC->pending_current != NULL) { next = NPC->pending_current->next; free(NPC->pending_current); NPC->pending_current = next; } /* Print statistics about network usage (if desired). */ if (tarsnap_opt_debug_network_stats) netpacket_printstats(NPC); /* Free string allocated by strdup. */ free(NPC->useragent); /* Free the cookie. */ free(NPC); /* Success! */ return (0); err1: NPC->pending_current = NPC->pending_head; while (NPC->pending_current != NULL) { next = NPC->pending_current->next; free(NPC->pending_current); NPC->pending_current = next; } free(NPC->useragent); free(NPC); /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/netpacket_read.c000066400000000000000000000026221476705112600211210ustar00rootroot00000000000000#include #include #include #include "crypto.h" #include "netpacket_internal.h" #include "netproto.h" #include "sysendian.h" #include "warnp.h" #include "netpacket.h" /** * netpacket_read_file(NPC, machinenum, class, name, size, callback): * Construct and send a NETPACKET_READ_FILE packet asking to read the * specified file, which should be ${size} (<= 262144) bytes long if ${size} * is not (uint32_t)(-1). */ int netpacket_read_file(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t class, const uint8_t name[32], uint32_t size, handlepacket_callback * callback) { uint8_t packetbuf[77]; /* Sanity-check size. */ if ((size > 262144) && (size != (uint32_t)(-1))) { warn0("file of class %c too large: (%zu > %zu)", class, (size_t)size, (size_t)262144); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = class; memcpy(&packetbuf[9], name, 32); be32enc(&packetbuf[41], size); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_READ_FILE, packetbuf, 45, CRYPTO_KEY_AUTH_GET)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_READ_FILE, packetbuf, 77, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/netpacket_register.c000066400000000000000000000045671476705112600220440ustar00rootroot00000000000000#include #include #include #include "crypto.h" #include "netpacket_internal.h" #include "netproto.h" #include "warnp.h" #include "netpacket.h" /** * netpacket_register_request(NPC, user, callback): * Construct and send a NETPACKET_REGISTER_REQUEST packet asking to register * a new machine belonging to the specified user. */ int netpacket_register_request(NETPACKET_CONNECTION * NPC, const char * user, handlepacket_callback * callback) { /* Make sure user name is a sane length. */ if (strlen(user) > 255) { warn0("User name too long: %s", user); goto err0; } /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_REGISTER_REQUEST, (const uint8_t *)user, strlen(user), netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_register_cha_response(NPC, keys, name, register_key, callback): * Construct and send a NETPACKET_REGISTER_CHA_RESPONSE packet providing the * given access ${keys} and user-friendly ${name}, signed using the shared key * ${register_key} computed by hashing the Diffie-Hellman shared secret K. */ int netpacket_register_cha_response(NETPACKET_CONNECTION * NPC, const uint8_t keys[96], const char * name, const uint8_t register_key[32], handlepacket_callback * callback) { size_t namelen; uint8_t * packetbuf; uint8_t prefixbuf[1]; /* Allocate temporary space for constructing packet. */ namelen = strlen(name); if ((packetbuf = malloc(129 + namelen)) == NULL) goto err0; /* Construct challenge response packet. */ memcpy(packetbuf, keys, 96); packetbuf[96] = (uint8_t)namelen; memcpy(packetbuf + 97, name, namelen); /* Append hmac. */ prefixbuf[0] = NETPACKET_REGISTER_CHA_RESPONSE; crypto_hash_data_key_2(register_key, 32, prefixbuf, 1, packetbuf, 97 + namelen, &packetbuf[97 + namelen]); /* Send challenge response packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_REGISTER_CHA_RESPONSE, packetbuf, 129 + namelen, netpacket_op_packetsent, NPC)) goto err1; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Free temporary packet buffer. */ free(packetbuf); /* Success! */ return (0); err1: free(packetbuf); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/netpacket_transaction.c000066400000000000000000000227471476705112600225450ustar00rootroot00000000000000#include #include #include "crypto.h" #include "netpacket_internal.h" #include "netproto.h" #include "sysendian.h" #include "warnp.h" #include "netpacket.h" /** * netpacket_transaction_getnonce(NPC, machinenum, callback): * Construct and send a NETPACKET_TRANSACTION_GETNONCE packet asking to get * a transaction server nonce. */ int netpacket_transaction_getnonce(NETPACKET_CONNECTION * NPC, uint64_t machinenum, handlepacket_callback * callback) { uint8_t packetbuf[8]; /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_GETNONCE, packetbuf, 8, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_transaction_start(NPC, machinenum, operation, snonce, cnonce, * state, callback): * Construct and send a NETPACKET_TRANSACTION_START packet asking to * start a transaction; the transaction is a write transaction if * ${operation} is 0, a delete transaction if ${operation} is 1, a fsck * transaction with the delete key if ${operation} is 2, or a fsck * transaction with the write key if ${operation} is 3. */ int netpacket_transaction_start(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t operation, const uint8_t snonce[32], const uint8_t cnonce[32], const uint8_t state[32], handlepacket_callback * callback) { uint8_t packetbuf[137]; int key; /* Look up the key which is used to sign this packet. */ switch (operation) { case 0: /* Write. */ case 3: /* Read-only fsck using the write key. */ key = CRYPTO_KEY_AUTH_PUT; break; case 1: /* Delete. */ case 2: /* Fsck. */ key = CRYPTO_KEY_AUTH_DELETE; break; default: warn0("Programmer error: " "Invalid operation in netpacket_transaction_start"); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = operation; memcpy(&packetbuf[9], snonce, 32); memcpy(&packetbuf[41], cnonce, 32); memcpy(&packetbuf[73], state, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_TRANSACTION_START, packetbuf, 105, key)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_START, packetbuf, 137, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_transaction_commit(NPC, machinenum, whichkey, nonce, callback): * Construct and send a NETPACKET_TRANSACTION_COMMIT packet asking to commit * a transaction; the packet is signed with the write access key if * ${whichkey} is 0, and with the delete access key if ${whichkey} is 1. */ int netpacket_transaction_commit(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t whichkey, const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t packetbuf[73]; int key; /* Look up the key which is used to sign this packet. */ switch (whichkey) { case 0: key = CRYPTO_KEY_AUTH_PUT; break; case 1: key = CRYPTO_KEY_AUTH_DELETE; break; default: warn0("Programmer error: " "Invalid key in netpacket_transaction_commit"); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = whichkey; memcpy(&packetbuf[9], nonce, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_TRANSACTION_COMMIT, packetbuf, 41, key)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_COMMIT, packetbuf, 73, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_transaction_checkpoint(NPC, machinenum, whichkey, ckptnonce, * nonce, callback): * Construct and send a NETPACKET_TRANSACTION_CHECKPOINT packet asking to * create a checkpoint in a write transaction. */ int netpacket_transaction_checkpoint(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t whichkey, const uint8_t ckptnonce[32], const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t packetbuf[105]; int key; /* Look up the key which is used to sign this packet. */ switch (whichkey) { case 0: key = CRYPTO_KEY_AUTH_PUT; break; case 1: key = CRYPTO_KEY_AUTH_DELETE; break; default: warn0("Programmer error: " "Invalid key in netpacket_transaction_commit"); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = whichkey; memcpy(&packetbuf[9], ckptnonce, 32); memcpy(&packetbuf[41], nonce, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_TRANSACTION_CHECKPOINT, packetbuf, 73, key)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_CHECKPOINT, packetbuf, 105, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_transaction_cancel(NPC, machinenum, whichkey, snonce, cnonce, * state, callback): * Construct and send a NETPACKET_TRANSACTION_CANCEL packet asking to cancel * a pending transaction if the state is correct. */ int netpacket_transaction_cancel(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t whichkey, const uint8_t snonce[32], const uint8_t cnonce[32], const uint8_t state[32], handlepacket_callback * callback) { uint8_t packetbuf[137]; int key; /* Look up the key which is used to sign this packet. */ switch (whichkey) { case 0: /* Write key. */ case 3: /* Write key and state = 0. */ key = CRYPTO_KEY_AUTH_PUT; break; case 1: /* Delete key. */ case 2: /* Delete key and state = 0. */ key = CRYPTO_KEY_AUTH_DELETE; break; default: warn0("Programmer error: " "Invalid operation in netpacket_transaction_cancel"); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = whichkey; memcpy(&packetbuf[9], snonce, 32); memcpy(&packetbuf[41], cnonce, 32); memcpy(&packetbuf[73], state, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_TRANSACTION_CANCEL, packetbuf, 105, key)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_CANCEL, packetbuf, 137, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_transaction_trycommit(NPC, machinenum, whichkey, nonce, * callback): * Construct and send a NETPACKET_TRANSACTION_TRYCOMMIT packet asking to * commit a transaction; the packet is signed with the write access key if * ${whichkey} is 0, and with the delete access key if ${whichkey} is 1. */ int netpacket_transaction_trycommit(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t whichkey, const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t packetbuf[73]; int key; /* Look up the key which is used to sign this packet. */ switch (whichkey) { case 0: key = CRYPTO_KEY_AUTH_PUT; break; case 1: key = CRYPTO_KEY_AUTH_DELETE; break; default: warn0("Programmer error: " "Invalid key in netpacket_transaction_trycommit"); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = whichkey; memcpy(&packetbuf[9], nonce, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_TRANSACTION_TRYCOMMIT, packetbuf, 41, key)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_TRYCOMMIT, packetbuf, 73, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_transaction_ischeckpointed(NPC, machinenum, whichkey, nonce, * callback): * Construct and send a NETPACKET_TRANSACTION_ISCHECKPOINTED packet asking if * a checkpointed write transaction is in progress; the packet is signed with * the write access key if ${whichkey} is 0, and with the delete access key * if ${whichkey} is 1. */ int netpacket_transaction_ischeckpointed(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t whichkey, const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t packetbuf[73]; int key; /* Look up the key which is used to sign this packet. */ switch (whichkey) { case 0: key = CRYPTO_KEY_AUTH_PUT; break; case 1: key = CRYPTO_KEY_AUTH_DELETE; break; default: warn0("Programmer error: " "Invalid key in netpacket_transaction_ischeckpointed"); goto err0; } /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = whichkey; memcpy(&packetbuf[9], nonce, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_TRANSACTION_ISCHECKPOINTED, packetbuf, 41, key)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_TRANSACTION_ISCHECKPOINTED, packetbuf, 73, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/netpacket_write.c000066400000000000000000000051471476705112600213450ustar00rootroot00000000000000#include #include #include #include "crypto.h" #include "netpacket_internal.h" #include "netproto.h" #include "sysendian.h" #include "warnp.h" #include "netpacket.h" /** * netpacket_write_fexist(NPC, machinenum, class, name, nonce, callback): * Construct and send a NETPACKET_WRITE_FEXIST packet asking if the * specified file exists. */ int netpacket_write_fexist(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t class, const uint8_t name[32], const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t packetbuf[105]; /* Construct the packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = class; memcpy(&packetbuf[9], name, 32); memcpy(&packetbuf[41], nonce, 32); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_WRITE_FEXIST, packetbuf, 73, CRYPTO_KEY_AUTH_PUT)) goto err0; /* Send the packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_WRITE_FEXIST, packetbuf, 105, netpacket_op_packetsent, NPC)) goto err0; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netpacket_write_file(NPC, machinenum, class, name, buf, buflen, * nonce, callback): * Construct and send a NETPACKET_WRITE_FILE packet asking to write the * specified file. */ int netpacket_write_file(NETPACKET_CONNECTION * NPC, uint64_t machinenum, uint8_t class, const uint8_t name[32], const uint8_t * buf, size_t buflen, const uint8_t nonce[32], handlepacket_callback * callback) { uint8_t * packetbuf; /* Sanity-check file size. */ if (buflen > 262144) { warn0("file of class %c too large: (%zu > %zu)", class, buflen, (size_t)262144); goto err0; } /* Allocate space for constructing packet. */ if ((packetbuf = malloc(109 + buflen)) == NULL) goto err0; /* Construct packet. */ be64enc(&packetbuf[0], machinenum); packetbuf[8] = class; memcpy(&packetbuf[9], name, 32); memcpy(&packetbuf[41], nonce, 32); be32enc(&packetbuf[73], (uint32_t)buflen); memcpy(&packetbuf[77], buf, buflen); /* Append hmac. */ if (netpacket_hmac_append(NETPACKET_WRITE_FILE, packetbuf, 77 + buflen, CRYPTO_KEY_AUTH_PUT)) goto err1; /* Send packet. */ if (netproto_writepacket(NPC->NC, NETPACKET_WRITE_FILE, packetbuf, 109 + buflen, netpacket_op_packetsent, NPC)) goto err1; /* Set callback for handling a response. */ NPC->pending_current->handlepacket = callback; /* Free packet construction buffer. */ free(packetbuf); /* Success! */ return (0); err1: free(packetbuf); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netpacket/packets.txt000066400000000000000000000365501476705112600202060ustar00rootroot00000000000000Tarsnap client-server protocol packets ====================================== Unless specified otherwise, all values are stored as big-endian integers. HMACW(X) = HMAC(, X) HMACR(X) = HMAC(, X) HMACD(X) = HMAC(, X) In any single connection, the following operation types cannot be mixed: * Registering a machine. * Reading files. * Listing files. * Transactions (start/commit/write file/delete file, etc.) In addition, a NETPACKET_WRITE_FEXIST_REQUEST may not be sent while any other requests are pending on the same connection. NETPACKET_REGISTER_REQUEST (0x00) --------------------------------- A new machine wants to register with the server. Packet contents: char user[] The string user[] must be no more than 255 bytes, and is not NUL terminated. The server will respond with a NETPACKET_REGISTER_CHALLENGE packet or a NETPACKET_REGISTER_RESPONSE packet specifying "no such user". NETPACKET_REGISTER_CHALLENGE (0x80) ----------------------------------- As part of the machine registration process, the server needs to verify that the machine has the user password. Packet contents: uint8_t salt[32] uint8_t serverpub[CRYPTO_DH_PUBLEN] The client must compute DH parameters (priv, pub) from the salt and password, and then compute K = serverpub^priv. It must then send back a NETPACKET_REGISTER_CHA_RESPONSE packet. NOTE: The value serverpub[] expires when the network protocol connection is closed, so the NETPACKET_REGISTER_CHA_RESPONSE packet MUST be sent as part of the same connection. NETPACKET_REGISTER_CHA_RESPONSE (0x01) -------------------------------------- As part of the machine registration process, the new machine needs to prove that it holds the user password, and send its access keys and user-friendly name. Packet contents: uint8_t key_put[32] uint8_t key_get[32] uint8_t key_delete[32] uint8_t namelen char name[namelen] uint8_t hmac[32] The string name[namelen] is not NUL terminated. The value hmac is HMAC(SHA256(K), 0x01 || [the packet minus the final hmac]), where K is as described under NETPACKET_REGISTER_CHALLENGE. The server will respond with a NETPACKET_REGISTER_RESPONSE packet. NETPACKET_REGISTER_RESPONSE (0x81) ---------------------------------- As part of the machine registration process, the server needs to confirm that the machine is registered and provide the registration number; or inform the client that the user or password is incorrect. Packet contents: uint8_t status uint64_t machinenum uint8_t hmac[32] The value status is 0 Success; machinenum is the machine number 1 No such user 2 Incorrect hmac (i.e., password is wrong) 3 Account balance is not positive. The value machinenum is (uint64_t)(-1) if status is non-zero. The value hmac is HMAC(SHA256(K), 0x81 || [the packet minus the final hmac]), where K is as described under NETPACKET_REGISTER_CHALLENGE, if status is 0 or 3; or zero otherwise. The server will close the connection after sending this packet. NETPACKET_TRANSACTION_GETNONCE (0x10) ------------------------------------- Packet contents: uint64_t machinenum NETPACKET_TRANSACTION_GETNONCE_RESPONSE (0x90) ---------------------------------------------- Packet contents: uint8_t snonce[32] NOTE: This server nonce becomes invalid as soon as (a) the network protocol connection is closed, or (b) a transaction is started by the machine for which this nonce is being provided. NETPACKET_TRANSACTION_START (0x11) ---------------------------------- Packet contents: uint64_t machinenum uint8_t operation uint8_t snonce[32] uint8_t cnonce[32] uint8_t state[32] uint8_t hmac[32] The value operation is 0 Write transaction (key = write access key) 1 Delete transaction (key = delete access key) 2 Fsck transaction (key = delete access key, state = 0) 3 Fsck transaction w/o pruning (key = write access key, state = 0). The value snonce is as provided by the server (see above); the value cnonce is a random client nonce; the value state is the transaction nonce of the last committed transaction, or 32 zero bytes if this is the first transaction by this machine or if this is a fsck transaction. The value hmac is HMAC(key, 0x11 || [packet minus final hmac]). The transaction nonce for the new transaction is SHA256(snonce || cnonce). NETPACKET_TRANSACTION_START_RESPONSE (0x91) ------------------------------------------- Packet contents: uint8_t status uint8_t hmac[32] The value status is 0 Success 1 Bad state (run --fsck) 2 Account balance is not positive. A status value of 2 will only ever be sent in response to an attempt to start a write transaction. The value hmac is HMAC(key, 0x91 || nonce || status) where key is the write or delete key as in NETPACKET_TRANSACTION_START and nonce is the transaction nonce as described above. NETPACKET_TRANSACTION_COMMIT (0x12) ----------------------------------- Packet contents: uint64_t machinenum uint8_t whichkey uint8_t nonce[32] uint8_t hmac[32] The value whichkey is 0 key = write access key 1 key = delete access key. The value nonce is the transaction nonce for the transaction which the client wants to have committed. The value hmac is HMAC(key, 0x12 || [packet minus final hmac]). NETPACKET_TRANSACTION_COMMIT_RESPONSE (0x92) -------------------------------------------- Packet contents: uint8_t hmac[32] The value hmac is HMAC(key, 0x92 || nonce) where key is the write or delete key as in NETPACKET_TRANSACTION_COMMIT. Note that this packet does not indicate that the requested transaction was committed; only that IF the requested transaction was the most recent non-committed transaction, THEN it has been committed. NETPACKET_TRANSACTION_CHECKPOINT (0x13) --------------------------------------- Mark a "checkpoint" in a write transaction. When committing a write transaction, files are omitted from the transaction if (a) they were uploaded after the last checkpoint, or (b) they were uploaded prior to the penultimate checkpoint and have class 'i' or 'm'. Packet contents: uint64_t machinenum uint8_t whichkey uint8_t ckptnonce[32] uint8_t nonce[32] uint8_t hmac[32] The value whichkey is 0 key = write access key 1 key = delete access key. Note that while checkpoints only exist in write transactions, a checkpoint request can be signed with a delete access key in the "log checkpoint creation; crash; run tarsnap -d" case. The value ckptnonce is a random nonce; if a checkpoint request is replayed, the ckptnonce value must be identical. The value nonce is the transaction nonce of the current write transaction. The value hmac is HMAC(0x13 || [packet minute final hmac]). NETPACKET_TRANSACTION_CHECKPOINT_RESPONSE (0x93) ------------------------------------------------ Packet contents: uint8_t status uint8_t ckptnonce[32] uint8_t hmac[32] The value status means: 0 Success 1 Transaction nonce is incorrect. The value hmac is HMAC(key, 0x93 || nonce || [packet minus final hmac]), where nonce is the nonce provided by the client (which may not be the nonce of the current transaction, if status == 1), and key is the write or delete key as in NETPACKET_TRANSACTION_CHECKPOINT. NETPACKET_TRANSACTION_CANCEL (0x14) ----------------------------------- The client wants to cancel any pending transaction as with TRANSACTION_START, but not actually start a transaction (yet). Packet contents: uint64_t machinenum uint8_t whichkey uint8_t snonce[32] uint8_t cnonce[32] uint8_t state[32] uint8_t hmac[32] The value whichkey is 0 key = write access key 1 key = delete access key 2 key = delete access key and state = 0 3 key = write access key and state = 0. The value snonce is as provided by the server in response to a NETPACKET_TRANSACTION_GETNONCE request; the value cnonce is a random client nonce; the value state is the transaction nonce of the last committed transaction. The value hmac is HMAC(key, 0x14 || [packet minus final hmac]). NETPACKET_TRANSACTION_CANCEL_RESPONSE (0x94) ------------------------------------------- Packet contents: uint8_t status uint8_t hmac[32] The value status is 0 Success 1 Try again later A "success" here means that *if* the client's state value was correct *then* there is currently no transaction in progress. A "try again later" does not guarantee anything except that the server is alive and responsive; in particular, the server may cancel the currently in-progress transaction but still respond "try again later". The value hmac is HMAC(key, 0x94 || nonce || status) where key is the write or delete key as in NETPACKET_TRANSACTION_CANCEL and nonce is SHA256(snonce || cnonce). NETPACKET_TRANSACTION_TRYCOMMIT (0x15) -------------------------------------- Packet contents: uint64_t machinenum uint8_t whichkey uint8_t nonce[32] uint8_t hmac[32] The value whichkey is 0 key = write access key 1 key = delete access key. The value nonce is the transaction nonce for the transaction which the client wants to have committed. The value hmac is HMAC(key, 0x15 || [packet minus final hmac]). NETPACKET_TRANSACTION_TRYCOMMIT_RESPONSE (0x95) ----------------------------------------------- Packet contents: uint8_t status uint8_t hmac[32] The value status is 0 Success 1 Try again later A "success" here means that *if* the specified transaction was the most recent non-committed transaction, *then* it has been committed. A "try again later" guarantees nothing except that the server is alive and responsive; in particular, the server may commit the transaction in question but still respond "try again later". The value hmac is HMAC(key, 0x95 || nonce || status) where key is the write or delete key as in NETPACKET_TRANSACTION_TRYCOMMIT. NETPACKET_TRANSACTION_ISCHECKPOINTED (0x16) ------------------------------------------- Packet contents: uint64_t machinenum uint8_t whichkey uint8_t nonce[32] uint8_t hmac[32] The value whichkey is 0 key = write access key 1 key = delete access key. The value nonce is a random 256-bit operation nonce. The value hmac is HMAC(key, 0x16 || [packet minus final hmac]). NETPACKET_TRANSACTION_ISCHECKPOINTED_RESPONSE (0x96) ---------------------------------------------------- Packet contents: uint8_t status uint8_t tnonce[32] uint8_t hmac[32] The value status is 0 No transaction is in progress, the transaction in progress is a delete transaction, or the write transaction in progress does not have any checkpoints. 1 A write transaction is in progress and has a checkpoint. 2 "Reply hazy, try again" -- Magic 8-Ball The value tnonce is the transaction nonce of the current transaction if status is 1; and zero otherwise. The value hmac is HMAC(key, 0x96 || nonce || [packet minus final hmac]) where key is the write or delete key as in NETPACKET_TRANSACTION_ISCHECKPOINTED. NETPACKET_WRITE_FEXIST (0x20) ----------------------------- Packet contents: uint64_t machinenum uint8_t class uint8_t name[32] uint8_t nonce[32] uint8_t hmac[32] The value nonce is the transaction nonce of the current transaction. The value hmac is HMACW(0x20 || [packet minus final hmac]). NETPACKET_WRITE_FEXIST_RESPONSE (0xa0) -------------------------------------- Packet contents: uint8_t status uint8_t class uint8_t name[32] uint8_t hmac[32] The value status means: 0 File 'name' does not exist in class 'class'. 1 File 'name' exists in class 'class'. 2 Transaction nonce is incorrect. The value hmac is HMACW(0xa0 || nonce || [packet minus final hmac]), where nonce is the nonce provided by the client (which may not be the nonce of the current transaction). NETPACKET_WRITE_FILE (0x21) --------------------------- Packet contents: uint64_t machinenum uint8_t class uint8_t name[32] uint8_t nonce[32] uint32_t filelen uint8_t data[filelen] uint8_t hmac[32] The value nonce is the transaction nonce of the current transaction. The value filelen is at most 262144 (2^18). The value hmac is HMACW(0x20 || [packet minus final hmac]). NETPACKET_WRITE_FILE_RESPONSE (0xa1) ------------------------------------ Packet contents: uint8_t status uint8_t class uint8_t name[32] uint8_t hmac[32] The value status is 0 Success 1 The specified file already exists 2 Transaction nonce is incorrect. The value hmac is HMACW(0xa1 || nonce || [packet minus final hmac]), where nonce is the nonce provided by the client (which may not be the nonce of the current transaction). NETPACKET_DELETE_FILE (0x30) ---------------------------- Packet contents: uint64_t machinenum uint8_t class uint8_t name[32] uint8_t nonce[32] uint8_t hmac[32] The value nonce is the transaction nonce of the current transaction. The value hmac is HMACD(0x30 || [packet minus final hmac]). NETPACKET_DELETE_FILE_RESPONSE (0xb0) ------------------------------------- Packet contents: uint8_t status uint8_t class uint8_t name[32] uint8_t hmac[32] The value status is 0 Success 1 The specified file does not exist 2 Transaction nonce is incorrect. The value hmac is HMACD(0xb0 || nonce || [packet minus final hmac]), where nonce is the nonce provided by the client (which may not be the nonce of the current transaction). NETPACKET_READ_FILE (0x40) -------------------------- Packet contents: uint64_t machinenum uint8_t class uint8_t name[32] uint32_t size uint8_t hmac[32] The value size is the expected file size (at most 262144), or (uint32_t)(-1) if the file size is unknown. The value hmac is HMACR(0x40 || [packet minus final hmac]). NETPACKET_READ_FILE_RESPONSE (0xc0) ----------------------------------- Packet contents: uint8_t status uint8_t class uint8_t name[32] uint32_t filelen uint8_t data[filelen or 0] uint8_t hmac[32] The value status is 0 Success 1 File does not exist 2 File size is incorrect 3 Account balance is not positive. The value filelen is the actual size of the file (possibly not equal to the size field of the NETPACKET_READ_FILE packet); or 0 if status is 1 or 3. The field data[] contains the file if status is 0; otherwise, the field is omitted. The value hmac is HMACR(0xc0 || [packet minus final hmac]). NETPACKET_DIRECTORY (0x50) -------------------------- Packet contents: uint64_t machinenum uint8_t class uint8_t start[32] uint8_t snonce[32] uint8_t cnonce[32] uint8_t hmac[32] The value class is the class for which a directory listing is desired. The value start is the least value which should be returned by the server. The value snonce is as provided by the server (as in transaction starting); the value cnonce is a random client nonce. The value hmac is HMACR(0x50 || [packet minus final hmac]). NOTE: The server will respond with one or more NETPACKET_DIRECTORY_RESPONSE packets. NETPACKET_DIRECTORY_D (0x51) ---------------------------- This is identical to a NETPACKET_DIRECTORY packet, except that hmac is HMACD(0x51 || [packet minus final hmac]). NETPACKET_DIRECTORY_RESPONSE (0xd0) ----------------------------------- Packet contents: uint8_t status uint8_t class uint8_t start[32] uint32_t nfiles uint8_t flist[nfiles * 32] uint8_t hmac[32] The value status is 0 There are no more files after this 1 More packets to come 2 There might be more files after this; send another NETPACKET_DIRECTORY packet to request them 3 Account balance is not positive. A status value of 3 will only ever be sent in response to a DIRECTORY request, not to a DIRECTORY_D request. The files returned are the first nfiles files after start in lexicographical order. The value hmac is HMACR(0xd0 || nonce || [packet minus final hmac]), where nonce is SHA256(snonce || cnonce); or HMACD(...) if the packet is being sent in response to a NETPACKET_DIRECTORY_D packet. tarsnap-1.0.41/lib/netproto/000077500000000000000000000000001476705112600156765ustar00rootroot00000000000000tarsnap-1.0.41/lib/netproto/netproto.c000066400000000000000000000137361476705112600177260ustar00rootroot00000000000000#include #include #include #include #include "crypto.h" #include "tsnetwork.h" #include "warnp.h" #include "netproto.h" #include "netproto_internal.h" static network_callback callback_sleep; /** * netproto_printerr_internal(status): * Print the error message associated with the given status code. */ void netproto_printerr_internal(int status) { switch (status) { case NETWORK_STATUS_CONNERR: /* Could not connect. */ warn0("Error connecting to server"); break; case NETWORK_STATUS_ERR: /* Error is specified in errno. */ warnp("Network error"); break; case NETWORK_STATUS_NODATA: case NETWORK_STATUS_TIMEOUT: /* Server timed out. */ warn0("Timeout communicating with server"); break; case NETWORK_STATUS_CTIMEOUT: /* Server timed out. */ warn0("Timeout connecting to server"); break; case NETWORK_STATUS_CLOSED: /* Server closed connection. */ warn0("Connection closed by server"); break; case NETWORK_STATUS_CANCEL: /* Operation cancelled; no error message. */ break; case NETPROTO_STATUS_PROTERR: /* Protocol violation by server. */ warn0("Network protocol violation by server"); break; } } /** * netproto_alloc(callback, cookie): * Allocate a network protocol connection cookie. If the connection is closed * before netproto_setfd is called, netproto_close will call callback(cookie) * in lieu of performing callback cancels on a socket. */ struct netproto_connection_internal * netproto_alloc(int (* callback)(void *), void * cookie) { struct netproto_connection_internal * C; /* Allocate memory. */ if ((C = malloc(sizeof(struct netproto_connection_internal))) == NULL) goto err0; /* Record connect-cancel callback and cookie. */ C->cancel = callback; C->cookie = cookie; /* We have no state yet. */ C->fd = -1; C->keys = NULL; C->sleepcookie.handle = -1; C->bytesin = C->bytesout = C->bytesqueued = 0; C->broken = 0; C->Q = NULL; /* Success! */ return (C); err0: /* Failure! */ return (NULL); } /** * netproto_setfd(C, fd): * Set the network protocol connection cookie ${C} to use connected socket * ${fd}. This function must be called exactly once after netproto_alloc * before calling any other functions aside from netproto_free. */ int netproto_setfd(struct netproto_connection_internal * C, int fd) { /* The connect is no longer pending. */ C->cancel = NULL; C->cookie = NULL; /* We have a file descriptor. */ C->fd = fd; /* Create a network layer write queue. */ if ((C->Q = network_writeq_init(fd)) == NULL) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * netproto_getstats(C, in, out, queued): * Obtain the number of bytes received and sent via the connection, and the * number of bytes ${queued} to be written. */ void netproto_getstats(NETPROTO_CONNECTION * C, uint64_t * in, uint64_t * out, uint64_t * queued) { *in = C->bytesin; *out = C->bytesout; *queued = C->bytesqueued; } /** * netproto_sleep(C, secs, callback, cookie): * Call the provided callback after ${secs} seconds. */ int netproto_sleep(NETPROTO_CONNECTION * C, int secs, network_callback * callback, void * cookie) { struct timeval timeo; /* Set timeout. */ timeo.tv_sec = secs; timeo.tv_usec = 0; /* Make sure this connection isn't already sleeping. */ if (C->sleepcookie.handle != -1) { warn0("Connection is already sleeping!"); goto err0; } /* Record callback parameters. */ C->sleepcookie.callback = callback; C->sleepcookie.cookie = cookie; /* Ask for a wake-up call. */ if ((C->sleepcookie.handle = network_sleep(&timeo, callback_sleep, C)) == -1) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * callback_sleep(cookie, status): * Helper function for netproto_sleep. */ static int callback_sleep(void * cookie, int status) { NETPROTO_CONNECTION * C = cookie; /* * Our wake-up call is happening right now; record that there is no * pending callback so that we don't try to deregister it when the * connection is closed later. */ C->sleepcookie.handle = -1; /* Call the requested callback. */ return ((C->sleepcookie.callback)(C->sleepcookie.cookie, status)); } /** * netproto_flush(C): * Cancel all pending writes and any in-progress read. */ int netproto_flush(NETPROTO_CONNECTION * C) { int rc, rc2; /* Cancel pending writes. */ if (C->Q != NULL) rc = network_writeq_cancel(C->Q); else rc = 0; /* * Mark this connection as being broken. The upstream caller should * never try to write any packets after calling netproto_flush -- * this allows us to detect and print a warning if it does. */ C->broken = 1; /* Cancel any in-progress read. */ if (C->fd != -1) { rc2 = network_deregister(C->fd, NETWORK_OP_READ); if (rc == 0) rc = rc2; } /* Return success or the first nonzero callback value. */ return (rc); } /** * netproto_close(C): * Cancel all pending writes and any in-progress read, and free memory. */ int netproto_close(NETPROTO_CONNECTION * C) { int rc, rc2; /* If we were connecting, cancel that. */ if (C->cancel != NULL) rc = (C->cancel)(C->cookie); else rc = 0; /* Cancel pending writes. */ if (C->Q != NULL) { rc2 = network_writeq_cancel(C->Q); rc = rc ? rc : rc2; } /* Free the write queue. */ if (C->Q != NULL) network_writeq_free(C->Q); /* Cancel any in-progress read. */ if (C->fd != -1) { rc2 = network_deregister(C->fd, NETWORK_OP_READ); rc = rc ? rc : rc2; } /* Free cryptographic keys, if any exist. */ crypto_session_free(C->keys); /* Close the socket. */ while (C->fd != -1 && close(C->fd)) { if (errno == ECONNRESET) { /* * You can't dump me! I'm dumping you! We don't * care about the connection dying since we're * done with it anyway. */ break; } if (errno != EINTR) { warnp("close()"); goto err1; } } /* Free the network protocol cookie. */ free(C); /* Return success or the first nonzero callback value. */ return (rc); err1: free(C); /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netproto/netproto.h000066400000000000000000000046221476705112600177250ustar00rootroot00000000000000#ifndef NETPROTO_H_ #define NETPROTO_H_ #include #include #include "warnp.h" #include "tsnetwork.h" typedef struct netproto_connection_internal NETPROTO_CONNECTION; /* callback status values. */ #define NETPROTO_STATUS_PROTERR \ (NETWORK_STATUS_MAX + 1) /* Protocol error. */ /** * netproto_printerr_internal(status): * Print the error message associated with the given status code. */ void netproto_printerr_internal(int); #define netproto_printerr(x) do { \ warnline; \ netproto_printerr_internal(x); \ } while (0) /** * netproto_connect(useragent, callback, cookie): * Create a socket, connect to the tarsnap server, and perform the necessary * key exchange. Return a network protocol connection cookie; note that * this cookie must not be used until the callback is called. */ NETPROTO_CONNECTION * netproto_connect(const char *, network_callback *, void *); /** * netproto_writepacket(C, type, buf, buflen, callback, cookie): * Write the provided packet to the connection. When complete, call * callback(cookie, status), where status is a NETPROTO_STATUS_* value. */ int netproto_writepacket(NETPROTO_CONNECTION *, uint8_t, const uint8_t *, size_t, network_callback *, void *); /** * netproto_readpacket(C, callback_getbuf, callback_done, cookie): * Read a packet from the connection. Once the type and length of the * packet is known, call callback_getbuf(cookie, type, buf, buflen); once * the packet is read or fails, call callback_done(cookie, status), where * status is a NETPROTO_STATUS_* value. */ int netproto_readpacket(NETPROTO_CONNECTION *, int(void *, uint8_t, uint8_t **, size_t), network_callback *, void *); /** * netproto_getstats(C, in, out, queued): * Obtain the number of bytes received and sent via the connection, and the * number of bytes ${queued} to be written. */ void netproto_getstats(NETPROTO_CONNECTION *, uint64_t *, uint64_t *, uint64_t *); /** * netproto_sleep(C, secs, callback, cookie): * Call the provided callback after ${secs} seconds. */ int netproto_sleep(NETPROTO_CONNECTION *, int, network_callback *, void *); /** * netproto_flush(C): * Cancel all pending writes and any in-progress read. */ int netproto_flush(NETPROTO_CONNECTION *); /** * netproto_close(C): * Cancel all pending writes and any in-progress read, and free memory. */ int netproto_close(NETPROTO_CONNECTION *); #endif /* !NETPROTO_H_ */ tarsnap-1.0.41/lib/netproto/netproto_connect.c000066400000000000000000000114651476705112600214340ustar00rootroot00000000000000#include "platform.h" #include #include #include #include #include #include #include "netproto_internal.h" #include "network.h" #include "sock.h" #include "sock_util.h" #include "tsnetwork.h" #include "warnp.h" #include "netproto.h" struct netproto_connect_cookie { struct sock_addr ** sas; char * useragent; network_callback * callback; void * cookie; void * connect_cookie; NETPROTO_CONNECTION * NC; }; static struct sock_addr ** srv_addr = NULL; static void netproto_connect_atexit(void); static void netproto_connect_atexit(void) { sock_addr_freelist(srv_addr); } static int callback_connect(void * cookie, int s) { struct netproto_connect_cookie * C = cookie; int rc; /* The connect is no longer pending. */ C->connect_cookie = NULL; /* Did the connection attempt fail? */ if (s == -1) { /* * Call the upstream callback. Upon being informed that the * connect has failed, the upstream code is responsible for * calling netproto_close, which (since we haven't called * netproto_setfd yet) will call into callback_cancel and let * us clean up. */ return ((C->callback)(C->cookie, NETWORK_STATUS_CONNERR)); } /* Inform the netproto code that we have a socket. */ if (netproto_setfd(C->NC, s)) goto err2; /* Perform key exchange. */ if (netproto_keyexchange(C->NC, C->useragent, C->callback, C->cookie)) goto err1; /* Free the cookie. */ sock_addr_freelist(C->sas); free(C->useragent); free(C); /* Success! */ return (0); err2: /* Drop the socket since we can't use it properly. */ if (close(s)) warnp("close"); err1: /* Call the upstream callback. */ rc = (C->callback)(C->cookie, NETWORK_STATUS_ERR); /* * We've called netproto_setfd, so callback_cancel won't happen; we * are responsible for cleaning up after ourselves. */ sock_addr_freelist(C->sas); free(C->useragent); free(C); /* Return value from user callback. */ return (rc); } static int callback_cancel(void * cookie) { struct netproto_connect_cookie * C = cookie; int rc; /* Cancel the connection attempt if still pending. */ if (C->connect_cookie != NULL) network_connect_cancel(C->connect_cookie); /* We were cancelled. */ rc = (C->callback)(C->cookie, NETWORK_STATUS_CANCEL); /* Free our cookie. */ sock_addr_freelist(C->sas); free(C->useragent); free(C); /* Return value from user callback. */ return (rc); } static struct sock_addr ** getserveraddr(void) { static time_t srv_time = (time_t)(-1); struct sock_addr ** tmp_addr; time_t tmp_time; /* * If we haven't done a DNS lookup already, or our cached value is * more than 60 seconds old, do a DNS lookup. */ tmp_time = time(NULL); if ((srv_time == (time_t)(-1)) || (tmp_time > srv_time + 60)) { tmp_addr = sock_resolve(TSSERVER "-server.tarsnap.com:9279"); if (tmp_addr == NULL) { if (srv_addr != NULL) warn0("Using cached DNS lookup"); else warn0("Cannot obtain server address"); } } else { tmp_addr = NULL; } /* If we have a new lookup, update the cache. */ if (tmp_addr != NULL) { /* First time we have an address to cache, register atexit. */ if ((srv_addr == NULL) && (atexit(netproto_connect_atexit))) { warnp("Could not initialize atexit"); return (NULL); } /* Update the cache. */ sock_addr_freelist(srv_addr); srv_addr = tmp_addr; srv_time = tmp_time; } /* Return a duplicate of the cached value. */ return (srv_addr ? sock_addr_duplist(srv_addr) : NULL); } /** * netproto_connect(useragent, callback, cookie): * Create a socket, connect to the tarsnap server, and perform the necessary * key exchange. Return a network protocol connection cookie; note that * this cookie must not be used until the callback is called. */ NETPROTO_CONNECTION * netproto_connect(const char * useragent, network_callback * callback, void * cookie) { struct netproto_connect_cookie * C; struct timeval timeo; /* Create a cookie to be passed to callback_connect. */ if ((C = malloc(sizeof(struct netproto_connect_cookie))) == NULL) goto err0; if ((C->useragent = strdup(useragent)) == NULL) goto err1; C->callback = callback; C->cookie = cookie; /* Look up the server's IP address. */ if ((C->sas = getserveraddr()) == NULL) goto err2; /* Try to connect to server, waiting up to 5 seconds per address. */ timeo.tv_sec = 5; timeo.tv_usec = 0; if ((C->connect_cookie = network_connect_timeo(C->sas, &timeo, callback_connect, C)) == NULL) { netproto_printerr(NETWORK_STATUS_CONNERR); goto err3; } /* Create a network protocol connection cookie. */ if ((C->NC = netproto_alloc(callback_cancel, C)) == NULL) goto err4; /* Success! */ return (C->NC); err4: network_connect_cancel(C->connect_cookie); err3: sock_addr_freelist(C->sas); err2: free(C->useragent); err1: free(C); err0: /* Failure! */ return (NULL); } tarsnap-1.0.41/lib/netproto/netproto_internal.h000066400000000000000000000030161476705112600216150ustar00rootroot00000000000000#ifndef NETPROTO_INTERNAL_H_ #define NETPROTO_INTERNAL_H_ #include #include "crypto.h" #include "tsnetwork.h" struct netproto_connection_internal { int (* cancel)(void *); void * cookie; int fd; NETWORK_WRITEQ * Q; CRYPTO_SESSION * keys; struct sleepcookie { int handle; network_callback * callback; void * cookie; } sleepcookie; uint64_t bytesin; uint64_t bytesout; uint64_t bytesqueued; int broken; }; /** * netproto_alloc(callback, cookie): * Allocate a network protocol connection cookie. If the connection is closed * before netproto_setfd is called, netproto_close will call callback(cookie) * in lieu of performing callback cancels on a socket. */ struct netproto_connection_internal * netproto_alloc(int (*)(void *), void *); /** * netproto_setfd(C, fd): * Set the network protocol connection cookie ${C} to use connected socket * ${fd}. This function must be called exactly once after netproto_alloc * before calling any other functions aside from netproto_free. */ int netproto_setfd(struct netproto_connection_internal *, int); /** * netproto_keyexchange(C, useragent, callback, cookie): * Perform protocol negotiation and key exchange with the tarsnap server * on the newly opened connection with cookie ${C}. When the negotiation * is complete or has failed, call callback(cookie, status) where status is * a NETPROTO_STATUS_* value. */ int netproto_keyexchange(struct netproto_connection_internal *, const char *, network_callback *, void *); #endif /* !NETPROTO_INTERNAL_H_ */ tarsnap-1.0.41/lib/netproto/netproto_keyexchange.c000066400000000000000000000265231476705112600222770ustar00rootroot00000000000000#include #include #include #include "crypto_dh.h" #include "ctassert.h" #include "warnp.h" #include "crypto.h" #include "netproto_internal.h" #include "tsnetwork.h" #include "netproto.h" struct keyexchange_internal { char * useragent; uint8_t useragentlen; network_callback * callback; void * cookie; NETPROTO_CONNECTION * C; struct timeval timeout; size_t refcount; uint8_t serverproto; uint8_t serverparams[CRYPTO_DH_PUBLEN + 256 + 32]; uint8_t pub[CRYPTO_DH_PUBLEN]; uint8_t priv[CRYPTO_DH_PRIVLEN]; uint8_t mkey[48]; uint8_t clientproof[32]; uint8_t serverproof[32]; }; static int docallback(struct keyexchange_internal *, int); static network_callback proto_sent; static network_callback proto_received; static network_callback namelen_sent; static network_callback name_sent; static network_callback dh_received; static network_callback dh_sent; static network_callback proof_sent; static network_callback proof_received; /** * docallback(C, status): * Call ${C->callback} if it is non-NULL; and set it to NULL to make sure * that we don't call it again later. */ static int docallback(struct keyexchange_internal * KC, int status) { network_callback * cb = KC->callback; /* We don't want to call this again. */ KC->callback = NULL; /* If we haven't called it before, call it now. */ if (cb != NULL) return ((cb)(KC->cookie, status)); else return (0); } /** * Connection negotiation and key exchange protocol: * Client Server * Protocol version (== 0; 1 byte) -> * <- Protocol version (== 0; 1 byte) * namelen (1 -- 255; 1 byte) -> * User-agent name (namelen bytes) -> * <- 2^x mod p (CRYPTO_DH_PUBLEN bytes) * <- RSA-PSS(2^x mod p) (256 bytes) * <- nonce (random; 32 bytes) * 2^y mod p (CRYPTO_DH_PUBLEN bytes) -> * C_auth(mkey) (32 bytes) -> * <- S_auth(mkey) (32 bytes) * * Both sides compute K = 2^(xy) mod p. * Shared "master" key is mkey = MGF1(nonce || K, 48). * Server encryption key is S_encr = HMAC(mkey, "S_encr"). * Server authentication key is S_auth = HMAC(mkey, "S_auth"). * Client keys C_encr and C_auth are generated in the same way. * * This is cryptographically similar to SSL where the server has an * RSA_DH certificate, except that the client random is omitted (it is * unnecessary given that the client provides 256 bits of entropy via * its choice of 2^y mod p). */ static uint8_t protovers = 0; /** * netproto_keyexchange(C, useragent, callback, cookie): * Perform protocol negotiation and key exchange with the tarsnap server * on the newly opened connection with cookie ${C}. When the negotiation * is complete or has failed, call callback(cookie, status) where status is * a NETPROTO_STATUS_* value. */ int netproto_keyexchange(NETPROTO_CONNECTION * C, const char * useragent, network_callback * callback, void * cookie) { struct keyexchange_internal * KC; size_t useragentlen = strlen(useragent); /* Sanity-check user-agent string. */ if ((useragentlen < 1) || (useragentlen > 255)) { warn0("Programmer error: " "User-agent string has invalid length (%zu): %s", useragentlen, useragent); goto err0; } /* Create keyexchange cookie. */ if ((KC = malloc(sizeof(struct keyexchange_internal))) == NULL) goto err0; if ((KC->useragent = strdup(useragent)) == NULL) goto err1; KC->useragentlen = (uint8_t)useragentlen; KC->callback = callback; KC->cookie = cookie; KC->C = C; KC->timeout.tv_sec = 5; KC->timeout.tv_usec = 0; KC->refcount = 1; /* Send protocol version. */ if (network_writeq_add(KC->C->Q, &protovers, 1, &KC->timeout, proto_sent, KC)) goto err2; /* Success! */ return (0); err2: free(KC->useragent); err1: free(KC); err0: /* Failure! */ return (-1); } static int proto_sent(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesout += 1; /* Data was sent. Read the server protocol version. */ if (tsnetwork_read(KC->C->fd, &KC->serverproto, 1, &KC->timeout, &KC->timeout, proto_received, KC)) goto err2; /* Success! */ return (0); err2: status = NETWORK_STATUS_ERR; err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int proto_received(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesin += 1; /* Make sure the protocol version is zero. */ if (KC->serverproto != 0) { status = NETPROTO_STATUS_PROTERR; goto err1; } /* Send our identity. */ if (network_writeq_add(KC->C->Q, &KC->useragentlen, 1, &KC->timeout, namelen_sent, KC)) goto err2; KC->refcount++; if (network_writeq_add(KC->C->Q, (const uint8_t *)KC->useragent, KC->useragentlen, &KC->timeout, name_sent, KC)) goto err2; /* Success! */ return (0); err2: status = NETWORK_STATUS_ERR; err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int namelen_sent(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesout += 1; /* We're not asking for another callback. */ KC->refcount--; /* Success! */ return (0); err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int name_sent(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesout += KC->useragentlen; /* Data was sent. Read the server crypto parameters. */ if (tsnetwork_read(KC->C->fd, KC->serverparams, CRYPTO_DH_PUBLEN + 256 + 32, &KC->timeout, &KC->timeout, dh_received, KC)) goto err2; /* Success! */ return (0); err2: status = NETWORK_STATUS_ERR; err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int dh_received(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesin += CRYPTO_DH_PUBLEN + 256 + 32; /* Verify the signature on the DH public value. */ switch (crypto_rsa_verify(CRYPTO_KEY_ROOT_PUB, KC->serverparams, CRYPTO_DH_PUBLEN, KC->serverparams + CRYPTO_DH_PUBLEN, 256)) { case -1: /* Internal error in crypto_rsa_verify. */ goto err2; case 1: /* Bad signature. */ warn0("Diffie-Hellman parameter from server is not validly" " signed"); status = NETPROTO_STATUS_PROTERR; goto err1; case 0: /* Good signature. */ break; } /* Sanity-check the received public Diffie-Hellman value. */ if (crypto_dh_sanitycheck(KC->serverparams)) { /* Value is insane. */ status = NETPROTO_STATUS_PROTERR; goto err1; } /* Generate DH pair and send public value to server. */ if (crypto_dh_generate(KC->pub, KC->priv)) goto err2; if (network_writeq_add(KC->C->Q, KC->pub, CRYPTO_DH_PUBLEN, &KC->timeout, dh_sent, KC)) goto err2; /* Success! */ return (0); err2: status = NETWORK_STATUS_ERR; err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int dh_sent(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesout += CRYPTO_DH_PUBLEN; /*- * NOTE: We could construct the session keys, and compute and send * the proof of key possession in dh_received instead of here; doing * so would reduce the bandwidth used since the two messages of 256 * and 32 bytes would be coalesced into a single TCP/IP packet, but * it would slow down the connection setup since it would prevent the * client and server computations of the session keys from being * overlapped. * * The trade-off here is between ~40 bytes of bandwidth and ~15ms of * latency; at typical internet bandwidth costs of < $1/GB, taking * the faster but bandwidth-wasting option is preferable if latency * costs more than $0.01/hour, while at (extortionate) mobile phone * data rates of $5/MB the bandwidth-saving but slower option is * better as long as latency costs less than $45/hour. * * Since I don't expect people to be running tarsnap from mobile * phones (or over 19.2kbps modems, where the added bandwidth slows * down the connection by longer than the overlapping of computation * time saves) any time soon, I'm taking the route which optimizes * for time. */ /* Construct session keys. */ if ((KC->C->keys = crypto_session_init(KC->serverparams, KC->priv, KC->serverparams + CRYPTO_DH_PUBLEN + 256, KC->mkey, "C_encr", "C_auth", "S_encr", "S_auth")) == NULL) goto err2; /* Construct proof of key possession and send to server. */ crypto_session_sign(KC->C->keys, KC->mkey, 48, KC->clientproof); if (network_writeq_add(KC->C->Q, KC->clientproof, 32, &KC->timeout, proof_sent, KC)) goto err2; /* Success! */ return (0); err2: status = NETWORK_STATUS_ERR; err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int proof_sent(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ KC->C->bytesout += 32; /* Client proof was sent. Read the server proof. */ if (tsnetwork_read(KC->C->fd, KC->serverproof, 32, &KC->timeout, &KC->timeout, proof_received, KC)) goto err2; /* Success! */ return (0); err2: status = NETWORK_STATUS_ERR; err1: /* Something went wrong. Let the callback handle it. */ rc = docallback(KC, status); if (KC->refcount-- == 1) { free(KC->useragent); free(KC); } /* Failure! */ return (rc); } static int proof_received(void * cookie, int status) { struct keyexchange_internal * KC = cookie; int rc; /* This should be our last reference. */ if (KC->refcount != 1) { warn0("Wrong # of references: %zu", KC->refcount); goto err0; } /* Might we have been successful? */ if (status == NETWORK_STATUS_OK) { /* Adjust traffic statistics. */ KC->C->bytesin += 32; /* Verify that the server proof is valid. */ if (crypto_session_verify(KC->C->keys, KC->mkey, 48, KC->serverproof)) status = NETPROTO_STATUS_PROTERR; } /* We're done or errored out -- either way, call the callback. */ rc = docallback(KC, status); /* Free the cookie. */ free(KC->useragent); free(KC); /* Return the value from the callback. */ return (rc); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/netproto/netproto_packet.c000066400000000000000000000202261476705112600212450ustar00rootroot00000000000000#include #include #include #include #include "crypto_verify_bytes.h" #include "sysendian.h" #include "warnp.h" #include "crypto.h" #include "netproto_internal.h" #include "tsnetwork.h" #include "netproto.h" /** * Packet format: * position length * 0 1 packet type (encrypted) * 1 4 data length, big-endian (encrypted) * 5 32 SHA256(data) (encrypted) * 37 32 HMAC(ciphertext bytes 0--36) (not encrypted) * 69 N packet data (encrypted) */ struct writepacket_internal { network_callback * callback; void * cookie; NETPROTO_CONNECTION * C; size_t len; uint8_t * buf; }; struct readpacket_internal { int (* callback_getbuf)(void *, uint8_t, uint8_t **, size_t); network_callback * callback_done; void * cookie; NETPROTO_CONNECTION * C; uint8_t header[69]; size_t len; uint8_t * buf; }; static network_callback packet_sent; static network_callback header_received; static network_callback data_received; static int packet_sent(void * cookie, int status) { struct writepacket_internal * WC = cookie; int rc; /* Adjust traffic statistics. */ WC->C->bytesqueued -= WC->len; if (status == NETWORK_STATUS_OK) WC->C->bytesout += WC->len; /* Call upstream callback. */ rc = (WC->callback)(WC->cookie, status); /* Free buffer and writepacket cookie. */ free(WC->buf); free(WC); /* Return value from callback. */ return (rc); } static int header_received(void * cookie, int status) { struct readpacket_internal * RC = cookie; struct timeval to0, to1; uint32_t len; int rc = 0; /* No error unless specified otherwise. */ if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ RC->C->bytesin += 69; /* Verify that the header is authentic. */ if (crypto_session_verify(RC->C->keys, RC->header, 37, RC->header + 37)) { status = NETPROTO_STATUS_PROTERR; goto err1; } /* Decrypt header in-place. */ crypto_session_decrypt(RC->C->keys, RC->header, RC->header, 37); /* Decode packet length. */ len = be32dec(&RC->header[1]); #if SIZE_MAX < UINT32_MAX if (len > SIZE_MAX) { errno = ENOMEM; goto err2; } #endif RC->len = len; /* Ask callback to provide a buffer. */ status = (RC->callback_getbuf)(RC->cookie, RC->header[0], &RC->buf, RC->len); if (status != NETWORK_STATUS_OK) goto err1; /* * If the packet data is zero bytes long, we have finished reading * the packet; invoke the upstream callback. We don't bother to * verify that the included SHA256 hash is equal to SHA256(nothing), * since the authenticity of the packet data length is "proved" by * the HMAC on the header and there is only one possible data block * of length zero bytes. */ if (RC->len == 0) { rc = (RC->callback_done)(RC->cookie, NETWORK_STATUS_OK); free(RC); goto done; } /* * Read data into the provided buffer. Allow up to 5 minutes for * the entire packet to arrive -- this works out to a 256kB packet * arriving at a rate of 7kbps, which is a reasonable lower bound * for the bandwidth of systems running tarsnap. */ to0.tv_sec = 60; to1.tv_sec = 300; to0.tv_usec = to1.tv_usec = 0; if (tsnetwork_read(RC->C->fd, RC->buf, RC->len, &to0, &to1, data_received, RC)) goto err2; done: /* Success! */ return (rc); err2: status = NETWORK_STATUS_ERR; err1: rc = (RC->callback_done)(RC->cookie, status); free(RC); /* Failure! */ return (rc); } static int data_received(void * cookie, int status) { struct readpacket_internal * RC = cookie; uint8_t hash[32]; int rc; if (status != NETWORK_STATUS_OK) goto err1; /* Adjust traffic statistics. */ RC->C->bytesin += RC->len; /* Decrypt the data in-place. */ crypto_session_decrypt(RC->C->keys, RC->buf, RC->buf, RC->len); /* * Verify that the data is not corrupt. Protecting against a timing * side channel which would allow an attacker to determine in which * byte of its SHA256 hash a buffer which he mangled had changed is * probably a little bit of overkill, but you can never have too much * overkill where security is concerned. :-) */ if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, RC->buf, RC->len, hash)) { warn0("Programmer error: " "SHA256 should never fail"); goto err1; } if (crypto_verify_bytes(&RC->header[5], hash, 32)) { status = NETPROTO_STATUS_PROTERR; goto err1; } err1: /* We're done. Tell the callback. */ rc = (RC->callback_done)(RC->cookie, status); /* Free readpacket cookie. */ free(RC); /* Return the value from the callback. */ return (rc); } /** * netproto_writepacket(C, type, buf, buflen, callback, cookie): * Write the provided packet to the connection. When complete, call * callback(cookie, status), where status is a NETPROTO_STATUS_* value. */ int netproto_writepacket(NETPROTO_CONNECTION * C, uint8_t type, const uint8_t * buf, size_t buflen, network_callback * callback, void * cookie) { struct writepacket_internal * WC; uint8_t header[37]; struct timeval timeout; /* * Print a warning if the connection is broken. This should never * happen, so we could make this a fatal error; but it's not actually * going to cause any harm (the remote host will detect a protocol * error), so in the interest of resilience we might as well just * print the warning and keep on going. */ if (C->broken) { warn0("Programmer error: " "attempt to write to connection marked as broken"); } /* Sanity check buffer length. */ if (buflen > UINT32_MAX) { warn0("Programmer error: " "buffer too large in netproto_writepacket"); goto err0; } /* Allocate storage for writepacket cookie. */ if ((WC = malloc(sizeof(struct writepacket_internal))) == NULL) goto err0; /* Store callback and cookie. */ WC->callback = callback; WC->cookie = cookie; /* Record parameters needed for statistics purposes. */ WC->C = C; WC->len = buflen + 69; /* Allocate storage for buffered packet. */ if (buflen > SIZE_MAX - 69) { errno = ENOMEM; goto err1; } if ((WC->buf = malloc(buflen + 69)) == NULL) goto err1; /* Construct header. */ header[0] = type; be32enc(&header[1], (uint32_t)buflen); if (crypto_hash_data(CRYPTO_KEY_HMAC_SHA256, buf, buflen, &header[5])) { warn0("Programmer error: " "SHA256 should never fail"); goto err2; } /* Encrypt packet header. */ crypto_session_encrypt(C->keys, header, WC->buf, 37); /* Compute HMAC of packet header. */ crypto_session_sign(C->keys, WC->buf, 37, WC->buf + 37); /* Encrypt packet data. */ crypto_session_encrypt(C->keys, buf, WC->buf + 69, buflen); /* * Add packet to connection write queue. See comments in * header_received concerning the timeout. */ timeout.tv_sec = 300; timeout.tv_usec = 0; if (network_writeq_add(C->Q, WC->buf, buflen + 69, &timeout, packet_sent, WC)) goto err2; C->bytesqueued += WC->len; /* Success! */ return (0); err2: free(WC->buf); err1: free(WC); err0: /* Failure! */ return (-1); } /** * netproto_readpacket(C, callback_getbuf, callback_done, cookie): * Read a packet from the connection. Once the type and length of the * packet is known, call callback_getbuf(cookie, type, buf, buflen); once * the packet is read or fails, call callback_done(cookie, status), where * status is a NETPROTO_STATUS_* value. */ int netproto_readpacket(NETPROTO_CONNECTION * C, int (* callback_getbuf)(void *, uint8_t, uint8_t **, size_t), network_callback * callback_done, void * cookie) { struct readpacket_internal * RC; struct timeval to0, to1; /* Allocate space for readpacket cookie. */ if ((RC = malloc(sizeof(struct readpacket_internal))) == NULL) goto err0; /* Store callbacks, cookie, and connection. */ RC->callback_getbuf = callback_getbuf; RC->callback_done = callback_done; RC->cookie = cookie; RC->C = C; /* * Read packet header. Timeout if no data is received for 60s or * if the complete header is not received for 120s; this allows an * idle connection to be safely distinguished from a dead connection. */ to0.tv_sec = 60; to1.tv_sec = 120; to0.tv_usec = to1.tv_usec = 0; if (tsnetwork_read(C->fd, RC->header, 69, &to0, &to1, header_received, RC)) goto err1; /* Success! */ return (0); err1: free(RC); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/network/000077500000000000000000000000001476705112600155155ustar00rootroot00000000000000tarsnap-1.0.41/lib/network/tsnetwork.h000066400000000000000000000146171476705112600177370ustar00rootroot00000000000000#ifndef TSNETWORK_H_ #define TSNETWORK_H_ #include #include #include #include typedef struct network_writeq_internal NETWORK_WRITEQ; typedef int network_callback(void *, int); /* "op" parameters to network_register. */ #define NETWORK_OP_READ 0 #define NETWORK_OP_WRITE 1 /* callback status values. */ #define NETWORK_STATUS_CONNERR (-2) /* Error connecting to server. */ #define NETWORK_STATUS_ERR (-1) /* Error from recv/send. */ #define NETWORK_STATUS_OK 0 /* Success. */ #define NETWORK_STATUS_TIMEOUT 1 /* Timeout. */ #define NETWORK_STATUS_NODATA 2 /* Timeout with empty buffer. */ #define NETWORK_STATUS_CTIMEOUT 3 /* Timeout connecting to server. */ #define NETWORK_STATUS_CLOSED 4 /* Connection was closed. */ #define NETWORK_STATUS_CANCEL 5 /* Operation cancelled. */ #define NETWORK_STATUS_ZEROBYTE 6 /* Zero-byte write via writeq. */ #define NETWORK_STATUS_MAX 6 /** * network_bwlimit(down, up): * Set the bandwidth rate limit to ${down} bytes per second of read bandwidth * and ${up} bytes per second of write bandwidth. The values ${down} and * ${up} must be between 8000 and 10^9. */ void network_bwlimit(double, double); /** * network_register(fd, op, timeo, callback, cookie): * Register a callback to be performed by network_select when file descriptor * ${fd} is ready for operation ${op}, or once the timeout has expired. */ int network_register(int, int, struct timeval *, network_callback *, void *); /** * network_deregister(fd, op): * Deregister the callback, if any, for operation ${op} on descriptor ${fd}. * The callback will be called with a status of NETWORK_STATUS_CANCEL. */ int network_deregister(int, int); /** * network_sleep(timeo, callback, cookie): * Register a callback to be performed by network_select once the specified * timeout has expired. Return a handle which can be passed to * network_desleep(). */ int network_sleep(struct timeval *, network_callback *, void *); /** * network_desleep(handle): * Deregister the callback associated with the provided handle. The * callback will be called with a status of NETWORK_STATUS_CANCEL. */ int network_desleep(int); /** * network_select(blocking): * Call select(2) on file descriptors provided via network_register and make * callbacks as appropriate (including timeouts). Callbacks are deregistered * before being performed. If ${blocking} is non-zero, allow select(2) to * block waiting for descriptors and timeouts. Stop performing callbacks and * return the first non-zero value returned by a callback if one occurs. */ int network_select(int); /** * network_spin(done): * Repeatedly call network_select until either an error occurs or the value * pointed to by ${done} is non-zero. */ int network_spin(int *); /** * tsnetwork_connect(s, addr, addrlen, timeout, callback, cookie): * Connect the specified socket to the specified address, and call the * specified callback when connected or the connection attempt has failed. */ int tsnetwork_connect(int, const struct sockaddr *, socklen_t, struct timeval *, network_callback *, void *); /** * tsnetwork_read(fd, buf, buflen, to0, to1, callback, cookie): * Asynchronously fill the provided buffer with data from ${fd}, and call * callback(cookie, status) where status is a NETWORK_STATUS_* value. Time * out if no data can be read for a period of time to0, or if the complete * buffer has not been read after time to1. Note that ${buflen} must be * non-zero, since otherwise deadlock would result. */ int tsnetwork_read(int, uint8_t *, size_t, struct timeval *, struct timeval *, network_callback *, void *); /** * tsnetwork_write(fd, buf, buflen, to0, to1, callback, cookie): * Asynchronously write data from the provided buffer to ${fd}, and call * callback(cookie, status) where status is a NETWORK_STATUS_* value. Time * out if no data can be written for a period of time to0, or if the complete * buffer has not been written after time to1. If ${buflen} is zero, the * callback will be invoked with a status of NETWORK_STATUS_CLOSED, even if * the connection is still open. */ int tsnetwork_write(int, const uint8_t *, size_t, struct timeval *, struct timeval *, network_callback *, void *); /** * network_writeq_init(fd): * Construct a queue to be used for writing data to ${fd}. */ NETWORK_WRITEQ * network_writeq_init(int); /** * network_writeq_add_internal(Q, buf, buflen, timeo, callback, cookie, * abstimeo): * Add a buffer write to the specified write queue. The callback function * will be called when the write is finished, fails, or is cancelled. * If ${abstimeo} is zero, the timeout is relative to when the buffer in * question starts to be written (i.e., when the previous buffered write * finishes); otherwise, the timeout is relative to the present time. If * ${buflen} is zero, the callback will be performed, at the appropriate * point, with a status of NETWORK_STATUS_ZEROBYTE. */ int network_writeq_add_internal(NETWORK_WRITEQ *, const uint8_t *, size_t, struct timeval *, network_callback *, void *, int); #define network_writeq_add(Q, buf, buflen, timeo, callback, cookie) \ network_writeq_add_internal(Q, buf, buflen, timeo, callback, cookie, 0) #define network_writeq_add_abs(Q, buf, buflen, timeo, callback, cookie) \ network_writeq_add_internal(Q, buf, buflen, timeo, callback, cookie, 1) /** * network_writeq_cancel(Q): * Cancel all queued writes, including any partially completed writes. Note * that since this leaves the connection in an indeterminate state (there is * no way to know how much data from the currently in-progress write was * written) this should probably only be used prior to closing a connection. * The callbacks for each pending write will be called with a status of * NETWORK_STATUS_DEQUEUE, and network_writeq_cancel will return the first * non-zero value returned by a callback. */ int network_writeq_cancel(NETWORK_WRITEQ *); /** * network_writeq_free(Q): * Free the specified write queue. If there might be any pending writes, * network_writeq_cancel should be called first. */ void network_writeq_free(NETWORK_WRITEQ *); /** * network_getselectstats(N, mu, va, max): * Return and zero statistics on the time between select(2) calls. */ void network_getselectstats(double *, double *, double *, double *); /** * network_fini(void): * Free resources associated with the network subsystem. */ void network_fini(void); #endif /* !TSNETWORK_H_ */ tarsnap-1.0.41/lib/network/tsnetwork_buf.c000066400000000000000000000173571476705112600205720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "tarsnap_opt.h" #include "tsnetwork_internal.h" #include "tvmath.h" #include "warnp.h" #include "tsnetwork.h" #define RECV 0 #define SEND 1 /* * If MSG_NOSIGNAL isn't defined, define it to zero; and then fiddle with * the socket options when needed. */ #ifdef MSG_NOSIGNAL #define HAVE_MSG_NOSIGNAL #endif #ifndef HAVE_MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif struct network_buf_cookie { network_callback * callback; void * cookie; int fd; uint8_t * buf; size_t buflen; size_t bufpos; struct timeval timeout; struct timeval timeout_max; int sendrecv; int netop; int flags; }; static int callback_buf(void * cookie, int status); static int network_buf(int fd, uint8_t * buf, size_t buflen, struct timeval * to0, struct timeval * to1, network_callback * callback, void * cookie, int sendrecv, int netop, int flags); /** * callback_buf(cookie, status): * Callback helper for tsnetwork_read and network_write. */ static int callback_buf(void * cookie, int status) { struct network_buf_cookie * C = cookie; struct timeval timeo; size_t bwlimit; size_t oplen; ssize_t len; int rc = -1; /* If not callback or reset, we have an error. */ #ifndef HAVE_MSG_NOSIGNAL int saved_errno; #ifdef SO_NOSIGPIPE int val; #else void (* oldsig)(int); #endif #endif if (status != NETWORK_STATUS_OK) { /* If we have no data, mark a timeout as "no data" instead. */ if ((C->bufpos != 0) && (status == NETWORK_STATUS_TIMEOUT)) status = NETWORK_STATUS_NODATA; goto docallback; } /* Figure out how far we have bandwidth quota for. */ if (network_bwlimit_get(C->netop, &bwlimit)) { status = NETWORK_STATUS_ERR; goto docallback; } /* If we have no bandwidth, schedule another callback. */ if (bwlimit == 0) goto tryagain; /* Try to read/write data to/from the buffer. */ #ifndef HAVE_MSG_NOSIGNAL #ifdef SO_NOSIGPIPE val = 1; if (setsockopt(C->fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(int))) { status = NETWORK_STATUS_ERR; goto docallback; } #else if ((oldsig = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) { warnp("signal(SIGPIPE)"); status = NETWORK_STATUS_ERR; goto docallback; } #endif #endif oplen = C->buflen - C->bufpos; if (oplen > bwlimit) oplen = bwlimit; /* Send or recv. */ if (C->sendrecv == SEND) len = send(C->fd, C->buf + C->bufpos, oplen, C->flags); else len = recv(C->fd, C->buf + C->bufpos, oplen, C->flags); #ifndef HAVE_MSG_NOSIGNAL /* Save errno in case it gets clobbered by setsockopt() or signal(). */ saved_errno = errno; #ifdef SO_NOSIGPIPE val = 0; if (setsockopt(C->fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(int))) { status = NETWORK_STATUS_ERR; goto docallback; } #else if (signal(SIGPIPE, oldsig) == SIG_ERR) { warnp("signal(SIGPIPE)"); status = NETWORK_STATUS_ERR; goto docallback; } #endif /* Restore saved errno. */ errno = saved_errno; #endif /* Failure, closed, or success? */ if (len == -1) { /* If no data is available, reset the callback. */ if ((errno == EAGAIN) || #if EAGAIN != EWOULDBLOCK (errno == EWOULDBLOCK) || #endif (errno == EINTR)) goto tryagain; /* An error occurred. Let the callback handle it. */ status = NETWORK_STATUS_ERR; goto docallback; } else if (len == 0) { /* Socket has been shut down by remote host. */ /* This should occur only when receiving, not when sending. */ status = NETWORK_STATUS_CLOSED; goto docallback; } else { /* Adjust bandwidth limit. */ if (network_bwlimit_eat(C->netop, (size_t)len)) { status = NETWORK_STATUS_ERR; goto docallback; } /* Data has been read/written into/from buffer. */ C->bufpos += (size_t)len; if (C->bufpos == C->buflen) { status = NETWORK_STATUS_OK; goto docallback; } /* Fall through to resetting the callback. */ } tryagain: /* We need more data. Reset the callback. */ memcpy(&timeo, &C->timeout, sizeof(struct timeval)); if (tvmath_subctime(&timeo)) { status = NETWORK_STATUS_ERR; goto docallback; } if (tv_lt(&C->timeout_max, &timeo)) memcpy(&timeo, &C->timeout_max, sizeof(struct timeval)); if (network_register(C->fd, C->netop, &timeo, callback_buf, C)) { status = NETWORK_STATUS_ERR; goto docallback; } /* Callback has been reset. */ return (0); docallback: /* * If there was a network error and we're being verbose, print the * error now in case errno gets mangled later. */ if ((tarsnap_opt_noisy_warnings) && (status == NETWORK_STATUS_ERR)) warnp("Network error"); /* Call the user callback. */ rc = (C->callback)(C->cookie, status); /* Free the cookie. */ free(C); /* Return error or value from user callback. */ return (rc); } /** * network_buf(fd, buf, buflen, to0, to1, callback, cookie, sendrecv, netop, * flags): * Asynchronously read/write the provided buffer from/to ${fd}, and call * callback(cookie, status) where status is a NETWORK_STATUS_* value. Time * out if no data can be read/writ for a period of time to0, or if the * complete buffer has not been read/writ after time to1. */ static int network_buf(int fd, uint8_t * buf, size_t buflen, struct timeval * to0, struct timeval * to1, network_callback * callback, void * cookie, int sendrecv, int netop, int flags) { struct network_buf_cookie * C; struct timeval timeo; /* Create a cookie to be passed to callback_buf. */ if ((C = malloc(sizeof(struct network_buf_cookie))) == NULL) goto err0; C->callback = callback; C->cookie = cookie; C->fd = fd; C->buf = buf; C->buflen = buflen; C->bufpos = 0; C->sendrecv = sendrecv; C->netop = netop; C->flags = flags; /* Figure out when we should give up waiting. */ memcpy(&C->timeout, to1, sizeof(struct timeval)); if (tvmath_addctime(&C->timeout)) goto err1; /* Record the maximum time that any single send/recv will wait. */ memcpy(&C->timeout_max, to0, sizeof(struct timeval)); /* Set up the callback. */ memcpy(&timeo, to1, sizeof(struct timeval)); if (tv_lt(to0, &timeo)) memcpy(&timeo, to0, sizeof(struct timeval)); if (network_register(fd, netop, &timeo, callback_buf, C)) goto err1; /* Success! */ return (0); err1: free(C); err0: /* Failure! */ return (-1); } /** * tsnetwork_read(fd, buf, buflen, to0, to1, callback, cookie): * Asynchronously fill the provided buffer with data from ${fd}, and call * callback(cookie, status) where status is a NETWORK_STATUS_* value. Time * out if no data can be read for a period of time to0, or if the complete * buffer has not been read after time to1. Note that ${buflen} must be * non-zero, since otherwise deadlock would result. */ int tsnetwork_read(int fd, uint8_t * buf, size_t buflen, struct timeval * to0, struct timeval * to1, network_callback * callback, void * cookie) { /* Make sure buflen is non-zero. */ if (buflen == 0) { warn0("Cannot read zero-byte buffer"); return (-1); } return (network_buf(fd, buf, buflen, to0, to1, callback, cookie, RECV, NETWORK_OP_READ, 0)); } /** * tsnetwork_write(fd, buf, buflen, to0, to1, callback, cookie): * Asynchronously write data from the provided buffer to ${fd}, and call * callback(cookie, status) where status is a NETWORK_STATUS_* value. Time * out if no data can be written for a period of time to0, or if the complete * buffer has not been written after time to1. If ${buflen} is zero, the * callback will be invoked with a status of NETWORK_STATUS_CLOSED, even if * the connection is still open. */ int tsnetwork_write(int fd, const uint8_t * buf, size_t buflen, struct timeval * to0, struct timeval * to1, network_callback * callback, void * cookie) { return (network_buf(fd, (uint8_t *)(uintptr_t)buf, buflen, to0, to1, callback, cookie, SEND, NETWORK_OP_WRITE, MSG_NOSIGNAL)); } tarsnap-1.0.41/lib/network/tsnetwork_bwlimit.c000066400000000000000000000143571476705112600214620ustar00rootroot00000000000000#include #include "events.h" #include "monoclock.h" #include "tsnetwork.h" #include "tsnetwork_internal.h" #ifdef TSNETWORK_PRINT_SPEED_SECONDS #include #include #include "tarsnap_opt.h" #endif /* Bandwidth limiting state. */ struct bwlimit { double Bps; double bucket; void * timer_cookie; int suspended; #ifdef TSNETWORK_PRINT_SPEED_SECONDS size_t bytes_since_last_print; #endif }; static struct bwlimit limit_read; static struct bwlimit limit_write; /* Last time tokens were added to buckets. */ static struct timeval tlast; static int tlast_set = 0; /* Initialized? */ static int initdone = 0; /* Update the internal state. */ static int poke(void); /* Initialize if necessary. */ static void init(void) { /* Limit to 1 GBps by default. */ limit_read.Bps = 1000000000.0; /* 2s burst. */ limit_read.bucket = 2 * limit_read.Bps; /* No timer yet. */ limit_read.timer_cookie = NULL; /* Traffic not suspended. */ limit_read.suspended = 0; #ifdef TSNETWORK_PRINT_SPEED_SECONDS limit_read.bytes_since_last_print = 0; #endif /* Write state is the same as the read state. */ memcpy(&limit_write, &limit_read, sizeof(struct bwlimit)); /* We've been initialized! */ initdone = 1; } /* Timer wakeup. */ static int callback_timer(void * cookie) { struct bwlimit * l = cookie; /* This timer is no longer running. */ l->timer_cookie = NULL; /* Update state. */ return (poke()); } /* Update the state for one direction. */ static int pokeone(struct bwlimit * l, double t, int op) { double waketime; /* Add tokens to the bucket. */ l->bucket += l->Bps * t; /* Overflow the bucket at 2 seconds of bandwidth. */ if (l->bucket > 2 * l->Bps) l->bucket = 2 * l->Bps; /* Do we need to re-enable traffic? */ if ((l->bucket >= 1460) && (l->suspended != 0)) { /* Allow traffic to pass. */ if (network_register_resume(op)) goto err0; l->suspended = 0; } /* Do we need to block traffic? */ if ((l->bucket < 1460) && (l->suspended == 0)) { /* Stop traffic from running. */ if (network_register_suspend(op)) goto err0; l->suspended = 1; } /* If traffic is running, we don't need a timer. */ if ((l->suspended == 0) && (l->timer_cookie != NULL)) { events_timer_cancel(l->timer_cookie); l->timer_cookie = NULL; } /* If traffic is suspended, we need a timer. */ if ((l->suspended == 1) && (l->timer_cookie == NULL)) { /* Wait 10 ms or for 1460 bytes of quota. */ waketime = (1460 - l->bucket) / l->Bps; if (waketime < 0.01) waketime = 0.01; /* Register a timer. */ if ((l->timer_cookie = events_timer_register_double(callback_timer, l, waketime)) == NULL) goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } #ifdef TSNETWORK_PRINT_SPEED_SECONDS static void print_speed(struct timeval tnow) { static struct timeval torig; static struct timeval tlast_printed; double tsince_last; /* If this is the first time, initialize the static variables. */ if (!tlast_set) { memcpy(&torig, &tnow, sizeof(struct timeval)); memcpy(&tlast_printed, &tnow, sizeof(struct timeval)); } /* Duration since the last time we printed the speed. */ tsince_last = timeval_diff(tlast_printed, tnow); /* Bail if not enough time has elapsed. */ if (tsince_last < TSNETWORK_PRINT_SPEED_SECONDS) return; /* Print speed. */ fprintf(stderr, "TSNETWORK_PRINT_SPEED_SECONDS\t%.3f\t%.1f\t%.1f\n", timeval_diff(torig, tnow), (double)limit_read.bytes_since_last_print / tsince_last, (double)limit_write.bytes_since_last_print / tsince_last); /* Reset stats and record the current time. */ limit_read.bytes_since_last_print = 0; limit_write.bytes_since_last_print = 0; memcpy(&tlast_printed, &tnow, sizeof(struct timeval)); } #endif /* Update the internal state. */ static int poke(void) { struct timeval tnow; double t; /* Get the current time. */ if (monoclock_get(&tnow)) goto err0; /* Compute the duration since the last poke. */ if (tlast_set) t = timeval_diff(tlast, tnow); else t = 0.0; /* Poke each direction. */ if (pokeone(&limit_read, t, NETWORK_OP_READ)) goto err0; if (pokeone(&limit_write, t, NETWORK_OP_WRITE)) goto err0; #ifdef TSNETWORK_PRINT_SPEED_SECONDS if (tarsnap_opt_debug_network_stats) print_speed(tnow); #endif /* We have been poked. */ memcpy(&tlast, &tnow, sizeof(struct timeval)); tlast_set = 1; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * network_bwlimit(down, up): * Set the bandwidth rate limit to ${down} bytes per second of read bandwidth * and ${up} bytes per second of write bandwidth. The values ${down} and * ${up} must be between 8000 and 10^9. */ void network_bwlimit(double down, double up) { /* Initialize if necessary. */ if (!initdone) init(); /* Record these values for future reference. */ limit_read.Bps = down; limit_write.Bps = up; /* Don't allow more than a 2 s burst. */ if (limit_read.bucket > 2 * limit_read.Bps) limit_read.bucket = 2 * limit_read.Bps; if (limit_write.bucket > 2 * limit_write.Bps) limit_write.bucket = 2 * limit_write.Bps; } /** * network_bwlimit_get(op, len): * Get the amount of instantaneously allowed bandwidth for ${op} operations. */ int network_bwlimit_get(int op, size_t * len) { /* Initialize if necessary. */ if (!initdone) init(); /* Update state. */ if (poke()) goto err0; /* Return the appropriate value. */ if (op == NETWORK_OP_READ) *len = (size_t)limit_read.bucket; else *len = (size_t)limit_write.bucket; /* * If the allowed bandwidth is less than one normal-sized TCP segment, * force it to zero, in order to avoid silly windowing. */ if (*len < 1460) *len = 0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * network_bwlimit_eat(op, len): * Consume ${len} bytes of bandwidth quota for ${op} operations. */ int network_bwlimit_eat(int op, size_t len) { /* Initialize if necessary. */ if (!initdone) init(); /* Eat tokens from the bucket. */ if (op == NETWORK_OP_READ) limit_read.bucket -= (double)len; else limit_write.bucket -= (double)len; #ifdef TSNETWORK_PRINT_SPEED_SECONDS /* Accumulate sum. */ if (op == NETWORK_OP_READ) limit_read.bytes_since_last_print += len; else limit_write.bytes_since_last_print += len; #endif /* Update state. */ return (poke()); } tarsnap-1.0.41/lib/network/tsnetwork_connect.c000066400000000000000000000111241476705112600214310ustar00rootroot00000000000000#include #include #include #include #include #include "warnp.h" #include "tsnetwork.h" struct network_connect_cookie { int s; int failed; int errnum; network_callback * callback; void * cookie; }; static network_callback callback_connect; /** * callback_connect(cookie, status): * Callback helper for tsnetwork_connect. */ static int callback_connect(void * cookie, int status) { struct network_connect_cookie * C = cookie; int sockerr; socklen_t sockerrlen = sizeof(int); int rc; /* * A timeout here is either a connection timeout or a connection * error, depending upon whether we got here as a result of a zero * second timeout used to postpone handling of a connect() failure. */ if (status == NETWORK_STATUS_TIMEOUT) { if (C->failed) { /* * The connect() call returned an error; restore the * errno value from that point so that when we invoke * our callback we have the right value there. */ status = NETWORK_STATUS_CONNERR; errno = C->errnum; } else { /* * There was a connection timeout; set errno to zero * here since any value in errno at this point is * just left over from a previous failed syscall. */ status = NETWORK_STATUS_CTIMEOUT; errno = 0; } } if (status != NETWORK_STATUS_OK) goto docallback; /* Even if the status is ok, we need to check for pending error. */ if (getsockopt(C->s, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen)) { status = NETWORK_STATUS_CONNERR; goto docallback; } if (sockerr != 0) { errno = sockerr; status = NETWORK_STATUS_CONNERR; } docallback: /* Call the upstream callback. */ rc = (C->callback)(C->cookie, status); /* Free the cookie. */ free(C); /* Return value from user callback. */ return (rc); } /** * tsnetwork_connect(s, addr, addrlen, timeout, callback, cookie): * Connect the specified socket to the specified address, and call the * specified callback when connected or the connection attempt has failed. */ int tsnetwork_connect(int s, const struct sockaddr * addr, socklen_t addrlen, struct timeval * timeout, network_callback * callback, void * cookie) { struct network_connect_cookie * C; struct timeval timeo; int rc = 0; /* Success unless specified otherwise. */ /* Create a cookie to be passed to callback_connect. */ if ((C = malloc(sizeof(struct network_connect_cookie))) == NULL) goto err0; C->s = s; C->failed = 0; C->callback = callback; C->cookie = cookie; /* Mark socket as non-blocking. */ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { warnp("fcntl(O_NONBLOCK)"); goto err1; } /* Try to connect to the server. */ if ((connect(s, addr, addrlen) == 0) || (errno == EINPROGRESS) || (errno == EINTR)) { /* Connection is being established. */ if (network_register(s, NETWORK_OP_WRITE, timeout, callback_connect, C)) goto err1; } else if ((errno == ECONNREFUSED) || (errno == ECONNRESET) || (errno == ENETDOWN) || (errno == ENETUNREACH) || #ifdef FREEBSD_PORTRANGE_BUG /*- * If FreeBSD's net.inet.ip.portrange.randomized sysctl is set to 1 * (the default value) FreeBSD sometimes reuses a source port faster * than might naively be expected. This doesn't cause any problems * except if the pf firewall is running on the source system; said * firewall detects the packet as belonging to an expired connection * and drops it. This would be fine, except that the FreeBSD kernel * doesn't merely drop the packet when a firewall blocks an outgoing * packet; instead, it reports EPERM back to the userland process * which was responsible for the packet being sent. * In short, things interact in wacky ways which make it possible to * get EPERM back in response to a connect(2) syscall. Work around * this by handling EPERM the same way as transient network glitches; * the upstream code will handle this appropriately by retrying the * connection, at which point a new source port number will be chosen * and everything will (probably) work fine. */ (errno == EPERM) || #endif (errno == EHOSTUNREACH)) { /* * The connection attempt has failed. Schedule a callback * to be performed after we return, since we're not allowed * to perform the callback right now. */ C->failed = 1; C->errnum = errno; timeo.tv_sec = timeo.tv_usec = 0; if (network_sleep(&timeo, callback_connect, C)) goto err1; } else { /* Something went seriously wrong. */ warnp("Network connection failure"); goto err1; } /* Return success or the status from the callback. */ return (rc); err1: free(C); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/network/tsnetwork_internal.h000066400000000000000000000026531476705112600216300ustar00rootroot00000000000000#ifndef NETWORK_INTERNAL_H_ #define NETWORK_INTERNAL_H_ /* Macros for handling struct timeval values. */ #define tv_lt(a, b) \ (((a)->tv_sec < (b)->tv_sec) || \ (((a)->tv_sec == (b)->tv_sec) && \ ((a)->tv_usec < (b)->tv_usec))) #define tv_add(a, b) do { \ (a)->tv_sec += (b)->tv_sec; \ (a)->tv_usec += (b)->tv_usec; \ if ((a)->tv_usec >= 1000000) { \ (a)->tv_usec -= 1000000; \ (a)->tv_sec += 1; \ } \ } while (0) #define tv_sub(a, b) do { \ (a)->tv_sec -= (b)->tv_sec; \ (a)->tv_usec -= (b)->tv_usec; \ if ((a)->tv_usec < 0) { \ (a)->tv_usec += 1000000; \ (a)->tv_sec -= 1; \ } \ } while (0) /** * network_register_suspend(op): * Suspend ${op} operations, on all file descriptors. */ int network_register_suspend(int); /** * network_register_resume(op): * Resume pending ${op} operations, on all file descriptors. */ int network_register_resume(int); /** * network_register_fini(void): * Free resources allocated. */ void network_register_fini(void); /** * network_sleep_fini(void): * Free resources allocated. */ void network_sleep_fini(void); /** * network_bwlimit_get(op, len): * Get the amount of instantaneously allowed bandwidth for ${op} operations. */ int network_bwlimit_get(int, size_t *); /** * network_bwlimit_eat(op, len): * Consume ${len} bytes of bandwidth quota for ${op} operations. */ int network_bwlimit_eat(int, size_t); #endif /* !NETWORK_INTERNAL_H_ */ tarsnap-1.0.41/lib/network/tsnetwork_register.c000066400000000000000000000172311476705112600216310ustar00rootroot00000000000000#include #include #include #include "elasticarray.h" #include "events.h" #include "warnp.h" #include "tsnetwork.h" #include "tsnetwork_internal.h" /* Callbacks. */ struct callback { network_callback * callback; void * cookie; int s; int eop; int event_network_pending; void * event_timer; }; ELASTICARRAY_DECL(CALLBACKS, callbacks, struct callback *); static CALLBACKS callbacks[2] = {NULL, NULL}; static int suspended[2] = {0, 0}; static int eops[2] = {EVENTS_NETWORK_OP_READ, EVENTS_NETWORK_OP_WRITE}; /* Translate an "op" value to a set of callbacks. */ static int op2cs(int op) { switch (op) { case NETWORK_OP_READ: return (0); case NETWORK_OP_WRITE: return (1); default: assert(0 && "Invalid operation type in network_register"); } } /* Invoke the callback. */ static int docallback(struct callback * c, int timedout) { network_callback * cb; int status; /* Cancel any pending event callbacks. */ if (c->event_network_pending) events_network_cancel(c->s, c->eop); if (c->event_timer != NULL) events_timer_cancel(c->event_timer); /* Grab the callback function and empty the record. */ cb = c->callback; c->callback = NULL; /* Translate timeout status to network status. */ switch (timedout) { case -1: status = NETWORK_STATUS_CANCEL; break; case 0: status = NETWORK_STATUS_OK; break; case 1: status = NETWORK_STATUS_TIMEOUT; break; default: assert(0 && "Invalid status to docallback"); return (-1); } /* Invoke callback. */ return ((cb)(c->cookie, status)); } /* Callback from network_register when timer expires. */ static int callback_timer(void * cookie) { struct callback * c = cookie; /* This callback is no longer pending. */ c->event_timer = NULL; /* Do callback. */ return (docallback(c, 1)); } /* Callback from network_register when socket is ready. */ static int callback_network(void * cookie) { struct callback * c = cookie; /* This callback is no longer pending. */ c->event_network_pending = 0; /* Do callback. */ return (docallback(c, 0)); } /** * network_register(fd, op, timeo, callback, cookie): * Register a callback to be performed by network_select when file descriptor * ${fd} is ready for operation ${op}, or once the timeout has expired. */ int network_register(int fd, int op, struct timeval * timeo, network_callback * callback, void * cookie) { CALLBACKS * csp; CALLBACKS cs; struct callback * c; int osize; int eop; /* Sanity-check the file descriptor. */ if ((fd < 0) || (fd >= (int)FD_SETSIZE)) { warn0("Invalid file descriptor: %d", fd); goto err0; } /* * Figure out which set of callbacks we're dealing with and which * type of event we need to listen for. */ csp = &callbacks[op2cs(op)]; eop = eops[op2cs(op)]; /* Initialize array if required. */ if ((cs = *csp) == NULL) { if ((cs = *csp = callbacks_init(0)) == NULL) goto err0; } /* Enlarge array if necessary. */ if ((osize = (int)callbacks_getsize(cs)) <= fd) { /* Resize. */ if (callbacks_resize(cs, (size_t)fd + 1)) goto err0; /* Initialize empty. */ for (; osize < fd + 1; osize++) { *callbacks_get(cs, (size_t)osize) = NULL; } } /* Grab the relevant callback record. */ c = *callbacks_get(cs, (size_t)fd); /* If there is no record in that slot, allocate one. */ if (c == NULL) { /* Allocate. */ if ((c = malloc(sizeof(struct callback))) == NULL) goto err0; /* Initialize. */ c->callback = NULL; c->event_network_pending = 0; c->event_timer = NULL; /* Insert into table slot. */ *callbacks_get(cs, (size_t)fd) = c; } /* Make sure we're not replacing an existing callback. */ if (c->callback != NULL) { warn0("Replacing callback: op = %d, fd = %d", op, fd); goto err0; } /* Set network operation parameters. */ c->callback = callback; c->cookie = cookie; c->s = fd; c->eop = eop; /* Register a timer event. */ if ((c->event_timer = events_timer_register(callback_timer, c, timeo)) == NULL) goto err0; /* Register a network event if not suspended. */ if (suspended[op2cs(op)] == 0) { if (events_network_register(callback_network, c, fd, eop)) goto err1; c->event_network_pending = 1; } else { c->event_network_pending = 0; } /* Success! */ return (0); err1: events_timer_cancel(c->event_timer); c->event_timer = NULL; err0: /* Failure! */ return (-1); } /** * network_deregister(fd, op): * Deregister the callback, if any, for operation ${op} on descriptor ${fd}. * The callback will be called with a status of NETWORK_STATUS_CANCEL. */ int network_deregister(int fd, int op) { CALLBACKS cs; struct callback * c; /* Figure out which set of callbacks we're dealing with. */ cs = callbacks[op2cs(op)]; /* If we have no callbacks array we have no callback, so do nothing. */ if (cs == NULL) return (0); /* If this is beyond the end of the array, we have no callback. */ if (fd >= (int)callbacks_getsize(cs)) return (0); /* Sanity-check the file descriptor. */ if (fd < 0) { warn0("Invalid file descriptor: %d", fd); goto err0; } /* Grab the relevant callback record. */ c = *callbacks_get(cs, (size_t)fd); /* If there is no callback, return silently. */ if (c == NULL || c->callback == NULL) return (0); /* Invoke the callback. */ return (docallback(c, -1)); err0: /* Failure! */ return (-1); } /** * network_register_suspend(op): * Suspend ${op} operations, on all file descriptors. */ int network_register_suspend(int op) { CALLBACKS cs; struct callback * c; size_t i; /* This direction is suspended. */ suspended[op2cs(op)] = 1; /* Look up which set of callbacks we're dealing with. */ cs = callbacks[op2cs(op)]; /* If we have no callbacks, return immediately. */ if (cs == NULL) return (0); /* Scan through the callbacks... */ for (i = 0; i < callbacks_getsize(cs); i++) { c = *callbacks_get(cs, i); /* ... ignoring any which don't actually exist... */ if ((c == NULL) || (c->callback == NULL)) continue; /* ... but cancelling the rest. */ if (c->event_network_pending) { c->event_network_pending = 0; if (events_network_cancel(c->s, c->eop)) goto err0; } } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * network_register_resume(op): * Resume pending ${op} operations, on all file descriptors. */ int network_register_resume(int op) { CALLBACKS cs; struct callback * c; size_t i; /* Not suspended any more. */ suspended[op2cs(op)] = 0; /* Look up which set of callbacks we're dealing with. */ cs = callbacks[op2cs(op)]; /* If we have no callbacks, return immediately. */ if (cs == NULL) return (0); /* Scan through the callbacks... */ for (i = 0; i < callbacks_getsize(cs); i++) { c = *callbacks_get(cs, i); /* ... ignoring any which don't actually exist... */ if ((c == NULL) || (c->callback == NULL)) continue; /* ... but starting the rest. */ if (c->event_network_pending == 0) { c->event_network_pending = 1; if (events_network_register(callback_network, c, c->s, c->eop)) goto err0; } } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * network_register_fini(void): * Free resources allocated. */ void network_register_fini(void) { struct callback * c; size_t i, j; /* For each direction... */ for (i = 0; i < 2; i++) { /* Skip uninitialized directions. */ if (callbacks[i] == NULL) continue; /* For each descriptor... */ for (j = 0; j < callbacks_getsize(callbacks[i]); j++) { c = *callbacks_get(callbacks[i], j); /* Skip records which don't exist. */ if (c == NULL) continue; /* If we have no callback, free the record. */ if (c->callback == NULL) free(c); } /* Free the callbacks array. */ callbacks_free(callbacks[i]); } } tarsnap-1.0.41/lib/network/tsnetwork_select.c000066400000000000000000000036501476705112600212640ustar00rootroot00000000000000#include #include "events.h" #include "tsnetwork_internal.h" #include "tsnetwork.h" /* Callback from network_select with zero timeout. */ static int callback_dontsleep(void * cookie) { int * done = cookie; /* We've been called. */ *done = 1; /* Success! */ return (0); } /** * network_select(blocking): * Call select(2) on file descriptors provided via network_register and make * callbacks as appropriate (including timeouts). Callbacks are deregistered * before being performed. If ${blocking} is non-zero, allow select(2) to * block waiting for descriptors and timeouts. Stop performing callbacks and * return the first non-zero value returned by a callback if one occurs. */ int network_select(int blocking) { int done = 0; /* * If we want to avoid blocking on descriptors and timeouts, register * a callback for "right now", and spin until that callback occurs. * Otherwise, just let events run. */ if (blocking == 0) { if (events_timer_register_double(callback_dontsleep, &done, 0.0) == NULL) goto err0; return (events_spin(&done)); } else { return (events_run()); } /* UNREACHABLE */ err0: /* Failure! */ return (-1); } /** * network_spin(done): * Repeatedly call network_select until either an error occurs or the value * pointed to by ${done} is non-zero. */ int network_spin(int * done) { /* Hand this off to the events system. */ return (events_spin(done)); } /** * network_getselectstats(N, mu, va, max): * Return and zero statistics on the time between select(2) calls. */ void network_getselectstats(double * NN, double * mu, double * va, double * max) { /* Hand off to events code. */ events_network_selectstats(NN, mu, va, max); } /** * network_fini(void): * Free resources associated with the network subsystem. */ void network_fini(void) { /* Clean up network registrations. */ network_register_fini(); /* Clean up sleeps. */ network_sleep_fini(); } tarsnap-1.0.41/lib/network/tsnetwork_sleep.c000066400000000000000000000071131476705112600211130ustar00rootroot00000000000000#include #include #include #include "elasticarray.h" #include "events.h" #include "warnp.h" #include "tsnetwork.h" #include "tsnetwork_internal.h" /* Sleepers. */ struct sleeper { network_callback * callback; void * cookie; void * event_cookie; }; ELASTICARRAY_DECL(SLEEPERS, sleepers, struct sleeper *); static SLEEPERS sleepers = NULL; /* Callback from network_sleep when timer expires. */ static int callback_timer(void * cookie) { struct sleeper * sp = cookie; /* This callback is no longer pending. */ sp->event_cookie = NULL; /* Do callback. */ return ((sp->callback)(sp->cookie, NETWORK_STATUS_TIMEOUT)); } /** * network_sleep(timeo, callback, cookie): * Register a callback to be performed by network_select once the specified * timeout has expired. Return a handle which can be passed to * network_desleep(). */ int network_sleep(struct timeval * timeo, network_callback * callback, void * cookie) { struct sleeper s; struct sleeper * sp = NULL; /* Silence bogus compiler warnings. */ size_t h; /* Initialize array if required. */ if (sleepers == NULL) { if ((sleepers = sleepers_init(0)) == NULL) goto err0; } /* Construct sleeper record. */ s.callback = callback; s.cookie = cookie; s.event_cookie = NULL; /* Search for empty space. */ for (h = 0; h < sleepers_getsize(sleepers); h++) { sp = *sleepers_get(sleepers, h); if (sp->event_cookie == NULL) { /* Use this one. */ memcpy(sp, &s, sizeof(struct sleeper)); break; } } /* If we didn't find an empty space, add a new sleeper. */ if (h == sleepers_getsize(sleepers)) { /* Don't have too many sleepers... */ if (h == 1024) { warn0("Too many sleepers"); goto err0; } /* Allocate a record. */ if ((sp = malloc(sizeof(struct sleeper))) == NULL) goto err0; /* Copy data in. */ memcpy(sp, &s, sizeof(struct sleeper)); /* Append the record. */ if (sleepers_append(sleepers, &sp, 1)) goto err1; } else { /*- * If (h != sleepers_getsize()), then sp was set in the * earlier 'for' loop, but compilers don't realize it. */ assert(sp != NULL); } /* Register the timer event. */ if ((sp->event_cookie = events_timer_register(callback_timer, sp, timeo)) == NULL) goto err0; /* Success! */ return ((int)h); err1: free(sp); err0: /* Failure! */ return (-1); } /** * network_desleep(handle): * Deregister the callback associated with the provided handle. The * callback will be called with a status of NETWORK_STATUS_CANCEL. */ int network_desleep(int handle) { struct sleeper * sp; /* Sanity-check the handle. */ if ((handle >= (int)sleepers_getsize(sleepers)) || (handle < 0)) { warn0("Invalid sleeper handle: %d", handle); goto err0; } /* Grab the relevant sleeper record. */ sp = *sleepers_get(sleepers, (size_t)handle); /* If there is no timer, return silently. */ if (sp->event_cookie == NULL) return (0); /* Cancel the timer. */ events_timer_cancel(sp->event_cookie); sp->event_cookie = NULL; /* Invoke the callback. */ return ((sp->callback)(sp->cookie, NETWORK_STATUS_CANCEL)); err0: /* Failure! */ return (-1); } /** * network_sleep_fini(void): * Free resources allocated. */ void network_sleep_fini(void) { struct sleeper * sp; size_t i; /* Nothing to do if we're uninitialized. */ if (sleepers == NULL) return; /* Free records. */ for (i = 0; i < sleepers_getsize(sleepers); i++) { sp = *sleepers_get(sleepers, i); /* If this sleep is no longer in progress, free the record. */ if (sp->event_cookie == NULL) free(sp); } /* Free the sleepers array. */ sleepers_free(sleepers); } tarsnap-1.0.41/lib/network/tsnetwork_writeq.c000066400000000000000000000141721476705112600213210ustar00rootroot00000000000000#include #include #include #include #include #include "tsnetwork_cork.h" #include "tsnetwork_internal.h" #include "tvmath.h" #include "tsnetwork.h" struct network_writeq_buf { const uint8_t * buf; size_t buflen; struct timeval timeo; int abstimeo; network_callback * callback; void * cookie; struct network_writeq_buf * next; }; struct network_writeq_internal { int fd; struct network_writeq_buf * head; struct network_writeq_buf ** tailptr; }; static int dowrite(struct network_writeq_internal *); static network_callback callback_bufdone; static int dowrite(struct network_writeq_internal * Q) { struct network_writeq_buf * QB = Q->head; struct timeval timeo; /* Sanity check that the queue is non-empty */ assert(Q->head != NULL); /* Figure out how long to allow for this buffer write. */ memcpy(&timeo, &QB->timeo, sizeof(struct timeval)); if (QB->abstimeo && tvmath_subctime(&timeo)) goto err0; /* Write the buffer. */ if (tsnetwork_write(Q->fd, QB->buf, QB->buflen, &timeo, &timeo, callback_bufdone, Q)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * callback_bufdone(cookie, status): * Call the upstream callback for the buffer at the head of the write queue * ${cookie}, remove it from the queue, and write the next buffer. */ static int callback_bufdone(void * cookie, int status) { struct network_writeq_internal * Q = cookie; struct network_writeq_buf * head_old; int rc; /* Unlink the current buffer from the queue. */ head_old = Q->head; Q->head = head_old->next; /* Update tail pointer if necessary. */ if (Q->tailptr == &head_old->next) Q->tailptr = &Q->head; /* * A callback of NETWORK_STATUS_CLOSED in response to an attempt to * write zero bytes is really a NETWORK_STATUS_ZEROBYTE. */ if ((status == NETWORK_STATUS_CLOSED) && (head_old->buflen == 0)) status = NETWORK_STATUS_ZEROBYTE; /* * If there's another buffer waiting to be written, register it to * be sent. If not and we're not handling an error, uncork the * socket. */ if (Q->head != NULL) { if (dowrite(Q)) goto err1; } else { if ((status == NETWORK_STATUS_OK) && network_uncork(Q->fd)) status = NETWORK_STATUS_ERR; } /* Call the upstream callback. */ rc = (head_old->callback)(head_old->cookie, status); /* Free the write parameters structure. */ free(head_old); /* Return value from callback. */ return (rc); err1: (head_old->callback)(head_old->cookie, status); free(head_old); /* Failure! */ return (-1); } /** * network_writeq_init(fd): * Construct a queue to be used for writing data to ${fd}. */ NETWORK_WRITEQ * network_writeq_init(int fd) { struct network_writeq_internal * Q; /* Allocate memory. */ if ((Q = malloc(sizeof(struct network_writeq_internal))) == NULL) goto err0; /* Initialize structure. */ Q->fd = fd; Q->head = NULL; Q->tailptr = &Q->head; /* Success! */ return (Q); err0: /* Failure! */ return (NULL); } /** * network_writeq_add_internal(Q, buf, buflen, timeo, callback, cookie, * abstimeo): * Add a buffer write to the specified write queue. The callback function * will be called when the write is finished, fails, or is cancelled. * If ${abstimeo} is zero, the timeout is relative to when the buffer in * question starts to be written (i.e., when the previous buffered write * finishes); otherwise, the timeout is relative to the present time. If * ${buflen} is zero, the callback will be performed, at the appropriate * point, with a status of NETWORK_STATUS_ZEROBYTE. */ int network_writeq_add_internal(NETWORK_WRITEQ * Q, const uint8_t * buf, size_t buflen, struct timeval * timeo, network_callback * callback, void * cookie, int abstimeo) { struct network_writeq_buf * QB; struct network_writeq_buf ** tailptr_old; struct network_writeq_buf * head_old; /* Wrap parameters into a structure. */ if ((QB = malloc(sizeof(struct network_writeq_buf))) == NULL) goto err0; QB->buf = buf; QB->buflen = buflen; memcpy(&QB->timeo, timeo, sizeof(struct timeval)); QB->abstimeo = abstimeo; QB->callback = callback; QB->cookie = cookie; QB->next = NULL; /* Compute absolute time if appropriate. */ if (abstimeo && tvmath_addctime(&QB->timeo)) goto err1; /* Add this to the write queue. */ head_old = Q->head; tailptr_old = Q->tailptr; *Q->tailptr = QB; Q->tailptr = &QB->next; /* If the queue head was NULL, we need to kick off the writing. */ if (head_old == NULL) { /* Cork the socket so that we don't send small packets. */ if (network_cork(Q->fd)) goto err2; if (dowrite(Q)) goto err2; } /* Success! */ return (0); err2: Q->tailptr = tailptr_old; *Q->tailptr = NULL; err1: free(QB); err0: /* Failure! */ return (-1); } /** * network_writeq_cancel(Q): * Cancel all queued writes, including any partially completed writes. Note * that since this leaves the connection in an indeterminate state (there is * no way to know how much data from the currently in-progress write was * written) this should probably only be used prior to closing a connection. * The callbacks for each pending write will be called with a status of * NETWORK_STATUS_DEQUEUE, and network_writeq_cancel will return the first * non-zero value returned by a callback. */ int network_writeq_cancel(NETWORK_WRITEQ * Q) { int rc = 0, rc2; /* Keep on deregistering callbacks until the queue is empty. */ while (Q->head != NULL) { rc2 = network_deregister(Q->fd, NETWORK_OP_WRITE); rc = rc ? rc : rc2; } /* Return first non-zero result from deregistration. */ return (rc); } /** * network_writeq_free(Q): * Free the specified write queue. If there might be any pending writes, * network_writeq_cancel should be called first. */ void network_writeq_free(NETWORK_WRITEQ * Q) { struct network_writeq_buf * head_old; /* Behave consistently with free(NULL). */ if (Q == NULL) return; /* Repeat until the queue is empty. */ while (Q->head != NULL) { /* Unlink the current buffer from the queue. */ head_old = Q->head; Q->head = Q->head->next; /* Free the write parameters structure. */ free(head_old); } /* Free the queue structure itself. */ free(Q); } tarsnap-1.0.41/lib/scryptenc/000077500000000000000000000000001476705112600160365ustar00rootroot00000000000000tarsnap-1.0.41/lib/scryptenc/scryptenc.c000066400000000000000000000600761476705112600202250ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include #include #include #include #include #include #include "crypto_aes.h" #include "crypto_aesctr.h" #include "crypto_entropy.h" #include "crypto_verify_bytes.h" #include "humansize.h" #include "insecure_memzero.h" #include "sha256.h" #include "sysendian.h" #include "warnp.h" #include "crypto_scrypt.h" #include "memlimit.h" #include "scryptenc_cpuperf.h" #include "scryptenc.h" #define ENCBLOCK 65536 static int pickparams(size_t, double, double, int *, uint32_t *, uint32_t *, int); static int checkparams(size_t, double, double, int, uint32_t, uint32_t, int, int); #ifdef POSIXFAIL_ABSTRACT_DECLARATOR static int scryptdec_file_load_header(FILE * infile, uint8_t header[static 96]); #else static int scryptdec_file_load_header(FILE *, uint8_t[static 96]); #endif struct scryptdec_file_cookie { FILE * infile; /* This is not owned by this cookie. */ uint8_t header[96]; uint8_t dk[64]; }; static void display_params(int logN, uint32_t r, uint32_t p, size_t memlimit, double opps, double maxtime) { uint64_t N = (uint64_t)(1) << logN; uint64_t mem_minimum = 128 * r * N; double expected_seconds = opps > 0 ? (double)(4 * N * r * p) / opps : 0; char * human_memlimit = humansize(memlimit); char * human_mem_minimum = humansize(mem_minimum); /* Parameters */ fprintf(stderr, "Parameters used: N = %" PRIu64 "; r = %" PRIu32 "; p = %" PRIu32 ";\n", N, r, p); /* Memory */ fprintf(stderr, " Decrypting this file requires at least" " %s of memory", human_mem_minimum); if (memlimit > 0) fprintf(stderr, " (%s available)", human_memlimit); /* CPU time */ if (opps > 0) fprintf(stderr, ",\n and will take approximately %.1f " "seconds (limit: %.1f seconds)", expected_seconds, maxtime); fprintf(stderr, ".\n"); /* Clean up */ free(human_memlimit); free(human_mem_minimum); } static int pickparams(size_t maxmem, double maxmemfrac, double maxtime, int * logN, uint32_t * r, uint32_t * p, int verbose) { size_t memlimit; double opps; double opslimit; double maxN, maxrp; uint64_t checkN; int rc; /* Figure out how much memory to use. */ if (memtouse(maxmem, maxmemfrac, &memlimit)) return (SCRYPT_ELIMIT); /* Figure out how fast the CPU is. */ if ((rc = scryptenc_cpuperf(&opps)) != SCRYPT_OK) return (rc); opslimit = opps * maxtime; /* Allow a minimum of 2^15 salsa20/8 cores. */ if (opslimit < 32768) opslimit = 32768; /* Fix r = 8 for now. */ *r = 8; /* * The memory limit requires that 128Nr <= memlimit, while the CPU * limit requires that 4Nrp <= opslimit. If opslimit < memlimit/32, * opslimit imposes the stronger limit on N. */ #ifdef DEBUG fprintf(stderr, "Requiring 128Nr <= %zu, 4Nrp <= %f\n", memlimit, opslimit); #endif if (opslimit < (double)memlimit / 32) { /* Set p = 1 and choose N based on the CPU limit. */ *p = 1; maxN = opslimit / (*r * 4); for (*logN = 1; *logN < 63; *logN += 1) { checkN = (uint64_t)(1) << *logN; /* * Find the largest power of two <= maxN, which is * also the least power of two > maxN/2. */ if ((double)checkN > maxN / 2) break; } } else { /* Set N based on the memory limit. */ maxN = (double)(memlimit / (*r * 128)); for (*logN = 1; *logN < 63; *logN += 1) { checkN = (uint64_t)(1) << *logN; if ((double)checkN > maxN / 2) break; } /* Choose p based on the CPU limit. */ checkN = (uint64_t)(1) << *logN; maxrp = (opslimit / 4) / (double)checkN; if (maxrp > 0x3fffffff) maxrp = 0x3fffffff; *p = (uint32_t)(maxrp) / *r; } if (verbose) display_params(*logN, *r, *p, memlimit, opps, maxtime); /* Success! */ return (SCRYPT_OK); } static int checkparams(size_t maxmem, double maxmemfrac, double maxtime, int logN, uint32_t r, uint32_t p, int verbose, int force) { size_t memlimit; double opps; double opslimit; uint64_t N; int rc; /* Sanity-check values. */ if ((logN < 1) || (logN > 63)) return (SCRYPT_EINVAL); if ((uint64_t)(r) * (uint64_t)(p) >= 0x40000000) return (SCRYPT_EINVAL); if ((r == 0) || (p == 0)) return (SCRYPT_EINVAL); /* Are we forcing decryption, regardless of resource limits? */ if (!force) { /* Figure out the maximum amount of memory we can use. */ if (memtouse(maxmem, maxmemfrac, &memlimit)) return (SCRYPT_ELIMIT); /* Figure out how fast the CPU is. */ if ((rc = scryptenc_cpuperf(&opps)) != SCRYPT_OK) return (rc); opslimit = opps * maxtime; if (verbose) display_params(logN, r, p, memlimit, opps, maxtime); /* Check limits. */ N = (uint64_t)(1) << logN; if (((memlimit / N) / r < 128) && (((opslimit / (double)N) / r) / p < 4)) return (SCRYPT_EBIGSLOW); if ((memlimit / N) / r < 128) return (SCRYPT_ETOOBIG); if (((opslimit / (double)N) / r) / p < 4) return (SCRYPT_ETOOSLOW); } else { /* We have no limit. */ memlimit = 0; opps = 0; if (verbose) display_params(logN, r, p, memlimit, opps, maxtime); } /* Success! */ return (SCRYPT_OK); } /* * NOTE: The caller is responsible for sanitizing ${dk}, including if this * function fails. */ static int scryptenc_setup(uint8_t header[96], uint8_t dk[64], const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force) { uint8_t salt[32]; uint8_t hbuf[32]; uint64_t N; SHA256_CTX ctx; uint8_t * key_hmac = &dk[32]; HMAC_SHA256_CTX hctx; int rc; /* Determine parameters. */ if (P->logN != 0) { /* Check logN, r, p. */ if ((rc = checkparams(P->maxmem, P->maxmemfrac, P->maxtime, P->logN, P->r, P->p, verbose, force)) != 0) { /* Warn about resource limit, but suppress the error. */ if ((rc == SCRYPT_ETOOBIG) || (rc == SCRYPT_EBIGSLOW)) warn0("Warning: Explicit parameters" " might exceed memory limit"); if ((rc == SCRYPT_ETOOSLOW) || (rc == SCRYPT_EBIGSLOW)) warn0("Warning: Explicit parameters" " might exceed time limit"); if ((rc == SCRYPT_ETOOBIG) || (rc == SCRYPT_ETOOSLOW) || (rc == SCRYPT_EBIGSLOW)) rc = 0; /* Provide a more meaningful error message. */ if (rc == SCRYPT_EINVAL) rc = SCRYPT_EPARAM; /* Bail if we haven't suppressed the error. */ if (rc != 0) return (rc); } } else { /* Pick values for N, r, p. */ if ((rc = pickparams(P->maxmem, P->maxmemfrac, P->maxtime, &P->logN, &P->r, &P->p, verbose)) != 0) return (rc); } /* Sanity check. */ assert((P->logN > 0) && (P->logN < 64)); /* Set N. */ N = (uint64_t)(1) << P->logN; /* Get some salt. */ if (crypto_entropy_read(salt, 32)) return (SCRYPT_ESALT); /* Generate the derived keys. */ if (crypto_scrypt(passwd, passwdlen, salt, 32, N, P->r, P->p, dk, 64)) return (SCRYPT_EKEY); /* Construct the file header. */ memcpy(header, "scrypt", 6); header[6] = 0; header[7] = P->logN & 0xff; be32enc(&header[8], P->r); be32enc(&header[12], P->p); memcpy(&header[16], salt, 32); /* Add header checksum. */ SHA256_Init(&ctx); SHA256_Update(&ctx, header, 48); SHA256_Final(hbuf, &ctx); memcpy(&header[48], hbuf, 16); /* Add header signature (used for verifying password). */ HMAC_SHA256_Init(&hctx, key_hmac, 32); HMAC_SHA256_Update(&hctx, header, 64); HMAC_SHA256_Final(hbuf, &hctx); memcpy(&header[64], hbuf, 32); /* Success! */ return (SCRYPT_OK); } /** * scryptdec_file_printparams(infile): * Print the encryption parameters (N, r, p) used for the encrypted ${infile}. */ int scryptdec_file_printparams(FILE * infile) { uint8_t header[96]; int logN; uint32_t r; uint32_t p; int rc; /* Load the header. */ if ((rc = scryptdec_file_load_header(infile, header)) != 0) goto err0; /* Parse N, r, p. */ logN = header[7]; r = be32dec(&header[8]); p = be32dec(&header[12]); /* Print parameters. */ display_params(logN, r, p, 0, 0, 0); /* Success! */ return (SCRYPT_OK); err0: /* Failure! */ return (rc); } /* * NOTE: The caller is responsible for sanitizing ${dk}, including if this * function fails. */ static int scryptdec_setup(const uint8_t header[96], uint8_t dk[64], const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force) { uint8_t salt[32]; uint8_t hbuf[32]; uint64_t N; SHA256_CTX ctx; uint8_t * key_hmac = &dk[32]; HMAC_SHA256_CTX hctx; int rc; /* Parse N, r, p, salt. */ P->logN = header[7]; P->r = be32dec(&header[8]); P->p = be32dec(&header[12]); memcpy(salt, &header[16], 32); /* Verify header checksum. */ SHA256_Init(&ctx); SHA256_Update(&ctx, header, 48); SHA256_Final(hbuf, &ctx); if (crypto_verify_bytes(&header[48], hbuf, 16)) return (SCRYPT_EINVAL); /* * Check whether the provided parameters are valid and whether the * key derivation function can be computed within the allowed memory * and CPU time, unless the user chose to disable this test. */ if ((rc = checkparams(P->maxmem, P->maxmemfrac, P->maxtime, P->logN, P->r, P->p, verbose, force)) != 0) return (rc); /* Compute the derived keys. */ N = (uint64_t)(1) << P->logN; if (crypto_scrypt(passwd, passwdlen, salt, 32, N, P->r, P->p, dk, 64)) return (SCRYPT_EKEY); /* Check header signature (i.e., verify password). */ HMAC_SHA256_Init(&hctx, key_hmac, 32); HMAC_SHA256_Update(&hctx, header, 64); HMAC_SHA256_Final(hbuf, &hctx); if (crypto_verify_bytes(hbuf, &header[64], 32)) return (SCRYPT_EPASS); /* Success! */ return (SCRYPT_OK); } /** * scryptenc_buf(inbuf, inbuflen, outbuf, passwd, passwdlen, * params, verbose, force): * Encrypt ${inbuflen} bytes from ${inbuf}, writing the resulting * ${inbuflen} + 128 bytes to ${outbuf}. If ${force} is 1, do not check * whether decryption will exceed the estimated available memory or time. * The explicit parameters within ${params} must be zero or must all be * non-zero. If explicit parameters are used and the computation is estimated * to exceed resource limits, print a warning instead of returning an error. * Return the explicit parameters used via ${params}. */ int scryptenc_buf(const uint8_t * inbuf, size_t inbuflen, uint8_t * outbuf, const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force) { uint8_t dk[64]; uint8_t hbuf[32]; uint8_t header[96]; uint8_t * key_enc = dk; uint8_t * key_hmac = &dk[32]; int rc; HMAC_SHA256_CTX hctx; struct crypto_aes_key * key_enc_exp; struct crypto_aesctr * AES; /* The explicit parameters must be zero, or all non-zero. */ assert(((P->logN == 0) && (P->r == 0) && (P->p == 0)) || ((P->logN != 0) && (P->r != 0) && (P->p != 0))); /* Generate the header and derived key. */ if ((rc = scryptenc_setup(header, dk, passwd, passwdlen, P, verbose, force)) != 0) goto err1; /* Copy header into output buffer. */ memcpy(outbuf, header, 96); /* Encrypt data. */ if ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) { rc = SCRYPT_EOPENSSL; goto err1; } if ((AES = crypto_aesctr_init(key_enc_exp, 0)) == NULL) { crypto_aes_key_free(key_enc_exp); rc = SCRYPT_ENOMEM; goto err1; } crypto_aesctr_stream(AES, inbuf, &outbuf[96], inbuflen); crypto_aesctr_free(AES); crypto_aes_key_free(key_enc_exp); /* Add signature. */ HMAC_SHA256_Init(&hctx, key_hmac, 32); HMAC_SHA256_Update(&hctx, outbuf, 96 + inbuflen); HMAC_SHA256_Final(hbuf, &hctx); memcpy(&outbuf[96 + inbuflen], hbuf, 32); /* Zero sensitive data. */ insecure_memzero(dk, 64); /* Success! */ return (SCRYPT_OK); err1: insecure_memzero(dk, 64); /* Failure! */ return (rc); } /** * scryptdec_buf(inbuf, inbuflen, outbuf, outlen, passwd, passwdlen, * params, verbose, force): * Decrypt ${inbuflen} bytes from ${inbuf}, writing the result into ${outbuf} * and the decrypted data length to ${outlen}. The allocated length of * ${outbuf} must be at least ${inbuflen}. If ${force} is 1, do not check * whether decryption will exceed the estimated available memory or time. * The explicit parameters within ${params} must be zero. Return the explicit * parameters used via ${params}. */ int scryptdec_buf(const uint8_t * inbuf, size_t inbuflen, uint8_t * outbuf, size_t * outlen, const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force) { uint8_t hbuf[32]; uint8_t dk[64]; uint8_t * key_enc = dk; uint8_t * key_hmac = &dk[32]; int rc; HMAC_SHA256_CTX hctx; struct crypto_aes_key * key_enc_exp; struct crypto_aesctr * AES; /* The explicit parameters must be zero. */ assert((P->logN == 0) && (P->r == 0) && (P->p == 0)); /* * All versions of the scrypt format will start with "scrypt" and * have at least 7 bytes of header. */ if ((inbuflen < 7) || (memcmp(inbuf, "scrypt", 6) != 0)) { rc = SCRYPT_EINVAL; goto err0; } /* Check the format. */ if (inbuf[6] != 0) { rc = SCRYPT_EVERSION; goto err0; } /* We must have at least 128 bytes. */ if (inbuflen < 128) { rc = SCRYPT_EINVAL; goto err0; } /* Parse the header and generate derived keys. */ if ((rc = scryptdec_setup(inbuf, dk, passwd, passwdlen, P, verbose, force)) != 0) goto err1; /* Decrypt data. */ if ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) { rc = SCRYPT_EOPENSSL; goto err1; } if ((AES = crypto_aesctr_init(key_enc_exp, 0)) == NULL) { crypto_aes_key_free(key_enc_exp); rc = SCRYPT_ENOMEM; goto err1; } crypto_aesctr_stream(AES, &inbuf[96], outbuf, inbuflen - 128); crypto_aesctr_free(AES); crypto_aes_key_free(key_enc_exp); *outlen = inbuflen - 128; /* Verify signature. */ HMAC_SHA256_Init(&hctx, key_hmac, 32); HMAC_SHA256_Update(&hctx, inbuf, inbuflen - 32); HMAC_SHA256_Final(hbuf, &hctx); if (crypto_verify_bytes(hbuf, &inbuf[inbuflen - 32], 32)) { rc = SCRYPT_EINVAL; goto err1; } /* Zero sensitive data. */ insecure_memzero(dk, 64); /* Success! */ return (SCRYPT_OK); err1: insecure_memzero(dk, 64); err0: /* Failure! */ return (rc); } /** * scryptenc_file(infile, outfile, passwd, passwdlen, params, verbose, force): * Read a stream from ${infile} and encrypt it, writing the resulting stream * to ${outfile}. If ${force} is 1, do not check whether decryption will * exceed the estimated available memory or time. The explicit parameters * within ${params} must be zero or must all be non-zero. If explicit * parameters are used and the computation is estimated to exceed resource * limits, print a warning instead of returning an error. Return the explicit * parameters used via ${params}. */ int scryptenc_file(FILE * infile, FILE * outfile, const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force) { uint8_t buf[ENCBLOCK]; uint8_t dk[64]; uint8_t hbuf[32]; uint8_t header[96]; uint8_t * key_enc = dk; uint8_t * key_hmac = &dk[32]; size_t readlen; HMAC_SHA256_CTX hctx; struct crypto_aes_key * key_enc_exp; struct crypto_aesctr * AES; int rc; /* The explicit parameters must be zero, or all non-zero. */ assert(((P->logN == 0) && (P->r == 0) && (P->p == 0)) || ((P->logN != 0) && (P->r != 0) && (P->p != 0))); /* Generate the header and derived key. */ if ((rc = scryptenc_setup(header, dk, passwd, passwdlen, P, verbose, force)) != 0) goto err1; /* Hash and write the header. */ HMAC_SHA256_Init(&hctx, key_hmac, 32); HMAC_SHA256_Update(&hctx, header, 96); if (fwrite(header, 96, 1, outfile) != 1) { rc = SCRYPT_EWRFILE; goto err1; } /* * Read blocks of data, encrypt them, and write them out; hash the * data as it is produced. */ if ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) { rc = SCRYPT_EOPENSSL; goto err1; } if ((AES = crypto_aesctr_init(key_enc_exp, 0)) == NULL) { crypto_aes_key_free(key_enc_exp); rc = SCRYPT_ENOMEM; goto err1; } do { if ((readlen = fread(buf, 1, ENCBLOCK, infile)) == 0) break; crypto_aesctr_stream(AES, buf, buf, readlen); HMAC_SHA256_Update(&hctx, buf, readlen); if (fwrite(buf, 1, readlen, outfile) < readlen) { crypto_aesctr_free(AES); rc = SCRYPT_EWRFILE; goto err1; } } while (1); crypto_aesctr_free(AES); crypto_aes_key_free(key_enc_exp); /* Did we exit the loop due to a read error? */ if (ferror(infile)) { rc = SCRYPT_ERDFILE; goto err1; } /* Compute the final HMAC and output it. */ HMAC_SHA256_Final(hbuf, &hctx); if (fwrite(hbuf, 32, 1, outfile) != 1) { rc = SCRYPT_EWRFILE; goto err1; } /* Zero sensitive data. */ insecure_memzero(dk, 64); /* Success! */ return (SCRYPT_OK); err1: insecure_memzero(dk, 64); /* Failure! */ return (rc); } /** * scryptdec_file_cookie_free(cookie): * Free the ${cookie}. */ void scryptdec_file_cookie_free(struct scryptdec_file_cookie * C) { /* Behave consistently with free(NULL). */ if (C == NULL) return; /* Zero sensitive data. */ insecure_memzero(C->dk, 64); /* We do not free C->infile because it is not owned by this cookie. */ /* Free the cookie. */ free(C); } /* Load the header and check the magic. */ static int scryptdec_file_load_header(FILE * infile, uint8_t header[static 96]) { int rc; /* * Read the first 7 bytes of the file; all future versions of scrypt * are guaranteed to have at least 7 bytes of header. */ if (fread(header, 7, 1, infile) < 1) { if (ferror(infile)) { rc = SCRYPT_ERDFILE; goto err0; } else { rc = SCRYPT_EINVAL; goto err0; } } /* Do we have the right magic? */ if (memcmp(header, "scrypt", 6)) { rc = SCRYPT_EINVAL; goto err0; } if (header[6] != 0) { rc = SCRYPT_EVERSION; goto err0; } /* * Read another 89 bytes of the file; version 0 of the scrypt file * format has a 96-byte header. */ if (fread(&header[7], 89, 1, infile) < 1) { if (ferror(infile)) { rc = SCRYPT_ERDFILE; goto err0; } else { rc = SCRYPT_EINVAL; goto err0; } } /* Success! */ return (SCRYPT_OK); err0: /* Failure! */ return (rc); } /** * scryptdec_file_prep(infile, passwd, passwdlen, params, verbose, force, * cookie): * Prepare to decrypt ${infile}, including checking the passphrase. Allocate * a cookie at ${cookie}. After calling this function, ${infile} should not * be modified until the decryption is completed by scryptdec_file_copy(). * If ${force} is 1, do not check whether decryption will exceed the estimated * available memory or time. The explicit parameters within ${params} must be * zero. Return the explicit parameters to be used via ${params}. */ int scryptdec_file_prep(FILE * infile, const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force, struct scryptdec_file_cookie ** cookie) { struct scryptdec_file_cookie * C; int rc; /* The explicit parameters must be zero. */ assert((P->logN == 0) && (P->r == 0) && (P->p == 0)); /* Allocate the cookie. */ if ((C = malloc(sizeof(struct scryptdec_file_cookie))) == NULL) return (SCRYPT_ENOMEM); C->infile = infile; /* Load the header. */ if ((rc = scryptdec_file_load_header(infile, C->header)) != 0) goto err1; /* Parse the header and generate derived keys. */ if ((rc = scryptdec_setup(C->header, C->dk, passwd, passwdlen, P, verbose, force)) != 0) goto err1; /* Set cookie for calling function. */ *cookie = C; /* Success! */ return (SCRYPT_OK); err1: scryptdec_file_cookie_free(C); /* Failure! */ return (rc); } /** * scryptdec_file_copy(cookie, outfile): * Read a stream from the file that was passed into the ${cookie} by * scryptdec_file_prep(), decrypt it, and write the resulting stream to * ${outfile}. After this function completes, it is safe to modify/close * ${outfile} and the ${infile} which was given to scryptdec_file_prep(). */ int scryptdec_file_copy(struct scryptdec_file_cookie * C, FILE * outfile) { uint8_t buf[ENCBLOCK + 32]; uint8_t hbuf[32]; uint8_t * key_enc; uint8_t * key_hmac; size_t buflen = 0; size_t readlen; HMAC_SHA256_CTX hctx; struct crypto_aes_key * key_enc_exp; struct crypto_aesctr * AES; int rc; /* Sanity check. */ assert(C != NULL); /* Use existing array for these pointers. */ key_enc = C->dk; key_hmac = &C->dk[32]; /* Start hashing with the header. */ HMAC_SHA256_Init(&hctx, key_hmac, 32); HMAC_SHA256_Update(&hctx, C->header, 96); /* * We don't know how long the encrypted data block is (we can't know, * since data can be streamed into 'scrypt enc') so we need to read * data and decrypt all of it except the final 32 bytes, then check * if that final 32 bytes is the correct signature. */ if ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) { rc = SCRYPT_EOPENSSL; goto err0; } if ((AES = crypto_aesctr_init(key_enc_exp, 0)) == NULL) { crypto_aes_key_free(key_enc_exp); rc = SCRYPT_ENOMEM; goto err0; } do { /* Read data until we have more than 32 bytes of it. */ if ((readlen = fread(&buf[buflen], 1, ENCBLOCK + 32 - buflen, C->infile)) == 0) break; buflen += readlen; if (buflen <= 32) continue; /* * Decrypt, hash, and output everything except the last 32 * bytes out of what we have in our buffer. */ HMAC_SHA256_Update(&hctx, buf, buflen - 32); crypto_aesctr_stream(AES, buf, buf, buflen - 32); if (fwrite(buf, 1, buflen - 32, outfile) < buflen - 32) { crypto_aesctr_free(AES); rc = SCRYPT_EWRFILE; goto err0; } /* Move the last 32 bytes to the start of the buffer. */ memmove(buf, &buf[buflen - 32], 32); buflen = 32; } while (1); crypto_aesctr_free(AES); crypto_aes_key_free(key_enc_exp); /* Did we exit the loop due to a read error? */ if (ferror(C->infile)) { rc = SCRYPT_ERDFILE; goto err0; } /* Did we read enough data that we *might* have a valid signature? */ if (buflen < 32) { rc = SCRYPT_EINVAL; goto err0; } /* Verify signature. */ HMAC_SHA256_Final(hbuf, &hctx); if (crypto_verify_bytes(hbuf, buf, 32)) { rc = SCRYPT_EINVAL; goto err0; } /* Success! */ return (SCRYPT_OK); err0: /* Failure! */ return (rc); } /** * scryptdec_file(infile, outfile, passwd, passwdlen, params, verbose, force): * Read a stream from ${infile} and decrypt it, writing the resulting stream * to ${outfile}. If ${force} is 1, do not check whether decryption * will exceed the estimated available memory or time. The explicit * parameters within ${params} must be zero. Return the explicit parameters * used via ${params}. */ int scryptdec_file(FILE * infile, FILE * outfile, const uint8_t * passwd, size_t passwdlen, struct scryptenc_params * P, int verbose, int force) { struct scryptdec_file_cookie * C; int rc; /* The explicit parameters must be zero. */ assert((P->logN == 0) && (P->r == 0) && (P->p == 0)); /* Check header, including passphrase. */ if ((rc = scryptdec_file_prep(infile, passwd, passwdlen, P, verbose, force, &C)) != 0) goto err0; /* Copy unencrypted data to outfile. */ if ((rc = scryptdec_file_copy(C, outfile)) != 0) goto err1; /* Clean up cookie, attempting to zero sensitive data. */ scryptdec_file_cookie_free(C); /* Success! */ return (SCRYPT_OK); err1: scryptdec_file_cookie_free(C); err0: /* Failure! */ return (rc); } tarsnap-1.0.41/lib/scryptenc/scryptenc.h000066400000000000000000000202071476705112600202220ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef SCRYPTENC_H_ #define SCRYPTENC_H_ #include #include /** * NOTE: This file provides prototypes for routines which encrypt/decrypt data * using a key derived from a password by using the scrypt key derivation * function. If you are just trying to "hash" a password for user logins, * this is not the code you are looking for. You want to use the * crypto_scrypt() function directly. */ /** * The parameters maxmem, maxmemfrac, and maxtime used by all of these * functions are defined as follows: * maxmem - maximum number of bytes of storage to use for V array (which is * by far the largest consumer of memory). If this value is set to 0, no * maximum will be enforced; any other value less than 1 MiB will be * treated as 1 MiB. * maxmemfrac - maximum fraction of available storage to use for the V array, * where "available storage" is defined as the minimum out of the * RLIMIT_AS, RLIMIT_DATA. and RLIMIT_RSS resource limits (if any are * set). This value will never cause a limit of less than 1 MiB to * be enforced. * maxtime - maximum amount of CPU time to spend computing the derived keys, * in seconds. This limit is only approximately enforced; the CPU * performance is estimated and parameter limits are chosen accordingly. * For the encryption functions, the parameters to the scrypt key derivation * function are chosen to make the key as strong as possible subject to the * specified limits; for the decryption functions, the parameters used are * compared to the computed limits and an error is returned if decrypting * the data would take too much memory or CPU time. */ struct scryptenc_params { size_t maxmem; double maxmemfrac; double maxtime; /* Explicit parameters. */ int logN; uint32_t r; uint32_t p; }; /* Return codes from scrypt(enc|dec)_(buf|file|prep). */ #define SCRYPT_OK 0 /* success */ #define SCRYPT_ELIMIT 1 /* getrlimit or sysctrl(hw.usermem) failed */ #define SCRYPT_ECLOCK 2 /* clock_getres or clock_gettime failed */ #define SCRYPT_EKEY 3 /* error computing derived key */ #define SCRYPT_ESALT 4 /* could not read salt */ #define SCRYPT_EOPENSSL 5 /* error in OpenSSL */ #define SCRYPT_ENOMEM 6 /* malloc failed */ #define SCRYPT_EINVAL 7 /* data is not a valid scrypt-encrypted block */ #define SCRYPT_EVERSION 8 /* unrecognized scrypt version number */ #define SCRYPT_ETOOBIG 9 /* decrypting would take too much memory */ #define SCRYPT_ETOOSLOW 10 /* decrypting would take too long */ #define SCRYPT_EPASS 11 /* password is incorrect */ #define SCRYPT_EWRFILE 12 /* error writing output file */ #define SCRYPT_ERDFILE 13 /* error reading input file */ #define SCRYPT_EPARAM 14 /* error in explicit parameters */ #define SCRYPT_EBIGSLOW 15 /* both SCRYPT_ETOOBIG and SCRYPT_ETOOSLOW */ /* Opaque structure. */ struct scryptdec_file_cookie; /** * scryptenc_buf(inbuf, inbuflen, outbuf, passwd, passwdlen, * params, verbose, force): * Encrypt ${inbuflen} bytes from ${inbuf}, writing the resulting * ${inbuflen} + 128 bytes to ${outbuf}. If ${force} is 1, do not check * whether decryption will exceed the estimated available memory or time. * The explicit parameters within ${params} must be zero or must all be * non-zero. If explicit parameters are used and the computation is estimated * to exceed resource limits, print a warning instead of returning an error. * Return the explicit parameters used via ${params}. */ int scryptenc_buf(const uint8_t *, size_t, uint8_t *, const uint8_t *, size_t, struct scryptenc_params *, int, int); /** * scryptdec_buf(inbuf, inbuflen, outbuf, outlen, passwd, passwdlen, * params, verbose, force): * Decrypt ${inbuflen} bytes from ${inbuf}, writing the result into ${outbuf} * and the decrypted data length to ${outlen}. The allocated length of * ${outbuf} must be at least ${inbuflen}. If ${force} is 1, do not check * whether decryption will exceed the estimated available memory or time. * The explicit parameters within ${params} must be zero. Return the explicit * parameters used via ${params}. */ int scryptdec_buf(const uint8_t *, size_t, uint8_t *, size_t *, const uint8_t *, size_t, struct scryptenc_params *, int, int); /** * scryptenc_file(infile, outfile, passwd, passwdlen, params, verbose, force): * Read a stream from ${infile} and encrypt it, writing the resulting stream * to ${outfile}. If ${force} is 1, do not check whether decryption will * exceed the estimated available memory or time. The explicit parameters * within ${params} must be zero or must all be non-zero. If explicit * parameters are used and the computation is estimated to exceed resource * limits, print a warning instead of returning an error. Return the explicit * parameters used via ${params}. */ int scryptenc_file(FILE *, FILE *, const uint8_t *, size_t, struct scryptenc_params *, int, int); /** * scryptdec_file_printparams(infile): * Print the encryption parameters (N, r, p) used for the encrypted ${infile}. */ int scryptdec_file_printparams(FILE *); /** * scryptdec_file(infile, outfile, passwd, passwdlen, params, verbose, force): * Read a stream from ${infile} and decrypt it, writing the resulting stream * to ${outfile}. If ${force} is 1, do not check whether decryption * will exceed the estimated available memory or time. The explicit * parameters within ${params} must be zero. Return the explicit parameters * used via ${params}. */ int scryptdec_file(FILE *, FILE *, const uint8_t *, size_t, struct scryptenc_params *, int, int); /** * scryptdec_file_prep(infile, passwd, passwdlen, params, verbose, force, * cookie): * Prepare to decrypt ${infile}, including checking the passphrase. Allocate * a cookie at ${cookie}. After calling this function, ${infile} should not * be modified until the decryption is completed by scryptdec_file_copy(). * If ${force} is 1, do not check whether decryption will exceed the estimated * available memory or time. The explicit parameters within ${params} must be * zero. Return the explicit parameters to be used via ${params}. */ int scryptdec_file_prep(FILE *, const uint8_t *, size_t, struct scryptenc_params *, int, int, struct scryptdec_file_cookie **); /** * scryptdec_file_copy(cookie, outfile): * Read a stream from the file that was passed into the ${cookie} by * scryptdec_file_prep(), decrypt it, and write the resulting stream to * ${outfile}. After this function completes, it is safe to modify/close * ${outfile} and the ${infile} which was given to scryptdec_file_prep(). */ int scryptdec_file_copy(struct scryptdec_file_cookie *, FILE *); /** * scryptdec_file_cookie_free(cookie): * Free the ${cookie}. */ void scryptdec_file_cookie_free(struct scryptdec_file_cookie *); #endif /* !SCRYPTENC_H_ */ tarsnap-1.0.41/lib/scryptenc/scryptenc_cpuperf.c000066400000000000000000000063221476705112600217430ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include #include #include #include "crypto_scrypt.h" #include "monoclock.h" #include "scryptenc.h" #include "scryptenc_cpuperf.h" static int getclockdiff(struct timeval * st, double * diffd) { struct timeval en; if (monoclock_get(&en)) return (1); *diffd = timeval_diff((*st), en); /* Success! */ return (0); } /** * scryptenc_cpuperf(opps): * Estimate the number of salsa20/8 cores which can be executed per second, * and store the value in ${opps}. Return a SCRYPT_* code. */ int scryptenc_cpuperf(double * opps) { struct timeval st; double resd, diffd; uint64_t i = 0; /* Get the clock resolution. */ if (monoclock_getres(&resd)) return (SCRYPT_ECLOCK); #ifdef DEBUG fprintf(stderr, "Clock resolution is %g\n", resd); #endif /* Loop until the clock ticks. */ if (monoclock_get(&st)) return (SCRYPT_ECLOCK); do { /* Do an scrypt. */ if (crypto_scrypt(NULL, 0, NULL, 0, 16, 1, 1, NULL, 0)) return (SCRYPT_EKEY); /* Has the clock ticked? */ if (getclockdiff(&st, &diffd)) return (SCRYPT_ECLOCK); if (diffd > 0) break; } while (1); /* Count how many scrypts we can do before the next tick. */ if (monoclock_get(&st)) return (SCRYPT_ECLOCK); do { /* Do an scrypt. */ if (crypto_scrypt(NULL, 0, NULL, 0, 128, 1, 1, NULL, 0)) return (SCRYPT_EKEY); /* We invoked the salsa20/8 core 512 times. */ i += 512; /* Check if we have looped for long enough. */ if (getclockdiff(&st, &diffd)) return (SCRYPT_ECLOCK); if (diffd > resd) break; } while (1); #ifdef DEBUG fprintf(stderr, "%ju salsa20/8 cores performed in %g seconds\n", (uintmax_t)i, diffd); #endif /* We can do approximately i salsa20/8 cores per diffd seconds. */ *opps = (double)i / diffd; /* Success! */ return (SCRYPT_OK); } tarsnap-1.0.41/lib/scryptenc/scryptenc_cpuperf.h000066400000000000000000000033471476705112600217540ustar00rootroot00000000000000/*- * Copyright 2009-2025 Tarsnap Backup Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef SCRYPTENC_CPUPERF_H_ #define SCRYPTENC_CPUPERF_H_ /** * scryptenc_cpuperf(opps): * Estimate the number of salsa20/8 cores which can be executed per second, * and store the value in ${opps}. Return a SCRYPT_* code. */ int scryptenc_cpuperf(double *); #endif /* !SCRYPTENC_CPUPERF_H_ */ tarsnap-1.0.41/lib/util/000077500000000000000000000000001476705112600150015ustar00rootroot00000000000000tarsnap-1.0.41/lib/util/dirutil.c000066400000000000000000000055641476705112600166330ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "asprintf.h" #include "warnp.h" #include "dirutil.h" /** * XXX Portability * XXX This function should ensure that in the sequence of events * XXX 1. Creation/link/unlink of a file in/to/from the directory X, * XXX 2. dirutil_fsyncdir(X), * XXX 3. Creation/link/unlink of a file anywhere else, * XXX the system can never (even in the event of power failure) have step 3 * XXX take place but not step 1. * XXX * XXX Calling fsync on the directory X is reported to be sufficient to * XXX achieve this on all widely used systems (although not necessary on * XXX all of them), but this should be reviewed when porting this code. */ /** * dirutil_fsyncdir(path): * Call fsync on the directory ${path}. */ int dirutil_fsyncdir(const char * path) { int fd; /* Open the directory read-only. */ if ((fd = open(path, O_RDONLY)) == -1) { warnp("open(%s)", path); return (-1); } /* Call fsync. */ if (fsync(fd)) { warnp("fsync(%s)", path); if (close(fd)) warnp("close"); return (-1); } /* Close the descriptor. */ if (close(fd)) { warnp("close(%s)", path); return (-1); } /* Success! */ return (0); } /** * build_dir(dir, diropt): * Make sure that ${dir} exists, creating it (and any parents) as necessary. */ int build_dir(const char * dir, const char * diropt) { struct stat sb; char * s; const char * dirseppos; /* We need a directory name and the config option. */ assert(dir != NULL); assert(diropt != NULL); /* Move through *dir and build all parent directories. */ for (dirseppos = dir; *dirseppos != '\0'; ) { /* Move to the next '/', or the end of the string. */ if ((dirseppos = strchr(dirseppos + 1, '/')) == NULL) dirseppos = dir + strlen(dir); /* Generate a string containing the parent directory. */ if (asprintf(&s, "%.*s", (int)(dirseppos - dir), dir) == -1) { warnp("No memory"); goto err0; } /* Does the parent directory exist already? */ if (stat(s, &sb) == 0) goto nextdir; /* Did something go wrong? */ if (errno != ENOENT) { warnp("stat(%s)", s); goto err1; } /* Create the directory. */ if (mkdir(s, 0700)) { warnp("Cannot create directory: %s", s); goto err1; } /* Tell the user what we did. */ fprintf(stderr, "Directory %s created for \"%s %s\"\n", s, diropt, dir); nextdir: free(s); } /* Make sure permissions on the directory are correct. */ if (stat(dir, &sb)) { warnp("stat(%s)", dir); goto err0; } if (sb.st_mode & (S_IRWXG | S_IRWXO)) { if (chmod(dir, sb.st_mode & (mode_t)(~(S_IRWXG | S_IRWXO)))) { warnp("Cannot sanitize permissions on directory: %s", dir); goto err0; } } /* Success! */ return (0); err1: free(s); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/util/dirutil.h000066400000000000000000000005141476705112600166260ustar00rootroot00000000000000#ifndef DIRUTIL_H_ #define DIRUTIL_H_ /** * dirutil_fsyncdir(path): * Call fsync on the directory ${path}. */ int dirutil_fsyncdir(const char *); /** * build_dir(dir, diropt): * Make sure that ${dir} exists, creating it (and any parents) as necessary. */ int build_dir(const char *, const char *); #endif /* !DIRUTIL_H_ */ tarsnap-1.0.41/lib/util/hexlink.c000066400000000000000000000037411476705112600166140ustar00rootroot00000000000000#include #include #include #include #include #include "hexify.h" #include "warnp.h" #include "hexlink.h" /** * hexlink_write(path, buf, buflen): * Convert ${buf} (of length ${buflen}) into hexadecimal and create a link * from ${path} pointing at it. */ int hexlink_write(const char * path, const uint8_t * buf, size_t buflen) { char * hexbuf; /* Allocate memory for buffer. */ if ((hexbuf = malloc(buflen * 2 + 1)) == NULL) goto err0; /* Convert ${buf} to hex. */ hexify(buf, hexbuf, buflen); /* Create the symlink. */ if (symlink(hexbuf, path)) { warnp("symlink(%s, %s)", hexbuf, path); goto err1; } /* Free allocated memory. */ free(hexbuf); /* Success! */ return (0); err1: free(hexbuf); err0: /* Failure! */ return (-1); } /** * hexlink_read(path, buf, buflen): * Read the link ${path}, which should point to a hexadecimal string of * length 2 * ${buflen}; and parse this into the provided buffer. In the * event of an error, return with errno == ENOENT iff the link does not * exist. */ int hexlink_read(const char * path, uint8_t * buf, size_t buflen) { char * hexbuf; ssize_t rc; int saved_errno = 0; /* Allocate memory for buffer. */ if ((hexbuf = malloc(buflen * 2 + 1)) == NULL) goto err0; /* Attempt to read the link. */ if ((rc = readlink(path, hexbuf, buflen * 2)) == -1) { /* Couldn't read the link. */ saved_errno = errno; goto err1; } /* Is the link the correct length? */ if ((size_t)rc != (buflen * 2)) { warn0("Link is incorrect length: %s", path); goto err1; } /* NUL-terminate. */ hexbuf[rc] = '\0'; /* Parse the link value into the provided buffer. */ if (unhexify(hexbuf, buf, buflen)) { warn0("Cannot parse link as hexadecimal: %s -> %s", path, hexbuf); goto err1; } /* Free allocated memory. */ free(hexbuf); /* Success! */ return (0); err1: free(hexbuf); /* Restore saved errno. */ errno = saved_errno; err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/util/hexlink.h000066400000000000000000000012001476705112600166050ustar00rootroot00000000000000#ifndef HEXLINK_H_ #define HEXLINK_H_ #include #include /** * hexlink_write(path, buf, buflen): * Convert ${buf} (of length ${buflen}) into hexadecimal and create a link * from ${path} pointing at it. */ int hexlink_write(const char *, const uint8_t *, size_t); /** * hexlink_read(path, buf, buflen): * Read the link ${path}, which should point to a hexadecimal string of * length 2 * ${buflen}; and parse this into the provided buffer. In the * event of an error, return with errno == ENOENT iff the link does not * exist. */ int hexlink_read(const char *, uint8_t *, size_t); #endif /* !HEXLINK_H_ */ tarsnap-1.0.41/lib/util/passphrase_entry.c000066400000000000000000000062261476705112600205450ustar00rootroot00000000000000#include #include #include "passphrase_entry.h" #include "readpass.h" #include "warnp.h" /** * passphrase_entry_parse(arg, entry_method_p, entry_arg_p): * Parse "METHOD:ARG" from ${arg} into an ${*entry_method_p}:${*entry_arg_p}. */ int passphrase_entry_parse(const char * arg, enum passphrase_entry * passphrase_entry_p, const char ** passphrase_arg_p) { const char * p; /* Find the separator in "method:arg", or fail if there isn't one. */ if ((p = strchr(arg, ':')) == NULL) goto err1; /* Extract the "arg" part. */ *passphrase_arg_p = &p[1]; /* Parse the "method". */ if (strncmp(arg, "dev:", 4) == 0) { if (strcmp(*passphrase_arg_p, "tty-stdin") == 0) { *passphrase_entry_p = PASSPHRASE_TTY_STDIN; goto success; } else if (strcmp(*passphrase_arg_p, "stdin-once") == 0) { *passphrase_entry_p = PASSPHRASE_STDIN_ONCE; goto success; } else if (strcmp(*passphrase_arg_p, "tty-once") == 0) { *passphrase_entry_p = PASSPHRASE_TTY_ONCE; goto success; } } if (strncmp(arg, "env:", 4) == 0) { *passphrase_entry_p = PASSPHRASE_ENV; goto success; } if (strncmp(arg, "file:", 5) == 0) { *passphrase_entry_p = PASSPHRASE_FILE; goto success; } err1: warn0("Invalid option: --passphrase %s", arg); /* Failure! */ return (-1); success: /* Success! */ return (0); } /** * passphrase_entry_readpass(passwd, entry_method, entry_arg, prompt, * confirmprompt, once): * Use ${entry_method} to read a passphrase and return it as a malloced * NUL-terminated string via ${passwd}. If ${entry_method} is * PASSPHRASE_TTY_STDIN and ${once} is zero, ask for the passphrase twice; * otherwise ask for it once. If reading from a terminal, use ${prompt} for * the first prompt, and ${confirmprompt} for the second prompt (if * applicable); otherwise do not print any prompts. */ int passphrase_entry_readpass(char ** passwd, enum passphrase_entry passphrase_entry, const char * passphrase_arg, const char * prompt, const char * confirmprompt, int once) { const char * passwd_env; switch (passphrase_entry) { case PASSPHRASE_TTY_STDIN: /* Read passphrase, prompting only once if decrypting. */ if (readpass(passwd, prompt, (once) ? NULL : confirmprompt, 1)) goto err0; break; case PASSPHRASE_STDIN_ONCE: /* Read passphrase, prompting only once, from stdin only. */ if (readpass(passwd, prompt, NULL, 0)) goto err0; break; case PASSPHRASE_TTY_ONCE: /* Read passphrase, prompting only once, from tty only. */ if (readpass(passwd, prompt, NULL, 2)) goto err0; break; case PASSPHRASE_ENV: /* We're not allowed to modify the output of getenv(). */ if ((passwd_env = getenv(passphrase_arg)) == NULL) { warn0("Failed to read from ${%s}", passphrase_arg); goto err0; } /* This allows us to use the same insecure_zero() logic. */ if ((*passwd = strdup(passwd_env)) == NULL) { warnp("Out of memory"); goto err0; } break; case PASSPHRASE_FILE: if (readpass_file(passwd, passphrase_arg)) goto err0; break; case PASSPHRASE_UNSET: warn0("Programming error: passphrase_entry is not set"); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/util/passphrase_entry.h000066400000000000000000000021711476705112600205450ustar00rootroot00000000000000#ifndef PASSPHRASE_ENTRY_H_ #define PASSPHRASE_ENTRY_H_ /* How should we get the passphrase? */ enum passphrase_entry { PASSPHRASE_UNSET, PASSPHRASE_TTY_STDIN, PASSPHRASE_STDIN_ONCE, PASSPHRASE_TTY_ONCE, PASSPHRASE_ENV, PASSPHRASE_FILE, }; /** * passphrase_entry_parse(arg, entry_method_p, entry_arg_p): * Parse "METHOD:ARG" from ${arg} into an ${*entry_method_p}:${*entry_arg_p}. */ int passphrase_entry_parse(const char *, enum passphrase_entry *, const char **); /** * passphrase_entry_readpass(passwd, entry_method, entry_arg, prompt, * confirmprompt, once): * Use ${entry_method} to read a passphrase and return it as a malloced * NUL-terminated string via ${passwd}. If ${entry_method} is * PASSPHRASE_TTY_STDIN and ${once} is zero, ask for the passphrase twice; * otherwise ask for it once. If reading from a terminal, use ${prompt} for * the first prompt, and ${confirmprompt} for the second prompt (if * applicable); otherwise do not print any prompts. */ int passphrase_entry_readpass(char **, enum passphrase_entry, const char *, const char *, const char *, int); #endif /* !PASSPHRASE_ENTRY_H_ */ tarsnap-1.0.41/lib/util/print_separator.c000066400000000000000000000013131476705112600203570ustar00rootroot00000000000000#include #include "warnp.h" #include "print_separator.h" /** * print_separator(stream, separator, print_nulls, num_nulls): * Print ${separator} to ${stream} if ${print_nulls} is zero; otherwise, * print ${num_nulls} '\0'. */ int print_separator(FILE * stream, const char * separator, int print_nulls, int num_nulls) { int i; /* Are we printing normal separators, or NULs? */ if (print_nulls == 0) { if (fprintf(stream, "%s", separator) < 0) { warnp("fprintf"); goto err0; } } else { for (i = 0; i < num_nulls; i++) { if (fprintf(stream, "%c", '\0') < 0) { warnp("fprintf"); goto err0; } } } /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/util/print_separator.h000066400000000000000000000005161476705112600203700ustar00rootroot00000000000000#ifndef PRINT_SEPARATOR_H_ #define PRINT_SEPARATOR_H_ #include /** * print_separator(stream, separator, print_nulls, num_nulls): * Print ${separator} to ${stream} if ${print_nulls} is zero; otherwise, * print ${num_nulls} '\0'. */ int print_separator(FILE *, const char *, int, int); #endif /* !PRINT_SEPARATOR_H_ */ tarsnap-1.0.41/lib/util/sigquit.c000066400000000000000000000073331476705112600166400ustar00rootroot00000000000000#include #include #include #include #include #include #include "ttyfd.h" #include "warnp.h" #include "sigquit.h" /* Set to a non-zero value when SIGQUIT is received. */ sig_atomic_t sigquit_received; /* Saved terminal settings. */ static struct termios tc_saved; static int fd_terminal; static void sigquit_handler(int); static void termios_restore(void); static int tcsetattr_nostop(int, int, const struct termios *); /** * sigquit_handler(sig): * Record that SIGQUIT has been received. */ static void sigquit_handler(int sig) { (void)sig; /* UNUSED */ sigquit_received = 1; } /** * termios_restore(void): * Restore the saved tc_saved termios state. */ static void termios_restore(void) { /* * Discard return value; we're exiting anyway and there's nothing * that we can do to remedy the situation if the system cannot * restore the previous terminal settings. */ (void)tcsetattr_nostop(fd_terminal, TCSANOW, &tc_saved); /* * Close the terminal file descriptor we opened a long time ago. We * had to hold it for the duration in case we find ourselves talking * to a different terminal; we don't want to restore settings to the * wrong terminal. */ if (close(fd_terminal)) warnp("close"); } /** * Call tcsetattr(3), but block SIGTTOU while doing so in order to avoid * being stopped if backgrounded. */ static int tcsetattr_nostop(int fd, int action, const struct termios *t) { void (* oldsig)(int); int rc; if ((oldsig = signal(SIGTTOU, SIG_IGN)) == SIG_ERR) goto err0; rc = tcsetattr(fd, action, t); if (signal(SIGTTOU, oldsig) == SIG_ERR) goto err0; /* Return status code from tcsetattr. */ return (rc); err0: /* Failure! */ return (-1); } /** * sigquit_init(void): * Prepare to catch SIGQUIT and ^Q, and zero sigquit_received. */ int sigquit_init(void) { struct sigaction sa; struct termios tc_new; size_t i; /* We haven't seen SIGQUIT yet... */ sigquit_received = 0; /* ... but when it happens, we want to catch it. */ sa.sa_handler = sigquit_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGQUIT, &sa, NULL)) { warnp("sigaction(SIGQUIT)"); goto err0; } /* Try to get a file descriptor for the terminal. */ if ((fd_terminal = ttyfd()) == -1) { /* * This is normal for processes which don't have a terminal * attached (e.g., if running from cron), so we shouldn't * print a warning, even though ^Q is going to break. */ goto done; } /* Get current terminal settings for stdin. */ if (tcgetattr(fd_terminal, &tc_saved)) { /* * Theoretically we should have a terminal now, but we can't * rule out the possibility of some weird "sort of a terminal * but not really" behaviour making tcgetattr fail even though * we were previously told that this was a terminal. Check for * the common error codes operating systems return from * tcgetattr for non-terminals and ignore them. */ if ((errno == ENOTTY) || (errno == ENXIO) || (errno == EBADF) || (errno == EINVAL) || (errno == ENODEV)) goto done; warnp("tcgetattr(stdin)"); goto err0; } /* Restore the terminal settings on exit(3). */ if (atexit(termios_restore)) { warn("atexit"); goto err0; } /* Copy terminal settings. */ memcpy(&tc_new, &tc_saved, sizeof(struct termios)); /* Remove any meaning which ^Q already has. */ for (i = 0; i < NCCS; i++) { if (tc_new.c_cc[i] == ('q' & 0x1f)) tc_new.c_cc[i] = _POSIX_VDISABLE; } /* Set VQUIT to ^Q. */ tc_new.c_cc[VQUIT] = 'q' & 0x1f; /* Set new terminal settings. */ if (tcsetattr_nostop(fd_terminal, TCSANOW, &tc_new)) { warnp("tcsetattr(stdin)"); goto err0; } done: /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/util/sigquit.h000066400000000000000000000004531476705112600166410ustar00rootroot00000000000000#ifndef SIGQUIT_H_ #define SIGQUIT_H_ #include /* Set to a non-zero value when SIGQUIT is received. */ extern sig_atomic_t sigquit_received; /** * sigquit_init(void): * Prepare to catch SIGQUIT and ^Q, and zero sigquit_received. */ int sigquit_init(void); #endif /* !SIGQUIT_H_ */ tarsnap-1.0.41/lib/util/tvmath.c000066400000000000000000000022471476705112600164550ustar00rootroot00000000000000#include #include "monoclock.h" #include "tvmath.h" /** * tvmath_addctime(tv): * Set tv += monoclock. */ int tvmath_addctime(struct timeval * tv) { struct timeval tnow; if (monoclock_get(&tnow)) goto err0; tv->tv_sec += tnow.tv_sec; tv->tv_usec += tnow.tv_usec; if (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec += 1; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * tvmath_subctime(tv): * Set tv -= monoclock. */ int tvmath_subctime(struct timeval * tv) { struct timeval tnow; if (monoclock_get(&tnow)) goto err0; tv->tv_sec -= tnow.tv_sec; tv->tv_usec -= tnow.tv_usec; if (tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec -= 1; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * tvmath_rsubctime(tv): * Set tv = monoclock - tv. */ int tvmath_rsubctime(struct timeval * tv) { struct timeval tnow; if (monoclock_get(&tnow)) goto err0; tv->tv_sec = tnow.tv_sec - tv->tv_sec; tv->tv_usec = tnow.tv_usec - tv->tv_usec; if (tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec -= 1; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/lib/util/tvmath.h000066400000000000000000000005721476705112600164610ustar00rootroot00000000000000#ifndef TVMATH_H_ #define TVMATH_H_ #include /** * tvmath_addctime(tv): * Set tv += monoclock. */ int tvmath_addctime(struct timeval *); /** * tvmath_subctime(tv): * Set tv -= monoclock. */ int tvmath_subctime(struct timeval *); /** * tvmath_rsubctime(tv): * Set tv = monoclock - tv. */ int tvmath_rsubctime(struct timeval *); #endif /* !TVMATH_H_ */ tarsnap-1.0.41/libarchive/000077500000000000000000000000001476705112600153665ustar00rootroot00000000000000tarsnap-1.0.41/libarchive/archive.h000066400000000000000000000764101476705112600171700ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $ */ #ifndef ARCHIVE_H_INCLUDED #define ARCHIVE_H_INCLUDED /* * Note: archive.h is for use outside of libarchive; the configuration * headers (config.h, archive_platform.h, etc.) are purely internal. * Do NOT use HAVE_XXX configuration macros to control the behavior of * this header! If you must conditionalize, use predefined compiler and/or * platform macros. */ #include #include /* Linux requires this for off_t */ #if !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) /* Header unavailable on Watcom C or MS Visual C++ or SFU. */ #include /* int64_t, etc. */ #endif #include /* For FILE * */ /* Get appropriate definitions of standard POSIX-style types. */ /* These should match the types used in 'struct stat' */ #if defined(_WIN32) && !defined(__CYGWIN__) #define __LA_INT64_T __int64 # if defined(_WIN64) # define __LA_SSIZE_T __int64 # else # define __LA_SSIZE_T long # endif #define __LA_UID_T unsigned int #define __LA_GID_T unsigned int #else #include /* ssize_t, uid_t, and gid_t */ #define __LA_INT64_T int64_t #define __LA_SSIZE_T ssize_t #define __LA_UID_T uid_t #define __LA_GID_T gid_t #endif /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ #if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL __attribute__((dllimport)) extern # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries or non-Windows needs no special declaration. */ # define __LA_DECL #endif #ifdef __cplusplus extern "C" { #endif /* * The version number is provided as both a macro and a function. * The macro identifies the installed header; the function identifies * the library version (which may not be the same if you're using a * dynamically-linked version of the library). Of course, if the * header and library are very different, you should expect some * strangeness. Don't do that. */ /* * The version number is expressed as a single integer that makes it * easy to compare versions at build time: for version a.b.c, the * version number is printf("%d%03d%03d",a,b,c). For example, if you * know your application requires version 2.12.108 or later, you can * assert that ARCHIVE_VERSION >= 2012108. * * This single-number format was introduced with libarchive 1.9.0 in * the libarchive 1.x family and libarchive 2.2.4 in the libarchive * 2.x family. The following may be useful if you really want to do * feature detection for earlier libarchive versions (which defined * ARCHIVE_API_VERSION and ARCHIVE_API_FEATURE instead): * * #ifndef ARCHIVE_VERSION_NUMBER * #define ARCHIVE_VERSION_NUMBER \ * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) * #endif */ #define ARCHIVE_VERSION_NUMBER 2007000 __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ #define ARCHIVE_VERSION_STRING "libarchive 2.7.0" __LA_DECL const char * archive_version_string(void); #if ARCHIVE_VERSION_NUMBER < 3000000 /* * Deprecated; these are older names that will be removed in favor of * the simpler definitions above. */ #define ARCHIVE_VERSION_STAMP ARCHIVE_VERSION_NUMBER __LA_DECL int archive_version_stamp(void); #define ARCHIVE_LIBRARY_VERSION ARCHIVE_VERSION_STRING __LA_DECL const char * archive_version(void); #define ARCHIVE_API_VERSION (ARCHIVE_VERSION_NUMBER / 1000000) __LA_DECL int archive_api_version(void); #define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_NUMBER / 1000) % 1000) __LA_DECL int archive_api_feature(void); #endif #if ARCHIVE_VERSION_NUMBER < 3000000 /* This should never have been here in the first place. */ /* Legacy of old tar assumptions, will be removed in libarchive 3.0. */ #define ARCHIVE_BYTES_PER_RECORD 512 #define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240 #endif /* Declare our basic types. */ struct archive; struct archive_entry; /* * Error codes: Use archive_errno() and archive_error_string() * to retrieve details. Unless specified otherwise, all functions * that return 'int' use these codes. */ #define ARCHIVE_EOF 1 /* Found end of archive. */ #define ARCHIVE_OK 0 /* Operation was successful. */ #define ARCHIVE_RETRY (-10) /* Retry might succeed. */ #define ARCHIVE_WARN (-20) /* Partial success. */ /* For example, if write_header "fails", then you can't push data. */ #define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */ /* But if write_header is "fatal," then this archive is dead and useless. */ #define ARCHIVE_FATAL (-30) /* No more operations are possible. */ /* * As far as possible, archive_errno returns standard platform errno codes. * Of course, the details vary by platform, so the actual definitions * here are stored in "archive_platform.h". The symbols are listed here * for reference; as a rule, clients should not need to know the exact * platform-dependent error code. */ /* Unrecognized or invalid file format. */ /* #define ARCHIVE_ERRNO_FILE_FORMAT */ /* Illegal usage of the library. */ /* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */ /* Unknown or unclassified error. */ /* #define ARCHIVE_ERRNO_MISC */ /* * Callbacks are invoked to automatically read/skip/write/open/close the * archive. You can provide your own for complex tasks (like breaking * archives across multiple tapes) or use standard ones built into the * library. */ /* Returns pointer and size of next block of data from archive. */ typedef __LA_SSIZE_T archive_read_callback(struct archive *, void *_client_data, const void **_buffer); /* Skips at most request bytes from archive and returns the skipped amount */ #if ARCHIVE_VERSION_NUMBER < 2000000 /* Libarchive 1.0 used ssize_t for the return, which is only 32 bits * on most 32-bit platforms; not large enough. */ typedef __LA_SSIZE_T archive_skip_callback(struct archive *, void *_client_data, size_t request); #elif ARCHIVE_VERSION_NUMBER < 3000000 /* Libarchive 2.0 used off_t here, but that is a bad idea on Linux and a * few other platforms where off_t varies with build settings. */ typedef off_t archive_skip_callback(struct archive *, void *_client_data, off_t request); #else /* Libarchive 3.0 uses int64_t here, which is actually guaranteed to be * 64 bits on every platform. */ typedef __LA_INT64_T archive_skip_callback(struct archive *, void *_client_data, __LA_INT64_T request); #endif /* Returns size actually written, zero on EOF, -1 on error. */ typedef __LA_SSIZE_T archive_write_callback(struct archive *, void *_client_data, const void *_buffer, size_t _length); #if ARCHIVE_VERSION_NUMBER < 3000000 /* Open callback is actually never needed; remove it in libarchive 3.0. */ typedef int archive_open_callback(struct archive *, void *_client_data); #endif typedef int archive_close_callback(struct archive *, void *_client_data); /* * Codes for archive_compression. */ #define ARCHIVE_COMPRESSION_NONE 0 #define ARCHIVE_COMPRESSION_GZIP 1 #define ARCHIVE_COMPRESSION_BZIP2 2 #define ARCHIVE_COMPRESSION_COMPRESS 3 #define ARCHIVE_COMPRESSION_PROGRAM 4 #define ARCHIVE_COMPRESSION_LZMA 5 #define ARCHIVE_COMPRESSION_XZ 6 /* * Codes returned by archive_format. * * Top 16 bits identifies the format family (e.g., "tar"); lower * 16 bits indicate the variant. This is updated by read_next_header. * Note that the lower 16 bits will often vary from entry to entry. * In some cases, this variation occurs as libarchive learns more about * the archive (for example, later entries might utilize extensions that * weren't necessary earlier in the archive; in this case, libarchive * will change the format code to indicate the extended format that * was used). In other cases, it's because different tools have * modified the archive and so different parts of the archive * actually have slightly different formats. (Both tar and cpio store * format codes in each entry, so it is quite possible for each * entry to be in a different format.) */ #define ARCHIVE_FORMAT_BASE_MASK 0xff0000 #define ARCHIVE_FORMAT_CPIO 0x10000 #define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) #define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2) #define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) #define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) #define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) #define ARCHIVE_FORMAT_SHAR 0x20000 #define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) #define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) #define ARCHIVE_FORMAT_TAR 0x30000 #define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1) #define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2) #define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3) #define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4) #define ARCHIVE_FORMAT_ISO9660 0x40000 #define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) #define ARCHIVE_FORMAT_ZIP 0x50000 #define ARCHIVE_FORMAT_EMPTY 0x60000 #define ARCHIVE_FORMAT_AR 0x70000 #define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) #define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) #define ARCHIVE_FORMAT_MTREE 0x80000 /*- * Basic outline for reading an archive: * 1) Ask archive_read_new for an archive reader object. * 2) Update any global properties as appropriate. * In particular, you'll certainly want to call appropriate * archive_read_support_XXX functions. * 3) Call archive_read_open_XXX to open the archive * 4) Repeatedly call archive_read_next_header to get information about * successive archive entries. Call archive_read_data to extract * data for entries of interest. * 5) Call archive_read_finish to end processing. */ __LA_DECL struct archive *archive_read_new(void); /* * The archive_read_support_XXX calls enable auto-detect for this * archive handle. They also link in the necessary support code. * For example, if you don't want bzlib linked in, don't invoke * support_compression_bzip2(). The "all" functions provide the * obvious shorthand. */ __LA_DECL int archive_read_support_compression_all(struct archive *); __LA_DECL int archive_read_support_compression_bzip2(struct archive *); __LA_DECL int archive_read_support_compression_compress(struct archive *); __LA_DECL int archive_read_support_compression_gzip(struct archive *); __LA_DECL int archive_read_support_compression_lzma(struct archive *); __LA_DECL int archive_read_support_compression_none(struct archive *); __LA_DECL int archive_read_support_compression_program(struct archive *, const char *command); __LA_DECL int archive_read_support_compression_program_signature (struct archive *, const char *, const void * /* match */, size_t); __LA_DECL int archive_read_support_compression_xz(struct archive *); __LA_DECL int archive_read_support_format_all(struct archive *); __LA_DECL int archive_read_support_format_ar(struct archive *); __LA_DECL int archive_read_support_format_cpio(struct archive *); __LA_DECL int archive_read_support_format_empty(struct archive *); __LA_DECL int archive_read_support_format_gnutar(struct archive *); __LA_DECL int archive_read_support_format_iso9660(struct archive *); __LA_DECL int archive_read_support_format_mtree(struct archive *); __LA_DECL int archive_read_support_format_tar(struct archive *); __LA_DECL int archive_read_support_format_zip(struct archive *); /* Open the archive using callbacks for archive I/O. */ __LA_DECL int archive_read_open(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_close_callback *); __LA_DECL int archive_read_open2(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_skip_callback *, archive_close_callback *); /* * A variety of shortcuts that invoke archive_read_open() with * canned callbacks suitable for common situations. The ones that * accept a block size handle tape blocking correctly. */ /* Use this if you know the filename. Note: NULL indicates stdin. */ __LA_DECL int archive_read_open_filename(struct archive *, const char *_filename, size_t _block_size); /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ __LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size); /* Read an archive that's stored in memory. */ __LA_DECL int archive_read_open_memory(struct archive *, void * buff, size_t size); /* A more involved version that is only used for internal testing. */ __LA_DECL int archive_read_open_memory2(struct archive *a, void *buff, size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ __LA_DECL int archive_read_open_fd(struct archive *, int _fd, size_t _block_size); /* Read an archive that's already open, using a FILE *. */ /* Note: DO NOT use this with tape drives. */ __LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); /* Parses and returns next entry header. */ __LA_DECL int archive_read_next_header(struct archive *, struct archive_entry **); /* Parses and returns next entry header using the archive_entry passed in */ __LA_DECL int archive_read_next_header2(struct archive *, struct archive_entry *); /* * Retrieve the byte offset in UNCOMPRESSED data where last-read * header started. */ __LA_DECL __LA_INT64_T archive_read_header_position(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ __LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, void *, size_t); /* * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify * the desired size of the block. The API does guarantee that offsets will * be strictly increasing and that returned blocks will not overlap. */ #if ARCHIVE_VERSION_NUMBER < 3000000 __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, off_t *offset); #else __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, __LA_INT64_T *offset); #endif /* * Return the amount of buffered data (data read from the client which has * not yet been passed back via archive_read_data_*), or -1 if unknown. */ __LA_DECL ssize_t archive_read_get_backlog(struct archive *a); /* * Return the remaining length of the current archive entry, including any * padding which exists in the archive format. */ __LA_DECL off_t archive_read_get_entryleft(struct archive *a); /* * Advance the position within the archive entry. */ __LA_DECL int archive_read_advance(struct archive *a, off_t offset); /*- * Some convenience functions that are built on archive_read_data: * 'skip': skips entire entry * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ __LA_DECL int archive_read_data_skip(struct archive *); __LA_DECL int archive_read_data_into_buffer(struct archive *, void *buffer, __LA_SSIZE_T len); __LA_DECL int archive_read_data_into_fd(struct archive *, int fd); /* * Set read options. */ /* Apply option string to the format only. */ __LA_DECL int archive_read_set_format_options(struct archive *_a, const char *s); /* Apply option string to the filter only. */ __LA_DECL int archive_read_set_filter_options(struct archive *_a, const char *s); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_read_set_options(struct archive *_a, const char *s); /*- * Convenience function to recreate the current entry (whose header * has just been read) on disk. * * This does quite a bit more than just copy data to disk. It also: * - Creates intermediate directories as required. * - Manages directory permissions: non-writable directories will * be initially created with write permission enabled; when the * archive is closed, dir permissions are edited to the values specified * in the archive. * - Checks hardlinks: hardlinks will not be extracted unless the * linked-to file was also extracted within the same session. (TODO) */ /* The "flags" argument selects optional behavior, 'OR' the flags you want. */ /* Default: Do not try to set owner/group. */ #define ARCHIVE_EXTRACT_OWNER (0x0001) /* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */ #define ARCHIVE_EXTRACT_PERM (0x0002) /* Default: Do not restore mtime/atime. */ #define ARCHIVE_EXTRACT_TIME (0x0004) /* Default: Replace existing files. */ #define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008) /* Default: Try create first, unlink only if create fails with EEXIST. */ #define ARCHIVE_EXTRACT_UNLINK (0x0010) /* Default: Do not restore ACLs. */ #define ARCHIVE_EXTRACT_ACL (0x0020) /* Default: Do not restore fflags. */ #define ARCHIVE_EXTRACT_FFLAGS (0x0040) /* Default: Do not restore xattrs. */ #define ARCHIVE_EXTRACT_XATTR (0x0080) /* Default: Do not try to guard against extracts redirected by symlinks. */ /* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */ #define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100) /* Default: Do not reject entries with '..' as path elements. */ #define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200) /* Default: Create parent directories as needed. */ #define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400) /* Default: Overwrite files, even if one on disk is newer. */ #define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800) /* Detect blocks of 0 and write holes instead. */ #define ARCHIVE_EXTRACT_SPARSE (0x1000) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); __LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, struct archive * /* dest */); __LA_DECL void archive_read_extract_set_progress_callback(struct archive *, void (*_progress_func)(void *), void *_user_data); /* Record the dev/ino of a file that will not be written. This is * generally set to the dev/ino of the archive being read. */ __LA_DECL void archive_read_extract_set_skip_file(struct archive *, dev_t, ino_t); /* Close the file and release most resources. */ __LA_DECL int archive_read_close(struct archive *); /* Release all resources and destroy the object. */ /* Note that archive_read_finish will call archive_read_close for you. */ #if ARCHIVE_VERSION_NUMBER < 2000000 /* Erroneously declared to return void in libarchive 1.x */ __LA_DECL void archive_read_finish(struct archive *); #else __LA_DECL int archive_read_finish(struct archive *); #endif /*- * To create an archive: * 1) Ask archive_write_new for an archive writer object. * 2) Set any global properties. In particular, you should set * the compression and format to use. * 3) Call archive_write_open to open the file (most people * will use archive_write_open_file or archive_write_open_fd, * which provide convenient canned I/O callbacks for you). * 4) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to write the header * - archive_write_data to write the entry data * 5) archive_write_close to close the output * 6) archive_write_finish to cleanup the writer and release resources */ __LA_DECL struct archive *archive_write_new(void); __LA_DECL int archive_write_set_bytes_per_block(struct archive *, int bytes_per_block); __LA_DECL int archive_write_get_bytes_per_block(struct archive *); /* XXX This is badly misnamed; suggestions appreciated. XXX */ __LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, int bytes_in_last_block); __LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); /* The dev/ino of a file that won't be archived. This is used * to avoid recursively adding an archive to itself. */ __LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t); __LA_DECL int archive_write_set_compression_bzip2(struct archive *); __LA_DECL int archive_write_set_compression_compress(struct archive *); __LA_DECL int archive_write_set_compression_gzip(struct archive *); __LA_DECL int archive_write_set_compression_lzma(struct archive *); __LA_DECL int archive_write_set_compression_none(struct archive *); __LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd); __LA_DECL int archive_write_set_compression_xz(struct archive *); /* A convenience function to set the format based on the code or name. */ __LA_DECL int archive_write_set_format(struct archive *, int format_code); __LA_DECL int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ __LA_DECL int archive_write_set_format_ar_bsd(struct archive *); __LA_DECL int archive_write_set_format_ar_svr4(struct archive *); __LA_DECL int archive_write_set_format_cpio(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_mtree(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __LA_DECL int archive_write_set_format_pax(struct archive *); __LA_DECL int archive_write_set_format_pax_restricted(struct archive *); __LA_DECL int archive_write_set_format_shar(struct archive *); __LA_DECL int archive_write_set_format_shar_dump(struct archive *); __LA_DECL int archive_write_set_format_ustar(struct archive *); __LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); __LA_DECL int archive_write_open_fd(struct archive *, int _fd); __LA_DECL int archive_write_open_filename(struct archive *, const char *_file); /* A deprecated synonym for archive_write_open_filename() */ __LA_DECL int archive_write_open_file(struct archive *, const char *_file); __LA_DECL int archive_write_open_FILE(struct archive *, FILE *); /* _buffSize is the size of the buffer, _used refers to a variable that * will be updated after each write into the buffer. */ __LA_DECL int archive_write_open_memory(struct archive *, void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided * to archive_write_header or pad if the provided data is short. */ __LA_DECL int archive_write_header(struct archive *, struct archive_entry *); #if ARCHIVE_VERSION_NUMBER < 2000000 /* This was erroneously declared to return "int" in libarchive 1.x. */ __LA_DECL int archive_write_data(struct archive *, const void *, size_t); #else /* Libarchive 2.0 and later return ssize_t here. */ __LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, const void *, size_t); #endif #if ARCHIVE_VERSION_NUMBER < 3000000 /* Libarchive 1.x and 2.x use off_t for the argument, but that's not * stable on Linux. */ __LA_DECL int archive_write_skip(struct archive *, off_t); __LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, off_t); #else /* Libarchive 3.0 uses explicit int64_t to ensure consistent 64-bit support. */ __LA_DECL int archive_write_skip(struct archive *, __LA_INT64_T); __LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, __LA_INT64_T); #endif __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); #if ARCHIVE_VERSION_NUMBER < 2000000 /* Return value was incorrect in libarchive 1.x. */ __LA_DECL void archive_write_finish(struct archive *); #else /* Libarchive 2.x and later returns an error if this fails. */ /* It can fail if the archive wasn't already closed, in which case * archive_write_finish() will implicitly call archive_write_close(). */ __LA_DECL int archive_write_finish(struct archive *); #endif /* * Set write options. */ /* Apply option string to the format only. */ __LA_DECL int archive_write_set_format_options(struct archive *_a, const char *s); /* Apply option string to the compressor only. */ __LA_DECL int archive_write_set_compressor_options(struct archive *_a, const char *s); /* Apply option string to both the format and the compressor. */ __LA_DECL int archive_write_set_options(struct archive *_a, const char *s); /*- * ARCHIVE_WRITE_DISK API * * To create objects on disk: * 1) Ask archive_write_disk_new for a new archive_write_disk object. * 2) Set any global properties. In particular, you probably * want to set the options. * 3) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to create the file/dir/etc on disk * - archive_write_data to write the entry data * 4) archive_write_finish to cleanup the writer and release resources * * In particular, you can use this in conjunction with archive_read() * to pull entries out of an archive and create them on disk. */ __LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ __LA_DECL int archive_write_disk_set_skip_file(struct archive *, dev_t, ino_t); /* Set flags to control how the next item gets created. * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */ __LA_DECL int archive_write_disk_set_options(struct archive *, int flags); /* * The lookup functions are given uname/uid (or gname/gid) pairs and * return a uid (gid) suitable for this system. These are used for * restoring ownership and for setting ACLs. The default functions * are naive, they just return the uid/gid. These are small, so reasonable * for applications that don't need to preserve ownership; they * are probably also appropriate for applications that are doing * same-system backup and restore. */ /* * The "standard" lookup functions use common system calls to lookup * the uname/gname, falling back to the uid/gid if the names can't be * found. They cache lookups and are reasonably fast, but can be very * large, so they are not used unless you ask for them. In * particular, these match the specifications of POSIX "pax" and old * POSIX "tar". */ __LA_DECL int archive_write_disk_set_standard_lookup(struct archive *); /* * If neither the default (naive) nor the standard (big) functions suit * your needs, you can write your own and register them. Be sure to * include a cleanup function if you have allocated private data. */ __LA_DECL int archive_write_disk_set_group_lookup(struct archive *, void * /* private_data */, __LA_GID_T (*)(void *, const char *, __LA_GID_T), void (* /* cleanup */)(void *)); __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, void * /* private_data */, __LA_UID_T (*)(void *, const char *, __LA_UID_T), void (* /* cleanup */)(void *)); /* * ARCHIVE_READ_DISK API * * This is still evolving and somewhat experimental. */ __LA_DECL struct archive *archive_read_disk_new(void); /* The names for symlink modes here correspond to an old BSD * command-line argument convention: -L, -P, -H */ /* Follow all symlinks. */ __LA_DECL int archive_read_disk_set_symlink_logical(struct archive *); /* Follow no symlinks. */ __LA_DECL int archive_read_disk_set_symlink_physical(struct archive *); /* Follow symlink initially, then not. */ __LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *); /* TODO: Handle Linux stat32/stat64 ugliness. */ __LA_DECL int archive_read_disk_entry_from_file(struct archive *, struct archive_entry *, int /* fd */, const struct stat *); /* Look up gname for gid or uname for uid. */ /* Default implementations are very, very stupid. */ __LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T); __LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T); /* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the * results for performance. */ __LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); /* You can install your own lookups if you like. */ __LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, __LA_GID_T), void (* /* cleanup_fn */)(void *)); __LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, __LA_UID_T), void (* /* cleanup_fn */)(void *)); /* * Accessor functions to read/set various information in * the struct archive object: */ /* Bytes written after compression or read before decompression. */ __LA_DECL __LA_INT64_T archive_position_compressed(struct archive *); /* Bytes written to compressor or read from decompressor. */ __LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *); __LA_DECL const char *archive_compression_name(struct archive *); __LA_DECL int archive_compression(struct archive *); __LA_DECL int archive_errno(struct archive *); __LA_DECL const char *archive_error_string(struct archive *); __LA_DECL const char *archive_format_name(struct archive *); __LA_DECL int archive_format(struct archive *); __LA_DECL void archive_clear_error(struct archive *); __LA_DECL void archive_set_error(struct archive *, int _err, const char *fmt, ...); __LA_DECL void archive_copy_error(struct archive *dest, struct archive *src); __LA_DECL int archive_file_count(struct archive *); #ifdef __cplusplus } #endif /* These are meaningless outside of this header. */ #undef __LA_DECL #undef __LA_GID_T #undef __LA_UID_T /* These need to remain defined because they're used in the * callback type definitions. XXX Fix this. This is ugly. XXX */ /* #undef __LA_INT64_T */ /* #undef __LA_SSIZE_T */ #endif /* !ARCHIVE_H_INCLUDED */ tarsnap-1.0.41/libarchive/archive_check_magic.c000066400000000000000000000070651476705112600214600ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_check_magic.c,v 1.9 2008/12/06 05:52:01 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #endif #include "archive_private.h" static void errmsg(const char *m) { size_t s = strlen(m); ssize_t written; while (s > 0) { written = write(2, m, strlen(m)); if (written <= 0) return; m += written; s -= written; } } static void diediedie(void) { #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) /* Cause a breakpoint exception */ DebugBreak(); #endif abort(); /* Terminate the program abnormally. */ } static const char * state_name(unsigned s) { switch (s) { case ARCHIVE_STATE_NEW: return ("new"); case ARCHIVE_STATE_HEADER: return ("header"); case ARCHIVE_STATE_DATA: return ("data"); case ARCHIVE_STATE_EOF: return ("eof"); case ARCHIVE_STATE_CLOSED: return ("closed"); case ARCHIVE_STATE_FATAL: return ("fatal"); default: return ("??"); } } static void write_all_states(unsigned int states) { unsigned int lowbit; /* A trick for computing the lowest set bit. */ while ((lowbit = states & (-states)) != 0) { states &= ~lowbit; /* Clear the low bit. */ errmsg(state_name(lowbit)); if (states != 0) errmsg("/"); } } /* * Check magic value and current state; bail if it isn't valid. * * This is designed to catch serious programming errors that violate * the libarchive API. */ void __archive_check_magic(struct archive *a, unsigned int magic, unsigned int state, const char *function) { if (a->magic != magic) { errmsg("INTERNAL ERROR: Function "); errmsg(function); errmsg(" invoked with invalid struct archive structure.\n"); diediedie(); } if (state == ARCHIVE_STATE_ANY) return; if ((a->state & state) == 0) { errmsg("INTERNAL ERROR: Function '"); errmsg(function); errmsg("' invoked with archive structure in state '"); write_all_states(a->state); errmsg("', should be in state '"); write_all_states(state); errmsg("'\n"); diediedie(); } } tarsnap-1.0.41/libarchive/archive_endian.h000066400000000000000000000100511476705112600204730ustar00rootroot00000000000000/*- * Copyright (c) 2002 Thomas Moestl * 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: src/lib/libarchive/archive_endian.h,v 1.4 2008/12/06 06:12:24 kientzle Exp $ * * Borrowed from FreeBSD's */ /* Note: This is a purely internal header! */ /* Do not use this outside of libarchive internal code! */ #ifndef ARCHIVE_ENDIAN_H_INCLUDED #define ARCHIVE_ENDIAN_H_INCLUDED /* * Disabling inline keyword for compilers known to choke on it: * - Watcom C++ in C code. (For any version?) * - SGI MIPSpro * - Microsoft Visual C++ 6.0 (supposedly newer versions too) */ #if defined(__WATCOMC__) || defined(__sgi) #define inline #elif defined(_MSC_VER) #define inline __inline #endif /* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ static inline uint16_t archive_be16dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[0] << 8) | p[1]); } static inline uint32_t archive_be32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); } static inline uint64_t archive_be64dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4)); } static inline uint16_t archive_le16dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[1] << 8) | p[0]); } static inline uint32_t archive_le32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); } static inline uint64_t archive_le64dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p)); } static inline void archive_be16enc(void *pp, uint16_t u) { unsigned char *p = (unsigned char *)pp; p[0] = (u >> 8) & 0xff; p[1] = u & 0xff; } static inline void archive_be32enc(void *pp, uint32_t u) { unsigned char *p = (unsigned char *)pp; p[0] = (u >> 24) & 0xff; p[1] = (u >> 16) & 0xff; p[2] = (u >> 8) & 0xff; p[3] = u & 0xff; } static inline void archive_be64enc(void *pp, uint64_t u) { unsigned char *p = (unsigned char *)pp; archive_be32enc(p, u >> 32); archive_be32enc(p + 4, u & 0xffffffff); } static inline void archive_le16enc(void *pp, uint16_t u) { unsigned char *p = (unsigned char *)pp; p[0] = u & 0xff; p[1] = (u >> 8) & 0xff; } static inline void archive_le32enc(void *pp, uint32_t u) { unsigned char *p = (unsigned char *)pp; p[0] = u & 0xff; p[1] = (u >> 8) & 0xff; p[2] = (u >> 16) & 0xff; p[3] = (u >> 24) & 0xff; } static inline void archive_le64enc(void *pp, uint64_t u) { unsigned char *p = (unsigned char *)pp; archive_le32enc(p, u & 0xffffffff); archive_le32enc(p + 4, u >> 32); } #endif tarsnap-1.0.41/libarchive/archive_entry.c000066400000000000000000001502331476705112600204000ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_entry.c,v 1.55 2008/12/23 05:01:43 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef MAJOR_IN_MKDEV #include #else #ifdef MAJOR_IN_SYSMACROS #include #endif #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" #undef max #define max(a, b) ((a)>(b)?(a):(b)) /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define ae_makedev(maj, min) makedev((maj), (min)) #endif static void aes_clean(struct aes *); static void aes_copy(struct aes *dest, struct aes *src); static const char * aes_get_mbs(struct aes *); static const wchar_t * aes_get_wcs(struct aes *); static int aes_set_mbs(struct aes *, const char *mbs); static int aes_copy_mbs(struct aes *, const char *mbs); /* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ static int aes_copy_wcs(struct aes *, const wchar_t *wcs); static int aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); static int acl_special(struct archive_entry *entry, int type, int permset, int tag); static struct ae_acl *acl_new_entry(struct archive_entry *entry, int type, int permset, int tag, int id); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test); static void archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name, size_t); #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p != L'\0') ++p; return p - s; } #endif #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif #ifndef HAVE_WMEMCPY #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif static void aes_clean(struct aes *aes) { if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } archive_string_free(&(aes->aes_mbs)); archive_string_free(&(aes->aes_utf8)); aes->aes_set = 0; } static void aes_copy(struct aes *dest, struct aes *src) { wchar_t *wp; dest->aes_set = src->aes_set; archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); if (src->aes_wcs != NULL) { wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) * sizeof(wchar_t)); if (wp == NULL) __archive_errx(1, "No memory for aes_copy()"); wcscpy(wp, src->aes_wcs); dest->aes_wcs = wp; } } static const char * aes_get_utf8(struct aes *aes) { if (aes->aes_set & AES_SET_UTF8) return (aes->aes_utf8.s); if ((aes->aes_set & AES_SET_WCS) && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) { aes->aes_set |= AES_SET_UTF8; return (aes->aes_utf8.s); } return (NULL); } static const char * aes_get_mbs(struct aes *aes) { /* If we already have an MBS form, return that immediately. */ if (aes->aes_set & AES_SET_MBS) return (aes->aes_mbs.s); /* If there's a WCS form, try converting with the native locale. */ if ((aes->aes_set & AES_SET_WCS) && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) { aes->aes_set |= AES_SET_MBS; return (aes->aes_mbs.s); } /* We'll use UTF-8 for MBS if all else fails. */ return (aes_get_utf8(aes)); } static const wchar_t * aes_get_wcs(struct aes *aes) { wchar_t *w; int r; /* Return WCS form if we already have it. */ if (aes->aes_set & AES_SET_WCS) return (aes->aes_wcs); if (aes->aes_set & AES_SET_MBS) { /* Try converting MBS to WCS using native locale. */ /* * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ size_t wcs_length = aes->aes_mbs.length; w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); if (w == NULL) __archive_errx(1, "No memory for aes_get_wcs()"); r = mbstowcs(w, aes->aes_mbs.s, wcs_length); if (r > 0) { w[r] = 0; aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs = w); } free(w); } if (aes->aes_set & AES_SET_UTF8) { /* Try converting UTF8 to WCS. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); if (aes->aes_wcs != NULL) aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs); } return (NULL); } static int aes_set_mbs(struct aes *aes, const char *mbs) { return (aes_copy_mbs(aes, mbs)); } static int aes_copy_mbs(struct aes *aes, const char *mbs) { if (mbs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ archive_strcpy(&(aes->aes_mbs), mbs); archive_string_empty(&(aes->aes_utf8)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } return (0); } /* * The 'update' form tries to proactively update all forms of * this string (WCS and MBS) and returns an error if any of * them fail. This is used by the 'pax' handler, for instance, * to detect and report character-conversion failures early while * still allowing clients to get potentially useful values from * the more tolerant lazy conversions. (get_mbs and get_wcs will * strive to give the user something useful, so you can get hopefully * usable values even if some of the character conversions are failing.) */ static int aes_update_utf8(struct aes *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; return (1); /* Succeeded in clearing everything. */ } /* Save the UTF8 string. */ archive_strcpy(&(aes->aes_utf8), utf8); /* Empty the mbs and wcs strings. */ archive_string_empty(&(aes->aes_mbs)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ /* TODO: We should just do a direct UTF-8 to MBS conversion * here. That would be faster, use less space, and give the * same information. (If a UTF-8 to MBS conversion succeeds, * then UTF-8->WCS and Unicode->MBS conversions will both * succeed.) */ /* Try converting UTF8 to WCS, return false on failure. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); if (aes->aes_wcs == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ /* Try converting WCS to MBS, return false on failure. */ if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; /* All conversions succeeded. */ return (1); } static int aes_copy_wcs(struct aes *aes, const wchar_t *wcs) { return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs)); } static int aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) { wchar_t *w; if (wcs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_utf8)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); if (w == NULL) __archive_errx(1, "No memory for aes_copy_wcs()"); wmemcpy(w, wcs, len); w[len] = L'\0'; aes->aes_wcs = w; return (0); } /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); aes_clean(&entry->ae_fflags_text); aes_clean(&entry->ae_gname); aes_clean(&entry->ae_hardlink); aes_clean(&entry->ae_pathname); aes_clean(&entry->ae_sourcepath); aes_clean(&entry->ae_symlink); aes_clean(&entry->ae_uname); archive_entry_acl_clear(entry); archive_entry_xattr_clear(entry); free(entry->stat); memset(entry, 0, sizeof(*entry)); return entry; } struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; struct ae_acl *ap, *ap2; struct ae_xattr *xp; /* Allocate new structure and copy over all of the fields. */ entry2 = (struct archive_entry *)malloc(sizeof(*entry2)); if (entry2 == NULL) return (NULL); memset(entry2, 0, sizeof(*entry2)); entry2->ae_stat = entry->ae_stat; entry2->ae_fflags_set = entry->ae_fflags_set; entry2->ae_fflags_clear = entry->ae_fflags_clear; aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); aes_copy(&entry2->ae_gname, &entry->ae_gname); aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink); aes_copy(&entry2->ae_pathname, &entry->ae_pathname); aes_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); aes_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; aes_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy ACL data over. */ ap = entry->acl_head; while (ap != NULL) { ap2 = acl_new_entry(entry2, ap->type, ap->permset, ap->tag, ap->id); if (ap2 != NULL) aes_copy(&ap2->name, &ap->name); ap = ap->next; } /* Copy xattr data over. */ xp = entry->xattr_head; while (xp != NULL) { archive_entry_xattr_add_entry(entry2, xp->name, xp->value, xp->size); xp = xp->next; } return (entry2); } void archive_entry_free(struct archive_entry *entry) { archive_entry_clear(entry); free(entry); } struct archive_entry * archive_entry_new(void) { struct archive_entry *entry; entry = (struct archive_entry *)malloc(sizeof(*entry)); if (entry == NULL) return (NULL); memset(entry, 0, sizeof(*entry)); return (entry); } /* * Functions for reading fields from an archive_entry. */ time_t archive_entry_atime(struct archive_entry *entry) { return (entry->ae_stat.aest_atime); } long archive_entry_atime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_atime_nsec); } int archive_entry_atime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_ATIME); } time_t archive_entry_birthtime(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime); } long archive_entry_birthtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime_nsec); } int archive_entry_birthtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_BIRTHTIME); } time_t archive_entry_ctime(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime); } int archive_entry_ctime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_CTIME); } long archive_entry_ctime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime_nsec); } dev_t archive_entry_dev(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return ae_makedev(entry->ae_stat.aest_devmajor, entry->ae_stat.aest_devminor); else return (entry->ae_stat.aest_dev); } dev_t archive_entry_devmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devmajor); else return major(entry->ae_stat.aest_dev); } dev_t archive_entry_devminor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devminor); else return minor(entry->ae_stat.aest_dev); } mode_t archive_entry_filetype(struct archive_entry *entry) { return (AE_IFMT & entry->ae_stat.aest_mode); } void archive_entry_fflags(struct archive_entry *entry, unsigned long *set, unsigned long *clear) { *set = entry->ae_fflags_set; *clear = entry->ae_fflags_clear; } /* * Note: if text was provided, this just returns that text. If you * really need the text to be rebuilt in a canonical form, set the * text, ask for the bitmaps, then set the bitmaps. (Setting the * bitmaps clears any stored text.) This design is deliberate: if * we're editing archives, we don't want to discard flags just because * they aren't supported on the current system. The bitmap<->text * conversions are platform-specific (see below). */ const char * archive_entry_fflags_text(struct archive_entry *entry) { const char *f; char *p; f = aes_get_mbs(&entry->ae_fflags_text); if (f != NULL) return (f); if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) return (NULL); p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); if (p == NULL) return (NULL); aes_copy_mbs(&entry->ae_fflags_text, p); free(p); f = aes_get_mbs(&entry->ae_fflags_text); return (f); } gid_t archive_entry_gid(struct archive_entry *entry) { return (entry->ae_stat.aest_gid); } const char * archive_entry_gname(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_gname)); } const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { return (aes_get_wcs(&entry->ae_gname)); } const char * archive_entry_hardlink(struct archive_entry *entry) { if (entry->ae_set & AE_SET_HARDLINK) return (aes_get_mbs(&entry->ae_hardlink)); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { if (entry->ae_set & AE_SET_HARDLINK) return (aes_get_wcs(&entry->ae_hardlink)); return (NULL); } ino_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->ae_stat.aest_mode); } time_t archive_entry_mtime(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime); } long archive_entry_mtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime_nsec); } int archive_entry_mtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_MTIME); } unsigned int archive_entry_nlink(struct archive_entry *entry) { return (entry->ae_stat.aest_nlink); } const char * archive_entry_pathname(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_pathname)); } const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { return (aes_get_wcs(&entry->ae_pathname)); } dev_t archive_entry_rdev(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return ae_makedev(entry->ae_stat.aest_rdevmajor, entry->ae_stat.aest_rdevminor); else return (entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevmajor); else return major(entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevminor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevminor); else return minor(entry->ae_stat.aest_rdev); } int64_t archive_entry_size(struct archive_entry *entry) { return (entry->ae_stat.aest_size); } int archive_entry_size_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_SIZE); } const char * archive_entry_sourcepath(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_sourcepath)); } const char * archive_entry_symlink(struct archive_entry *entry) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_get_mbs(&entry->ae_symlink)); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_get_wcs(&entry->ae_symlink)); return (NULL); } uid_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_uid); } const char * archive_entry_uname(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_uname)); } const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { return (aes_get_wcs(&entry->ae_uname)); } /* * Functions to set archive_entry properties. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->ae_stat.aest_mode &= ~AE_IFMT; entry->ae_stat.aest_mode |= AE_IFMT & type; } void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { aes_clean(&entry->ae_fflags_text); entry->ae_fflags_set = set; entry->ae_fflags_clear = clear; } const char * archive_entry_copy_fflags_text(struct archive_entry *entry, const char *flags) { aes_copy_mbs(&entry->ae_fflags_text, flags); return (ae_strtofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) { aes_copy_wcs(&entry->ae_fflags_text, flags); return (ae_wcstofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } void archive_entry_set_gid(struct archive_entry *entry, gid_t g) { entry->stat_valid = 0; entry->ae_stat.aest_gid = g; } void archive_entry_set_gname(struct archive_entry *entry, const char *name) { aes_set_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { aes_copy_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_gname, name); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_gname, name)); } void archive_entry_set_ino(struct archive_entry *entry, unsigned long ino) { entry->stat_valid = 0; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { aes_set_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { aes_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { aes_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_ATIME; entry->ae_stat.aest_atime = t; entry->ae_stat.aest_atime_nsec = ns; } void archive_entry_unset_atime(struct archive_entry *entry) { archive_entry_set_atime(entry, 0, 0); entry->ae_set &= ~AE_SET_ATIME; } void archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = m; entry->ae_stat.aest_birthtime_nsec = ns; } void archive_entry_unset_birthtime(struct archive_entry *entry) { archive_entry_set_birthtime(entry, 0, 0); entry->ae_set &= ~AE_SET_BIRTHTIME; } void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_CTIME; entry->ae_stat.aest_ctime = t; entry->ae_stat.aest_ctime_nsec = ns; } void archive_entry_unset_ctime(struct archive_entry *entry) { archive_entry_set_ctime(entry, 0, 0); entry->ae_set &= ~AE_SET_CTIME; } void archive_entry_set_dev(struct archive_entry *entry, dev_t d) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 0; entry->ae_stat.aest_dev = d; } void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devmajor = m; } void archive_entry_set_devminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devminor = m; } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_set_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_set_mbs(&entry->ae_symlink, target); else aes_set_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_copy_mbs(&entry->ae_symlink, target); else aes_copy_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_copy_wcs(&entry->ae_symlink, target); else aes_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_update_utf8(&entry->ae_symlink, target)); else return (aes_update_utf8(&entry->ae_hardlink, target)); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->ae_stat.aest_mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = m; entry->ae_stat.aest_mtime_nsec = ns; } void archive_entry_unset_mtime(struct archive_entry *entry) { archive_entry_set_mtime(entry, 0, 0); entry->ae_set &= ~AE_SET_MTIME; } void archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) { entry->stat_valid = 0; entry->ae_stat.aest_nlink = nlink; } void archive_entry_set_pathname(struct archive_entry *entry, const char *name) { aes_set_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { aes_copy_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_pathname, name); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_pathname, name)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->ae_stat.aest_mode &= AE_IFMT; entry->ae_stat.aest_mode |= ~AE_IFMT & p; } void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; } void archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevmajor = m; } void archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevminor = m; } void archive_entry_set_size(struct archive_entry *entry, int64_t s) { entry->stat_valid = 0; entry->ae_stat.aest_size = s; entry->ae_set |= AE_SET_SIZE; } void archive_entry_unset_size(struct archive_entry *entry) { archive_entry_set_size(entry, 0); entry->ae_set &= ~AE_SET_SIZE; } void archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) { aes_set_mbs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { aes_set_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { aes_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { aes_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_set_uid(struct archive_entry *entry, uid_t u) { entry->stat_valid = 0; entry->ae_stat.aest_uid = u; } void archive_entry_set_uname(struct archive_entry *entry, const char *name) { aes_set_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { aes_copy_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_uname, name); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_uname, name)); } /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and * complete standard that addressed the needs of ACL archiving and 2) * everyone followed it faithfully. Alas, neither is true, so the * following is a lot more complex than might seem necessary to the * uninitiated. */ void archive_entry_acl_clear(struct archive_entry *entry) { struct ae_acl *ap; while (entry->acl_head != NULL) { ap = entry->acl_head->next; aes_clean(&entry->acl_head->name); free(entry->acl_head); entry->acl_head = ap; } if (entry->acl_text_w != NULL) { free(entry->acl_text_w); entry->acl_text_w = NULL; } entry->acl_p = NULL; entry->acl_state = 0; /* Not counting. */ } /* * Add a single ACL entry to the internal list of ACL data. */ void archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { struct ae_acl *ap; if (acl_special(entry, type, permset, tag) == 0) return; ap = acl_new_entry(entry, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return; } if (name != NULL && *name != '\0') aes_copy_mbs(&ap->name, name); else aes_clean(&ap->name); } /* * As above, but with a wide-character name. */ void archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name)); } void archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name, size_t len) { struct ae_acl *ap; if (acl_special(entry, type, permset, tag) == 0) return; ap = acl_new_entry(entry, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return; } if (name != NULL && *name != L'\0' && len > 0) aes_copy_wcs_len(&ap->name, name, len); else aes_clean(&ap->name); } /* * If this ACL entry is part of the standard POSIX permissions set, * store the permissions in the stat structure and return zero. */ static int acl_special(struct archive_entry *entry, int type, int permset, int tag) { if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: entry->ae_stat.aest_mode &= ~0700; entry->ae_stat.aest_mode |= (permset & 7) << 6; return (0); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: entry->ae_stat.aest_mode &= ~0070; entry->ae_stat.aest_mode |= (permset & 7) << 3; return (0); case ARCHIVE_ENTRY_ACL_OTHER: entry->ae_stat.aest_mode &= ~0007; entry->ae_stat.aest_mode |= permset & 7; return (0); } } return (1); } /* * Allocate and populate a new ACL entry with everything but the * name. */ static struct ae_acl * acl_new_entry(struct archive_entry *entry, int type, int permset, int tag, int id) { struct ae_acl *ap; if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) return (NULL); if (entry->acl_text_w != NULL) { free(entry->acl_text_w); entry->acl_text_w = NULL; } /* XXX TODO: More sanity-checks on the arguments XXX */ /* If there's a matching entry already in the list, overwrite it. */ for (ap = entry->acl_head; ap != NULL; ap = ap->next) { if (ap->type == type && ap->tag == tag && ap->id == id) { ap->permset = permset; return (ap); } } /* Add a new entry to the list. */ ap = (struct ae_acl *)malloc(sizeof(*ap)); if (ap == NULL) return (NULL); memset(ap, 0, sizeof(*ap)); ap->next = entry->acl_head; entry->acl_head = ap; ap->type = type; ap->tag = tag; ap->id = id; ap->permset = permset; return (ap); } /* * Return a count of entries matching "want_type". */ int archive_entry_acl_count(struct archive_entry *entry, int want_type) { int count; struct ae_acl *ap; count = 0; ap = entry->acl_head; while (ap != NULL) { if ((ap->type & want_type) != 0) count++; ap = ap->next; } if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) count += 3; return (count); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_entry_acl_reset(struct archive_entry *entry, int want_type) { int count, cutoff; count = archive_entry_acl_count(entry, want_type); /* * If the only entries are the three standard ones, * then don't return any ACL data. (In this case, * client can just use chmod(2) to set permissions.) */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) cutoff = 3; else cutoff = 0; if (count > cutoff) entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; else entry->acl_state = 0; entry->acl_p = entry->acl_head; return (count); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { *name = NULL; *id = -1; /* * The acl_state is either zero (no entries available), -1 * (reading from list), or an entry type (retrieve that type * from ae_stat.aest_mode). */ if (entry->acl_state == 0) return (ARCHIVE_WARN); /* The first three access entries are special. */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { switch (entry->acl_state) { case ARCHIVE_ENTRY_ACL_USER_OBJ: *permset = (entry->ae_stat.aest_mode >> 6) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: *permset = (entry->ae_stat.aest_mode >> 3) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_OTHER: *permset = entry->ae_stat.aest_mode & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_OTHER; entry->acl_state = -1; entry->acl_p = entry->acl_head; return (ARCHIVE_OK); default: break; } } while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0) entry->acl_p = entry->acl_p->next; if (entry->acl_p == NULL) { entry->acl_state = 0; *type = 0; *permset = 0; *tag = 0; *id = -1; *name = NULL; return (ARCHIVE_EOF); /* End of ACL entries. */ } *type = entry->acl_p->type; *permset = entry->acl_p->permset; *tag = entry->acl_p->tag; *id = entry->acl_p->id; *name = aes_get_mbs(&entry->acl_p->name); entry->acl_p = entry->acl_p->next; return (ARCHIVE_OK); } /* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { int count; size_t length; const wchar_t *wname; const wchar_t *prefix; wchar_t separator; struct ae_acl *ap; int id; wchar_t *wp; if (entry->acl_text_w != NULL) { free (entry->acl_text_w); entry->acl_text_w = NULL; } separator = L','; count = 0; length = 0; ap = entry->acl_head; while (ap != NULL) { if ((ap->type & flags) != 0) { count++; if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) length += 8; /* "default:" */ length += 5; /* tag name */ length += 1; /* colon */ wname = aes_get_wcs(&ap->name); if (wname != NULL) length += wcslen(wname); else length += sizeof(uid_t) * 3 + 1; length ++; /* colon */ length += 3; /* rwx */ length += 1; /* colon */ length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; length ++; /* newline */ } ap = ap->next; } if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { length += 10; /* "user::rwx\n" */ length += 11; /* "group::rwx\n" */ length += 11; /* "other::rwx\n" */ } if (count == 0) return (NULL); /* Now, allocate the string and actually populate it. */ wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wp == NULL) __archive_errx(1, "No memory to generate the text version of the ACL"); count = 0; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, entry->ae_stat.aest_mode & 0700, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, entry->ae_stat.aest_mode & 0070, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, entry->ae_stat.aest_mode & 0007, -1); count += 3; ap = entry->acl_head; while (ap != NULL) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { wname = aes_get_wcs(&ap->name); *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, NULL, ap->tag, wname, ap->permset, id); count++; } ap = ap->next; } } if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) prefix = L"default:"; else prefix = NULL; ap = entry->acl_head; count = 0; while (ap != NULL) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { wname = aes_get_wcs(&ap->name); if (count > 0) *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, prefix, ap->tag, wname, ap->permset, id); count ++; } ap = ap->next; } } return (entry->acl_text_w); } static void append_id_w(wchar_t **wp, int id) { if (id < 0) id = 0; if (id > 9) append_id_w(wp, id / 10); *(*wp)++ = L"0123456789"[id % 10]; } static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id) { if (prefix != NULL) { wcscpy(*wp, prefix); *wp += wcslen(*wp); } switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: wcscpy(*wp, L"group"); break; case ARCHIVE_ENTRY_ACL_MASK: wcscpy(*wp, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: wcscpy(*wp, L"other"); wname = NULL; id = -1; break; } *wp += wcslen(*wp); *(*wp)++ = L':'; if (wname != NULL) { wcscpy(*wp, wname); *wp += wcslen(*wp); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id_w(wp, id); id = -1; } *(*wp)++ = L':'; *(*wp)++ = (perm & 0444) ? L'r' : L'-'; *(*wp)++ = (perm & 0222) ? L'w' : L'-'; *(*wp)++ = (perm & 0111) ? L'x' : L'-'; if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); } **wp = L'\0'; } /* * Parse a textual ACL. This automatically recognizes and supports * extensions described above. The 'type' argument is used to * indicate the type that should be used for any entries not * explicitly marked as "default:". */ int __archive_entry_acl_parse_w(struct archive_entry *entry, const wchar_t *text, int default_type) { struct { const wchar_t *start; const wchar_t *end; } field[4]; int fields; int type, tag, permset, id; const wchar_t *p; wchar_t sep; while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); if (fields < 4) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == L':'); if (fields < 3) return (ARCHIVE_WARN); /* Check for a numeric ID in field 1 or 3. */ id = -1; isint_w(field[1].start, field[1].end, &id); /* Field 3 is optional. */ if (id == -1 && fields > 3) isint_w(field[3].start, field[3].end, &id); /* Parse the permissions from field 2. */ permset = 0; p = field[2].start; while (p < field[2].end) { switch (*p++) { case 'r': case 'R': permset |= ARCHIVE_ENTRY_ACL_READ; break; case 'w': case 'W': permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case 'x': case 'X': permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case '-': break; default: return (ARCHIVE_WARN); } } /* * Solaris extension: "defaultuser::rwx" is the * default ACL corresponding to "user::rwx", etc. */ if (field[0].end-field[0].start > 7 && wmemcmp(field[0].start, L"default", 7) == 0) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; field[0].start += 7; } else type = default_type; if (prefix_w(field[0].start, field[0].end, L"user")) { if (id != -1 || field[1].start < field[1].end) tag = ARCHIVE_ENTRY_ACL_USER; else tag = ARCHIVE_ENTRY_ACL_USER_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"group")) { if (id != -1 || field[1].start < field[1].end) tag = ARCHIVE_ENTRY_ACL_GROUP; else tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"other")) { if (id != -1 || field[1].start < field[1].end) return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_OTHER; } else if (prefix_w(field[0].start, field[0].end, L"mask")) { if (id != -1 || field[1].start < field[1].end) return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_MASK; } else return (ARCHIVE_WARN); /* Add entry to the internal list. */ archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, field[1].start, field[1].end - field[1].start); } return (ARCHIVE_OK); } /* * 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; if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL) __archive_errx(1, "Out of memory"); if ((xp->name = strdup(name)) == NULL) __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 */ /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint_w(const wchar_t *start, const wchar_t *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10)) n = INT_MAX; else { n *= 10; n += *start - '0'; } start++; } *result = n; return (1); } /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep) { /* Skip leading whitespace to find start of field. */ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { (*wp)++; } *start = *wp; /* Scan for the separator. */ while (**wp != L'\0' && **wp != L',' && **wp != L':' && **wp != L'\n') { (*wp)++; } *sep = **wp; /* Trim trailing whitespace to locate end of field. */ *end = *wp - 1; while (**end == L' ' || **end == L'\t' || **end == L'\n') { (*end)--; } (*end)++; /* Adjust scanner location. */ if (**wp != L'\0') (*wp)++; } /* * Return true if the characters [start...end) are a prefix of 'test'. * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. */ static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) { if (start == end) return (0); if (*start++ != *test++) return (0); while (start < end && *start++ == *test++) ; if (start < end) return (0); return (1); } /* * Following code is modified from UC Berkeley sources, and * is subject to the following copyright notice. */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static struct flag { const char *name; const wchar_t *wname; unsigned long set; unsigned long clear; } flags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND { "nosappnd", L"nosappnd", SF_APPEND, 0 }, { "nosappend", L"nosappend", SF_APPEND, 0 }, #endif #ifdef EXT2_APPEND_FL /* 'a' */ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, #endif #ifdef SF_ARCHIVED { "noarch", L"noarch", SF_ARCHIVED, 0 }, { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, #endif #ifdef SF_IMMUTABLE { "noschg", L"noschg", SF_IMMUTABLE, 0 }, { "noschange", L"noschange", SF_IMMUTABLE, 0 }, { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, #endif #ifdef EXT2_IMMUTABLE_FL /* 'i' */ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, #endif #ifdef SF_NOUNLINK { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, #endif #ifdef SF_SNAPSHOT { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, #endif #ifdef UF_APPEND { "nouappnd", L"nouappnd", UF_APPEND, 0 }, { "nouappend", L"nouappend", UF_APPEND, 0 }, #endif #ifdef UF_IMMUTABLE { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #ifdef EXT2_NODUMP_FL /* 'd' */ { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE { "noopaque", L"noopaque", UF_OPAQUE, 0 }, #endif #ifdef UF_NOUNLINK { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, #endif #ifdef EXT2_COMPR_FL /* 'c' */ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, #endif #ifdef EXT2_NOATIME_FL /* 'A' */ { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif { NULL, NULL, 0, 0 } }; /* * fflagstostr -- * Convert file flags to a comma-separated string. If no flags * are set, return the empty string. */ static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; const char *sp; unsigned long bits; struct flag *flag; size_t length; bits = bitset | bitclear; length = 0; for (flag = flags; flag->name != NULL; flag++) if (bits & (flag->set | flag->clear)) { length += strlen(flag->name) + 1; bits &= ~(flag->set | flag->clear); } if (length == 0) return (NULL); string = (char *)malloc(length); if (string == NULL) return (NULL); dp = string; for (flag = flags; flag->name != NULL; flag++) { if (bitset & flag->set || bitclear & flag->clear) { sp = flag->name + 2; } else if (bitset & flag->clear || bitclear & flag->set) { sp = flag->name; } else continue; bitset &= ~(flag->set | flag->clear); bitclear &= ~(flag->set | flag->clear); if (dp > string) *dp++ = ','; while ((*dp++ = *sp++) != '\0') ; dp--; } *dp = '\0'; return (string); } /* * strtofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const char * ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) { const char *start, *end; struct flag *flag; unsigned long set, clear; const char *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; for (flag = flags; flag->name != NULL; flag++) { if (memcmp(start, flag->name, end - start) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (memcmp(start, flag->name + 2, end - start) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->name == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == '\t' || *start == ' ' || *start == ',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } /* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const wchar_t * ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) { const wchar_t *start, *end; struct flag *flag; unsigned long set, clear; const wchar_t *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == L'\t' || *start == L' ' || *start == L',') start++; while (*start != L'\0') { /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; for (flag = flags; flag->wname != NULL; flag++) { if (wmemcmp(start, flag->wname, end - start) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (wmemcmp(start, flag->wname + 2, end - start) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->wname == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == L'\t' || *start == L' ' || *start == L',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } #ifdef TEST #include int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); } #endif tarsnap-1.0.41/libarchive/archive_entry.h000066400000000000000000000562741476705112600204170ustar00rootroot00000000000000/*- * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.31 2008/12/06 06:18:46 kientzle Exp $ */ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED /* * Note: archive_entry.h is for use outside of libarchive; the * configuration headers (config.h, archive_platform.h, etc.) are * purely internal. Do NOT use HAVE_XXX configuration macros to * control the behavior of this header! If you must conditionalize, * use predefined compiler and/or platform macros. */ #include #include /* for wchar_t */ #include /* Get appropriate definitions of standard POSIX-style types. */ /* These should match the types used in 'struct stat' */ #if defined(_WIN32) && !defined(__CYGWIN__) #define __LA_INT64_T __int64 #define __LA_UID_T unsigned int #define __LA_GID_T unsigned int #define __LA_DEV_T unsigned int #define __LA_MODE_T unsigned short #else #include #define __LA_INT64_T int64_t #define __LA_UID_T uid_t #define __LA_GID_T gid_t #define __LA_DEV_T dev_t #define __LA_MODE_T mode_t #endif /* * XXX Is this defined for all Windows compilers? If so, in what * header? It would be nice to remove the __LA_INO_T indirection and * just use plain ino_t everywhere. Likewise for the other types just * above. */ #define __LA_INO_T ino_t /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ #if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL __attribute__((dllimport)) extern # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries on all platforms and shared libraries on non-Windows. */ # define __LA_DECL #endif #ifdef __cplusplus extern "C" { #endif /* * Description of an archive entry. * * You can think of this as "struct stat" with some text fields added in. * * TODO: Add "comment", "charset", and possibly other entries that are * supported by "pax interchange" format. However, GNU, ustar, cpio, * and other variants don't support these features, so they're not an * excruciatingly high priority right now. * * TODO: "pax interchange" format allows essentially arbitrary * key/value attributes to be attached to any entry. Supporting * such extensions may make this library useful for special * applications (e.g., a package manager could attach special * package-management attributes to each entry). */ struct archive_entry; /* * File-type constants. These are returned from archive_entry_filetype() * and passed to archive_entry_set_filetype(). * * These values match S_XXX defines on every platform I've checked, * including Windows, AIX, Linux, Solaris, and BSD. They're * (re)defined here because platforms generally don't define the ones * they don't support. For example, Windows doesn't define S_IFLNK or * S_IFBLK. Instead of having a mass of conditional logic and system * checks to define any S_XXX values that aren't supported locally, * I've just defined a new set of such constants so that * libarchive-based applications can manipulate and identify archive * entries properly even if the hosting platform can't store them on * disk. * * These values are also used directly within some portable formats, * such as cpio. If you find a platform that varies from these, the * correct solution is to leave these alone and translate from these * portable values to platform-native values when entries are read from * or written to disk. */ #define AE_IFMT 0170000 #define AE_IFREG 0100000 #define AE_IFLNK 0120000 #define AE_IFSOCK 0140000 #define AE_IFCHR 0020000 #define AE_IFBLK 0060000 #define AE_IFDIR 0040000 #define AE_IFIFO 0010000 /* * Basic object manipulation */ __LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *); /* The 'clone' function does a deep copy; all of the strings are copied too. */ __LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *); __LA_DECL void archive_entry_free(struct archive_entry *); __LA_DECL struct archive_entry *archive_entry_new(void); /* * Retrieve fields from an archive_entry. * * There are a number of implicit conversions among these fields. For * example, if a regular string field is set and you read the _w wide * character field, the entry will implicitly convert narrow-to-wide * using the current locale. Similarly, dev values are automatically * updated when you write devmajor or devminor and vice versa. * * In addition, fields can be "set" or "unset." Unset string fields * return NULL, non-string fields have _is_set() functions to test * whether they've been set. You can "unset" a string field by * assigning NULL; non-string fields have _unset() functions to * unset them. * * Note: There is one ambiguity in the above; string fields will * also return NULL when implicit character set conversions fail. * This is usually what you want. */ __LA_DECL time_t archive_entry_atime(struct archive_entry *); __LA_DECL long archive_entry_atime_nsec(struct archive_entry *); __LA_DECL int archive_entry_atime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_birthtime(struct archive_entry *); __LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_ctime(struct archive_entry *); __LA_DECL long archive_entry_ctime_nsec(struct archive_entry *); __LA_DECL int archive_entry_ctime_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_dev(struct archive_entry *); __LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_devminor(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); __LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); __LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); __LA_DECL __LA_GID_T archive_entry_gid(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); __LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); __LA_DECL time_t archive_entry_mtime(struct archive_entry *); __LA_DECL long archive_entry_mtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_mtime_is_set(struct archive_entry *); __LA_DECL unsigned int archive_entry_nlink(struct archive_entry *); __LA_DECL const char *archive_entry_pathname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); __LA_DECL const char *archive_entry_sourcepath(struct archive_entry *); __LA_DECL __LA_INT64_T archive_entry_size(struct archive_entry *); __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL __LA_UID_T archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); /* * Set fields in an archive_entry. * * Note that string 'set' functions do not copy the string, only the pointer. * In contrast, 'copy' functions do copy the object pointed to. * * Note: As of libarchive 2.4, 'set' functions do copy the string and * are therefore exact synonyms for the 'copy' versions. The 'copy' * names will be retired in libarchive 3.0. */ __LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_atime(struct archive_entry *); __LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_birthtime(struct archive_entry *); __LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_ctime(struct archive_entry *); __LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_fflags(struct archive_entry *, unsigned long /* set */, unsigned long /* clear */); /* Returns pointer to start of first invalid token, or NULL if none. */ /* Note that all recognized tokens are processed, regardless. */ __LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *, const char *); __LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_gid(struct archive_entry *, __LA_GID_T); __LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_ino(struct archive_entry *, unsigned long); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_mtime(struct archive_entry *); __LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_size(struct archive_entry *, __LA_INT64_T); __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_UID_T); __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *); /* * Routines to bulk copy fields to/from a platform-native "struct * stat." Libarchive used to just store a struct stat inside of each * archive_entry object, but this created issues when trying to * manipulate archives on systems different than the ones they were * created on. * * TODO: On Linux, provide both stat32 and stat64 versions of these functions. */ __LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); /* * ACL routines. This used to simply store and return text-format ACL * strings, but that proved insufficient for a number of reasons: * = clients need control over uname/uid and gname/gid mappings * = there are many different ACL text formats * = would like to be able to read/convert archives containing ACLs * on platforms that lack ACL libraries * * This last point, in particular, forces me to implement a reasonably * complete set of ACL support routines. * * TODO: Extend this to support NFSv4/NTFS permissions. That should * allow full ACL support on Mac OS, in particular, which uses * POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions. */ /* * Permission bits mimic POSIX.1e. Note that I've not followed POSIX.1e's * "permset"/"perm" abstract type nonsense. A permset is just a simple * bitmap, following long-standing Unix tradition. */ #define ARCHIVE_ENTRY_ACL_EXECUTE 1 #define ARCHIVE_ENTRY_ACL_WRITE 2 #define ARCHIVE_ENTRY_ACL_READ 4 /* We need to be able to specify either or both of these. */ #define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256 #define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512 /* Tag values mimic POSIX.1e */ #define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */ #define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */ #define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */ #define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */ #define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access. */ #define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public. */ /* * Set the ACL by clearing it and adding entries one at a time. * Unlike the POSIX.1e ACL routines, you must specify the type * (access/default) for each entry. Internally, the ACL data is just * a soup of entries. API calls here allow you to retrieve just the * entries of interest. This design (which goes against the spirit of * POSIX.1e) is useful for handling archive formats that combine * default and access information in a single ACL list. */ __LA_DECL void archive_entry_acl_clear(struct archive_entry *); __LA_DECL void archive_entry_acl_add_entry(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const char * /* name */); __LA_DECL void archive_entry_acl_add_entry_w(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const wchar_t * /* name */); /* * To retrieve the ACL, first "reset", then repeatedly ask for the * "next" entry. The want_type parameter allows you to request only * access entries or only default entries. */ __LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */); __LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const char ** /* name */); __LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const wchar_t ** /* name */); /* * Construct a text-format ACL. The flags argument is a bitmask that * can include any of the following: * * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries. * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries. * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in * each ACL entry. (As used by 'star'.) * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each * default ACL entry. */ #define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024 #define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int /* flags */); /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); /* * Private ACL parser. This is private because it handles some * very weird formats that clients should not be messing with. * Clients should only deal with their platform-native formats. * Because of the need to support many formats cleanly, new arguments * are likely to get added on a regular basis. Clients who try to use * this interface are likely to be surprised when it changes. * * You were warned! * * TODO: Move this declaration out of the public header and into * a private header. Warnings above are silly. */ __LA_DECL int __archive_entry_acl_parse_w(struct archive_entry *, const wchar_t *, int /* type */); /* * extended attributes */ __LA_DECL void archive_entry_xattr_clear(struct archive_entry *); __LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *, const char * /* name */, const void * /* value */, size_t /* size */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_xattr_count(struct archive_entry *); __LA_DECL int archive_entry_xattr_reset(struct archive_entry *); __LA_DECL int archive_entry_xattr_next(struct archive_entry *, const char ** /* name */, const void ** /* value */, size_t *); /* * Utility to match up hardlinks. * * The 'struct archive_entry_linkresolver' is a cache of archive entries * for files with multiple links. Here's how to use it: * 1. Create a lookup object with archive_entry_linkresolver_new() * 2. Tell it the archive format you're using. * 3. Hand each archive_entry to archive_entry_linkify(). * That function will return 0, 1, or 2 entries that should * be written. * 4. Call archive_entry_linkify(resolver, NULL) until * no more entries are returned. * 5. Call archive_entry_linkresolver_free(resolver) to free resources. * * The entries returned have their hardlink and size fields updated * appropriately. If an entry is passed in that does not refer to * a file with multiple links, it is returned unchanged. The intention * is that you should be able to simply filter all entries through * this machine. * * To make things more efficient, be sure that each entry has a valid * nlinks value. The hardlink cache uses this to track when all links * have been found. If the nlinks value is zero, it will keep every * name in the cache indefinitely, which can use a lot of memory. * * Note that archive_entry_size() is reset to zero if the file * body should not be written to the archive. Pay attention! */ struct archive_entry_linkresolver; /* * There are three different strategies for marking hardlinks. * The descriptions below name them after the best-known * formats that rely on each strategy: * * "Old cpio" is the simplest, it always returns any entry unmodified. * As far as I know, only cpio formats use this. Old cpio archives * store every link with the full body; the onus is on the dearchiver * to detect and properly link the files as they are restored. * "tar" is also pretty simple; it caches a copy the first time it sees * any link. Subsequent appearances are modified to be hardlink * references to the first one without any body. Used by all tar * formats, although the newest tar formats permit the "old cpio" strategy * as well. This strategy is very simple for the dearchiver, * and reasonably straightforward for the archiver. * "new cpio" is trickier. It stores the body only with the last * occurrence. The complication is that we might not * see every link to a particular file in a single session, so * there's no easy way to know when we've seen the last occurrence. * The solution here is to queue one link until we see the next. * At the end of the session, you can enumerate any remaining * entries by calling archive_entry_linkify(NULL) and store those * bodies. If you have a file with three links l1, l2, and l3, * you'll get the following behavior if you see all three links: * linkify(l1) => NULL (the resolver stores l1 internally) * linkify(l2) => l1 (resolver stores l2, you write l1) * linkify(l3) => l2, l3 (all links seen, you can write both). * If you only see l1 and l2, you'll get this behavior: * linkify(l1) => NULL * linkify(l2) => l1 * linkify(NULL) => l2 (at end, you retrieve remaining links) * As the name suggests, this strategy is used by newer cpio variants. * It's noticeably more complex for the archiver, slightly more complex * for the dearchiver than the tar strategy, but makes it straightforward * to restore a file using any link by simply continuing to scan until * you see a link that is stored with a body. In contrast, the tar * strategy requires you to rescan the archive from the beginning to * correctly extract an arbitrary link. */ __LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void); __LA_DECL void archive_entry_linkresolver_set_strategy( struct archive_entry_linkresolver *, int /* format_code */); __LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *); __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); #ifdef __cplusplus } #endif /* This is meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ tarsnap-1.0.41/libarchive/archive_entry_copy_stat.c000066400000000000000000000070341476705112600224650ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_entry_copy_stat.c,v 1.2 2008/09/30 03:53:03 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #include "archive_entry.h" void archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.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_atime(entry, st->st_atime, st->st_atim.tv_nsec); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.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_atime(entry, st->st_atime, st->st_atime_n); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n); archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000); archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000); archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000); archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000); #else archive_entry_set_atime(entry, st->st_atime, 0); archive_entry_set_ctime(entry, st->st_ctime, 0); archive_entry_set_mtime(entry, st->st_mtime, 0); #if HAVE_STRUCT_STAT_ST_BIRTHTIME archive_entry_set_birthtime(entry, st->st_birthtime, 0); #endif #endif #if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec); #endif archive_entry_set_dev(entry, st->st_dev); archive_entry_set_gid(entry, st->st_gid); archive_entry_set_uid(entry, st->st_uid); archive_entry_set_ino(entry, st->st_ino); archive_entry_set_nlink(entry, st->st_nlink); archive_entry_set_rdev(entry, st->st_rdev); archive_entry_set_size(entry, st->st_size); archive_entry_set_mode(entry, st->st_mode); } tarsnap-1.0.41/libarchive/archive_entry_link_resolver.c000066400000000000000000000252711476705112600233410ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_entry_link_resolver.c,v 1.4 2008/09/05 06:15:25 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #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" /* * This is mostly a pretty straightforward hash table implementation. * The only interesting bit is the different strategies used to * match up links. These strategies match those used by various * archiving formats: * tar - content stored with first link, remainder refer back to it. * This requires us to match each subsequent link up with the * first appearance. * cpio - Old cpio just stored body with each link, match-ups were * implicit. This is trivial. * new cpio - New cpio only stores body with last link, match-ups * are implicit. This is actually quite tricky; see the notes * below. */ /* Users pass us a format code, we translate that into a strategy here. */ #define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0 #define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1 #define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2 #define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3 /* Initial size of link cache. */ #define links_cache_initial_size 1024 struct links_entry { struct links_entry *next; struct links_entry *previous; int links; /* # links not yet seen */ int hash; struct archive_entry *canonical; struct archive_entry *entry; }; struct archive_entry_linkresolver { struct links_entry **buckets; struct links_entry *spare; unsigned long number_entries; size_t number_buckets; int strategy; }; static struct links_entry *find_entry(struct archive_entry_linkresolver *, struct archive_entry *); static void grow_hash(struct archive_entry_linkresolver *); static struct links_entry *insert_entry(struct archive_entry_linkresolver *, struct archive_entry *); static struct links_entry *next_entry(struct archive_entry_linkresolver *); struct archive_entry_linkresolver * archive_entry_linkresolver_new(void) { struct archive_entry_linkresolver *res; size_t i; res = malloc(sizeof(struct archive_entry_linkresolver)); if (res == NULL) return (NULL); memset(res, 0, sizeof(struct archive_entry_linkresolver)); res->spare = NULL; res->number_buckets = links_cache_initial_size; res->buckets = malloc(res->number_buckets * sizeof(res->buckets[0])); if (res->buckets == NULL) { free(res); return (NULL); } for (i = 0; i < res->number_buckets; i++) res->buckets[i] = NULL; return (res); } void archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res, int fmt) { int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK; switch (fmtbase) { case ARCHIVE_FORMAT_CPIO: switch (fmt) { case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC: case ARCHIVE_FORMAT_CPIO_SVR4_CRC: res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO; break; default: res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO; break; } break; case ARCHIVE_FORMAT_MTREE: res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE; break; case ARCHIVE_FORMAT_TAR: res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR; break; default: res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR; break; } } void archive_entry_linkresolver_free(struct archive_entry_linkresolver *res) { struct links_entry *le; if (res == NULL) return; if (res->buckets != NULL) { while ((le = next_entry(res)) != NULL) archive_entry_free(le->entry); free(res->buckets); res->buckets = NULL; } free(res); } void archive_entry_linkify(struct archive_entry_linkresolver *res, struct archive_entry **e, struct archive_entry **f) { struct links_entry *le; struct archive_entry *t; *f = NULL; /* Default: Don't return a second entry. */ if (*e == NULL) { le = next_entry(res); if (le != NULL) { *e = le->entry; le->entry = NULL; } return; } /* If it has only one link, then we're done. */ if (archive_entry_nlink(*e) == 1) return; /* Directories never have hardlinks. */ if (archive_entry_filetype(*e) == AE_IFDIR) return; switch (res->strategy) { case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR: le = find_entry(res, *e); if (le != NULL) { archive_entry_unset_size(*e); archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); } else insert_entry(res, *e); return; case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE: le = find_entry(res, *e); if (le != NULL) { archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); } else insert_entry(res, *e); return; case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO: /* This one is trivial. */ return; case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO: le = find_entry(res, *e); if (le != NULL) { /* * Put the new entry in le, return the * old entry from le. */ t = *e; *e = le->entry; le->entry = t; /* Make the old entry into a hardlink. */ archive_entry_unset_size(*e); archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); /* If we ran out of links, return the * final entry as well. */ if (le->links == 0) { *f = le->entry; le->entry = NULL; } } else { /* * If we haven't seen it, tuck it away * for future use. */ le = insert_entry(res, *e); le->entry = *e; *e = NULL; } return; default: break; } return; } static struct links_entry * find_entry(struct archive_entry_linkresolver *res, struct archive_entry *entry) { struct links_entry *le; int hash, bucket; dev_t dev; ino_t ino; /* Free a held entry. */ if (res->spare != NULL) { archive_entry_free(res->spare->canonical); archive_entry_free(res->spare->entry); free(res->spare); res->spare = NULL; } /* If the links cache overflowed and got flushed, don't bother. */ if (res->buckets == NULL) return (NULL); dev = archive_entry_dev(entry); ino = archive_entry_ino(entry); hash = dev ^ ino; /* Try to locate this entry in the links cache. */ bucket = hash % res->number_buckets; for (le = res->buckets[bucket]; le != NULL; le = le->next) { if (le->hash == hash && dev == archive_entry_dev(le->canonical) && ino == archive_entry_ino(le->canonical)) { /* * Decrement link count each time and release * the entry if it hits zero. This saves * memory and is necessary for detecting * missed links. */ --le->links; if (le->links > 0) return (le); /* Remove it from this hash bucket. */ if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (res->buckets[bucket] == le) res->buckets[bucket] = le->next; res->number_entries--; /* Defer freeing this entry. */ res->spare = le; return (le); } } return (NULL); } static struct links_entry * next_entry(struct archive_entry_linkresolver *res) { struct links_entry *le; size_t bucket; /* Free a held entry. */ if (res->spare != NULL) { archive_entry_free(res->spare->canonical); free(res->spare); res->spare = NULL; } /* If the links cache overflowed and got flushed, don't bother. */ if (res->buckets == NULL) return (NULL); /* Look for next non-empty bucket in the links cache. */ for (bucket = 0; bucket < res->number_buckets; bucket++) { le = res->buckets[bucket]; if (le != NULL) { /* Remove it from this hash bucket. */ if (le->next != NULL) le->next->previous = le->previous; res->buckets[bucket] = le->next; res->number_entries--; /* Defer freeing this entry. */ res->spare = le; return (le); } } return (NULL); } static struct links_entry * insert_entry(struct archive_entry_linkresolver *res, struct archive_entry *entry) { struct links_entry *le; int hash, bucket; /* Add this entry to the links cache. */ le = malloc(sizeof(struct links_entry)); if (le == NULL) return (NULL); memset(le, 0, sizeof(*le)); le->canonical = archive_entry_clone(entry); /* If the links cache is getting too full, enlarge the hash table. */ if (res->number_entries > res->number_buckets * 2) grow_hash(res); hash = archive_entry_dev(entry) ^ archive_entry_ino(entry); bucket = hash % res->number_buckets; /* If we could allocate the entry, record it. */ if (res->buckets[bucket] != NULL) res->buckets[bucket]->previous = le; res->number_entries++; le->next = res->buckets[bucket]; le->previous = NULL; res->buckets[bucket] = le; le->hash = hash; le->links = archive_entry_nlink(entry) - 1; return (le); } static void grow_hash(struct archive_entry_linkresolver *res) { struct links_entry *le, **new_buckets; size_t new_size; size_t i, bucket; /* Try to enlarge the bucket list. */ new_size = res->number_buckets * 2; new_buckets = malloc(new_size * sizeof(struct links_entry *)); if (new_buckets != NULL) { memset(new_buckets, 0, new_size * sizeof(struct links_entry *)); for (i = 0; i < res->number_buckets; i++) { while (res->buckets[i] != NULL) { /* Remove entry from old bucket. */ le = res->buckets[i]; res->buckets[i] = le->next; /* Add entry to new bucket. */ bucket = le->hash % new_size; if (new_buckets[bucket] != NULL) new_buckets[bucket]->previous = le; le->next = new_buckets[bucket]; le->previous = NULL; new_buckets[bucket] = le; } } free(res->buckets); res->buckets = new_buckets; res->number_buckets = new_size; } } tarsnap-1.0.41/libarchive/archive_entry_private.h000066400000000000000000000137641476705112600221460ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.6 2008/09/30 03:53:03 kientzle Exp $ */ #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED #define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED #include "archive_string.h" /* * Handle wide character (i.e., Unicode) and non-wide character * strings transparently. */ struct aes { struct archive_string aes_mbs; struct archive_string aes_utf8; const wchar_t *aes_wcs; /* Bitmap of which of the above are valid. Because we're lazy * about malloc-ing and reusing the underlying storage, we * can't rely on NULL pointers to indicate whether a string * has been set. */ int aes_set; #define AES_SET_MBS 1 #define AES_SET_UTF8 2 #define AES_SET_WCS 4 }; struct ae_acl { struct ae_acl *next; int type; /* E.g., access or default */ int tag; /* E.g., user/group/other/mask */ int permset; /* r/w/x bits */ int id; /* uid/gid for user/group */ struct aes name; /* uname/gname */ }; struct ae_xattr { struct ae_xattr *next; char *name; void *value; size_t size; }; /* * Description of an archive entry. * * Basically, this is a "struct stat" with a few text fields added in. * * TODO: Add "comment", "charset", and possibly other entries * that are supported by "pax interchange" format. However, GNU, ustar, * cpio, and other variants don't support these features, so they're not an * excruciatingly high priority right now. * * TODO: "pax interchange" format allows essentially arbitrary * key/value attributes to be attached to any entry. Supporting * such extensions may make this library useful for special * applications (e.g., a package manager could attach special * package-management attributes to each entry). There are tricky * API issues involved, so this is not going to happen until * there's a real demand for it. * * TODO: Design a good API for handling sparse files. */ struct archive_entry { /* * Note that ae_stat.st_mode & AE_IFMT can be 0! * * This occurs when the actual file type of the object is not * in the archive. For example, 'tar' archives store * hardlinks without marking the type of the underlying * object. */ /* * We have a "struct aest" for holding file metadata rather than just * a "struct stat" because on some platforms the "struct stat" has * fields which are too narrow to hold the range of possible values; * we don't want to lose information if we read an archive and write * out another (e.g., in "tar -cf new.tar @old.tar"). * * The "stat" pointer points to some form of platform-specific struct * stat; it is declared as a void * rather than a struct stat * as * some platforms have multiple varieties of stat structures. */ void *stat; int stat_valid; /* Set to 0 whenever a field in aest changes. */ struct aest { int64_t aest_atime; uint32_t aest_atime_nsec; int64_t aest_ctime; uint32_t aest_ctime_nsec; int64_t aest_mtime; uint32_t aest_mtime_nsec; int64_t aest_birthtime; uint32_t aest_birthtime_nsec; gid_t aest_gid; ino_t aest_ino; mode_t aest_mode; uint32_t aest_nlink; uint64_t aest_size; uid_t aest_uid; /* * Because converting between device codes and * major/minor values is platform-specific and * inherently a bit risky, we only do that conversion * lazily. That way, we will do a better job of * preserving information in those cases where no * conversion is actually required. */ int aest_dev_is_broken_down; dev_t aest_dev; dev_t aest_devmajor; dev_t aest_devminor; int aest_rdev_is_broken_down; dev_t aest_rdev; dev_t aest_rdevmajor; dev_t aest_rdevminor; } ae_stat; int ae_set; /* bitmap of fields that are currently set */ #define AE_SET_HARDLINK 1 #define AE_SET_SYMLINK 2 #define AE_SET_ATIME 4 #define AE_SET_CTIME 8 #define AE_SET_MTIME 16 #define AE_SET_BIRTHTIME 32 #define AE_SET_SIZE 64 /* * Use aes here so that we get transparent mbs<->wcs conversions. */ struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */ unsigned long ae_fflags_set; /* Bitmap fflags */ unsigned long ae_fflags_clear; struct aes ae_gname; /* Name of owning group */ struct aes ae_hardlink; /* Name of target for hardlink */ struct aes ae_pathname; /* Name of entry */ struct aes ae_symlink; /* symlink contents */ struct aes ae_uname; /* Name of owner */ /* Not used within libarchive; useful for some clients. */ struct aes ae_sourcepath; /* Path this entry is sourced from. */ /* ACL support. */ struct ae_acl *acl_head; struct ae_acl *acl_p; int acl_state; /* See acl_next for details. */ wchar_t *acl_text_w; /* extattr support. */ struct ae_xattr *xattr_head; struct ae_xattr *xattr_p; /* Miscellaneous. */ char strmode[12]; }; #endif /* !ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ tarsnap-1.0.41/libarchive/archive_entry_stat.c000066400000000000000000000105231476705112600214300ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_entry_stat.c,v 1.2 2008/09/30 03:53:03 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include "archive_entry.h" #include "archive_entry_private.h" const struct stat * archive_entry_stat(struct archive_entry *entry) { struct stat *st; if (entry->stat == NULL) { entry->stat = malloc(sizeof(*st)); if (entry->stat == NULL) return (NULL); entry->stat_valid = 0; } /* * If none of the underlying fields have been changed, we * don't need to regenerate. In theory, we could use a bitmap * here to flag only those items that have changed, but the * extra complexity probably isn't worth it. It will be very * rare for anyone to change just one field then request a new * stat structure. */ if (entry->stat_valid) return (entry->stat); st = entry->stat; /* * Use the public interfaces to extract items, so that * the appropriate conversions get invoked. */ st->st_atime = archive_entry_atime(entry); #if HAVE_STRUCT_STAT_ST_BIRTHTIME st->st_birthtime = archive_entry_birthtime(entry); #endif st->st_ctime = archive_entry_ctime(entry); st->st_mtime = archive_entry_mtime(entry); st->st_dev = archive_entry_dev(entry); st->st_gid = archive_entry_gid(entry); st->st_uid = archive_entry_uid(entry); st->st_ino = archive_entry_ino(entry); st->st_nlink = archive_entry_nlink(entry); st->st_rdev = archive_entry_rdev(entry); st->st_size = archive_entry_size(entry); st->st_mode = archive_entry_mode(entry); /* * On systems that support high-res timestamps, copy that * information into struct stat. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry); st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry); st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC st->st_atim.tv_nsec = archive_entry_atime_nsec(entry); st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry); st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry); #elif HAVE_STRUCT_STAT_ST_MTIME_N st->st_atime_n = archive_entry_atime_nsec(entry); st->st_ctime_n = archive_entry_ctime_nsec(entry); st->st_mtime_n = archive_entry_mtime_nsec(entry); #elif HAVE_STRUCT_STAT_ST_UMTIME st->st_uatime = archive_entry_atime_nsec(entry) / 1000; st->st_uctime = archive_entry_ctime_nsec(entry) / 1000; st->st_umtime = archive_entry_mtime_nsec(entry) / 1000; #elif HAVE_STRUCT_STAT_ST_MTIME_USEC st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000; st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000; st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000; #endif #if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry); #endif /* * TODO: On Linux, store 32 or 64 here depending on whether * the cached stat structure is a stat32 or a stat64. This * will allow us to support both variants interchangably. */ entry->stat_valid = 1; return (st); } tarsnap-1.0.41/libarchive/archive_entry_strmode.c000066400000000000000000000053211476705112600221320ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_entry_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive_entry.h" #include "archive_entry_private.h" const char * archive_entry_strmode(struct archive_entry *entry) { static const mode_t permbits[] = { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 }; char *bp = entry->strmode; mode_t mode; int i; /* Fill in a default string, then selectively override. */ strcpy(bp, "?rwxrwxrwx "); mode = archive_entry_mode(entry); switch (archive_entry_filetype(entry)) { case AE_IFREG: bp[0] = '-'; break; case AE_IFBLK: bp[0] = 'b'; break; case AE_IFCHR: bp[0] = 'c'; break; case AE_IFDIR: bp[0] = 'd'; break; case AE_IFLNK: bp[0] = 'l'; break; case AE_IFSOCK: bp[0] = 's'; break; case AE_IFIFO: bp[0] = 'p'; break; default: if (archive_entry_hardlink(entry) != NULL) { bp[0] = 'h'; break; } } for (i = 0; i < 9; i++) if (!(mode & permbits[i])) bp[i+1] = '-'; if (mode & S_ISUID) { if (mode & 0100) bp[3] = 's'; else bp[3] = 'S'; } if (mode & S_ISGID) { if (mode & 0010) bp[6] = 's'; else bp[6] = 'S'; } if (mode & S_ISVTX) { if (mode & 0001) bp[9] = 't'; else bp[9] = 'T'; } if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)) bp[10] = '+'; return (bp); } tarsnap-1.0.41/libarchive/archive_platform.h000066400000000000000000000105421476705112600210660ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.32 2008/12/06 05:53:05 kientzle Exp $ */ /* * This header is the first thing included in any of the libarchive * source files. As far as possible, platform-specific issues should * be dealt with here and not within individual source files. I'm * actively trying to minimize #if blocks within the main source, * since they obfuscate the code. */ #ifndef ARCHIVE_PLATFORM_H_INCLUDED #define ARCHIVE_PLATFORM_H_INCLUDED /* archive.h and archive_entry.h require this. */ #define __LIBARCHIVE_BUILD 1 #if defined(PLATFORM_CONFIG_H) /* Use hand-built config.h in environments that need it. */ #include PLATFORM_CONFIG_H #elif defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ #include "config.h" #else /* Warn if the library hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no pre-built configuration in archive_platform.h. #endif /* * The config files define a lot of feature macros. The following * uses those macros to select/define replacements and include key * headers as required. */ /* Get a real definition for __FBSDID if we can */ #if HAVE_SYS_CDEFS_H #include #endif /* If not, define it so as to avoid dangling semicolons. */ #ifndef __FBSDID #define __FBSDID(a) struct _undefined_hack #endif /* Try to get standard C99-style integer type definitions. */ #if HAVE_INTTYPES_H #include #endif #if HAVE_STDINT_H #include #endif /* Some platforms lack the standard *_MAX definitions. */ #if !HAVE_DECL_SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #if !HAVE_DECL_SSIZE_MAX #define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1)) #endif #if !HAVE_DECL_UINT32_MAX #define UINT32_MAX (~(uint32_t)0) #endif #if !HAVE_DECL_UINT64_MAX #define UINT64_MAX (~(uint64_t)0) #endif #if !HAVE_DECL_INT64_MAX #define INT64_MAX ((int64_t)(UINT64_MAX >> 1)) #endif #if !HAVE_DECL_INT64_MIN #define INT64_MIN ((int64_t)(~INT64_MAX)) #endif /* * If this platform has , acl_create(), acl_init(), * acl_set_file(), and ACL_USER, we assume it has the rest of the * POSIX.1e draft functions used in archive_read_extract.c. */ #if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE && HAVE_ACL_USER #define HAVE_POSIX_ACL 1 #endif /* * If we can't restore metadata using a file descriptor, then * for compatibility's sake, close files before trying to restore metadata. */ #if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN) #define CAN_RESTORE_METADATA_FD #endif /* Set up defaults for internal error codes. */ #ifndef ARCHIVE_ERRNO_FILE_FORMAT #if HAVE_EFTYPE #define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE #else #if HAVE_EILSEQ #define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ #else #define ARCHIVE_ERRNO_FILE_FORMAT EINVAL #endif #endif #endif #ifndef ARCHIVE_ERRNO_PROGRAMMER #define ARCHIVE_ERRNO_PROGRAMMER EINVAL #endif #ifndef ARCHIVE_ERRNO_MISC #define ARCHIVE_ERRNO_MISC (-1) #endif #endif /* !ARCHIVE_PLATFORM_H_INCLUDED */ tarsnap-1.0.41/libarchive/archive_private.h000066400000000000000000000073521476705112600207210ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.32 2008/12/06 06:23:37 kientzle Exp $ */ #ifndef ARCHIVE_PRIVATE_H_INCLUDED #define ARCHIVE_PRIVATE_H_INCLUDED #include "archive.h" #include "archive_string.h" #if defined(__GNUC__) && (__GNUC__ > 2 || \ (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) #define __LA_DEAD __attribute__((__noreturn__)) #else #define __LA_DEAD #endif #define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU) #define ARCHIVE_READ_MAGIC (0xdeb0c5U) #define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U) #define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U) #define ARCHIVE_STATE_ANY 0xFFFFU #define ARCHIVE_STATE_NEW 1U #define ARCHIVE_STATE_HEADER 2U #define ARCHIVE_STATE_DATA 4U #define ARCHIVE_STATE_DATA_END 8U #define ARCHIVE_STATE_EOF 0x10U #define ARCHIVE_STATE_CLOSED 0x20U #define ARCHIVE_STATE_FATAL 0x8000U struct archive_vtable { int (*archive_close)(struct archive *); int (*archive_finish)(struct archive *); int (*archive_write_header)(struct archive *, struct archive_entry *); int (*archive_write_finish_entry)(struct archive *); ssize_t (*archive_write_data)(struct archive *, const void *, size_t); ssize_t (*archive_write_data_block)(struct archive *, const void *, size_t, off_t); }; struct archive { /* * The magic/state values are used to sanity-check the * client's usage. If an API function is called at a * ridiculous time, or the client passes us an invalid * pointer, these values allow me to catch that. */ unsigned int magic; unsigned int state; /* * Some public API functions depend on the "real" type of the * archive object. */ struct archive_vtable *vtable; int archive_format; const char *archive_format_name; int compression_code; /* Currently active compression. */ const char *compression_name; /* Position in UNCOMPRESSED data stream. */ off_t file_position; /* Position in COMPRESSED data stream. */ off_t raw_position; /* Number of file entries processed. */ int file_count; int archive_error_number; const char *error; struct archive_string error_string; }; /* Check magic value and state; exit if it isn't valid. */ void __archive_check_magic(struct archive *, unsigned int magic, unsigned int state, const char *func); void __archive_errx(int retvalue, const char *msg) __LA_DEAD; int __archive_parse_options(const char *p, const char *fn, int keysize, char *key, int valsize, char *val); #define err_combine(a,b) ((a) < (b) ? (a) : (b)) #endif tarsnap-1.0.41/libarchive/archive_read.c000066400000000000000000001040501476705112600201460ustar00rootroot00000000000000/*- * 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. */ /* * This file contains the "essential" portions of the read API, that * is, stuff that will probably always be used by any client that * actually needs to read an archive. Optional pieces have been, as * far as possible, separated out into separate files to avoid * needlessly bloating statically-linked clients. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.39 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #define minimum(a, b) (a < b ? a : b) static int build_stream(struct archive_read *); static int choose_format(struct archive_read *); static struct archive_vtable *archive_read_vtable(void); static int _archive_read_close(struct archive *); static int _archive_read_finish(struct archive *); static struct archive_vtable * archive_read_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_finish = _archive_read_finish; av.archive_close = _archive_read_close; inited = 1; } return (&av); } /* * Allocate, initialize and return a struct archive object. */ struct archive * archive_read_new(void) { struct archive_read *a; a = (struct archive_read *)malloc(sizeof(*a)); if (a == NULL) return (NULL); memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_READ_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->entry = archive_entry_new(); a->archive.vtable = archive_read_vtable(); return (&a->archive); } /* * Record the do-not-extract-to file. This belongs in archive_read_extract.c. */ void archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i) { struct archive_read *a = (struct archive_read *)_a; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file"); a->skip_file_dev = d; a->skip_file_ino = i; } /* * Set read options for the format. */ int archive_read_set_format_options(struct archive *_a, const char *s) { struct archive_read *a; struct archive_format_descriptor *format; char key[64], val[64]; size_t i; int len, r; if (s == NULL || *s == '\0') return (ARCHIVE_OK); a = (struct archive_read *)_a; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_format_options"); len = 0; for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) { format = &a->formats[i]; if (format == NULL || format->options == NULL || format->name == NULL) /* This format does not support option. */ continue; while ((len = __archive_parse_options(s, format->name, sizeof(key), key, sizeof(val), val)) > 0) { if (val[0] == '\0') r = format->options(a, key, NULL); else r = format->options(a, key, val); if (r == ARCHIVE_FATAL) return (r); s += len; } } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Illegal format options."); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * Set read options for the filter. */ int archive_read_set_filter_options(struct archive *_a, const char *s) { struct archive_read *a; struct archive_read_filter *filter; struct archive_read_filter_bidder *bidder; char key[64], val[64]; int len, r; if (s == NULL || *s == '\0') return (ARCHIVE_OK); a = (struct archive_read *)_a; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_filter_options"); len = 0; for (filter = a->filter; filter != NULL; filter = filter->upstream) { bidder = filter->bidder; if (bidder == NULL) continue; if (bidder->options == NULL) /* This bidder does not support option */ continue; while ((len = __archive_parse_options(s, filter->name, sizeof(key), key, sizeof(val), val)) > 0) { if (val[0] == '\0') r = bidder->options(bidder, key, NULL); else r = bidder->options(bidder, key, val); if (r == ARCHIVE_FATAL) return (r); s += len; } } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Illegal format options."); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * Set read options for the format and the filter. */ int archive_read_set_options(struct archive *_a, const char *s) { int r; r = archive_read_set_format_options(_a, s); if (r != ARCHIVE_OK) return (r); r = archive_read_set_filter_options(_a, s); if (r != ARCHIVE_OK) return (r); return (ARCHIVE_OK); } /* * Open the archive */ int archive_read_open(struct archive *a, void *client_data, archive_open_callback *client_opener, archive_read_callback *client_reader, archive_close_callback *client_closer) { /* Old archive_read_open() is just a thin shell around * archive_read_open2. */ return archive_read_open2(a, client_data, client_opener, client_reader, NULL, client_closer); } static ssize_t client_read_proxy(struct archive_read_filter *self, const void **buff) { ssize_t r; r = (self->archive->client.reader)(&self->archive->archive, self->data, buff); self->archive->archive.raw_position += r; return (r); } static int64_t client_skip_proxy(struct archive_read_filter *self, int64_t request) { int64_t r; if (self->archive->client.skipper == NULL) return (0); r = (self->archive->client.skipper)(&self->archive->archive, self->data, request); self->archive->archive.raw_position += r; return (r); } static int client_close_proxy(struct archive_read_filter *self) { int r = ARCHIVE_OK; if (self->archive->client.closer != NULL) r = (self->archive->client.closer)((struct archive *)self->archive, self->data); self->data = NULL; return (r); } int archive_read_open2(struct archive *_a, void *client_data, archive_open_callback *client_opener, archive_read_callback *client_reader, archive_skip_callback *client_skipper, archive_close_callback *client_closer) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter; int e; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); if (client_reader == NULL) __archive_errx(1, "No reader function provided to archive_read_open"); /* Open data source. */ if (client_opener != NULL) { e =(client_opener)(&a->archive, client_data); if (e != 0) { /* If the open failed, call the closer to clean up. */ if (client_closer) (client_closer)(&a->archive, client_data); return (e); } } /* Save the client functions and mock up the initial source. */ a->client.reader = client_reader; a->client.skipper = client_skipper; a->client.closer = client_closer; filter = calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = NULL; filter->upstream = NULL; filter->archive = a; filter->data = client_data; filter->read = client_read_proxy; filter->skip = client_skip_proxy; filter->close = client_close_proxy; filter->name = "none"; filter->code = ARCHIVE_COMPRESSION_NONE; a->filter = filter; /* Build out the input pipeline. */ e = build_stream(a); if (e == ARCHIVE_OK) a->archive.state = ARCHIVE_STATE_HEADER; return (e); } /* * Allow each registered stream transform to bid on whether * it wants to handle this stream. Repeat until we've finished * building the pipeline. */ static int build_stream(struct archive_read *a) { int number_bidders, i, bid, best_bid; struct archive_read_filter_bidder *bidder, *best_bidder; struct archive_read_filter *filter; int r; for (;;) { number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); best_bid = 0; best_bidder = NULL; bidder = a->bidders; for (i = 0; i < number_bidders; i++, bidder++) { if (bidder->bid != NULL) { bid = (bidder->bid)(bidder, a->filter); if (bid > best_bid) { best_bid = bid; best_bidder = bidder; } } } /* If no bidder, we're done. */ if (best_bidder == NULL) { a->archive.compression_name = a->filter->name; a->archive.compression_code = a->filter->code; return (ARCHIVE_OK); } filter = (struct archive_read_filter *)calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = best_bidder; filter->archive = a; filter->upstream = a->filter; r = (best_bidder->init)(filter); if (r != ARCHIVE_OK) { free(filter); return (r); } a->filter = filter; } } /* * Read header of next entry. */ int archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read *a = (struct archive_read *)_a; int slot, ret; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header"); archive_entry_clear(entry); archive_clear_error(&a->archive); /* * If no format has yet been chosen, choose one. */ if (a->format == NULL) { slot = choose_format(a); if (slot < 0) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->format = &(a->formats[slot]); } /* * If client didn't consume entire data, skip any remainder * (This is especially important for GNU incremental directories.) */ if (a->archive.state == ARCHIVE_STATE_DATA) { ret = archive_read_data_skip(&a->archive); if (ret == ARCHIVE_EOF) { archive_set_error(&a->archive, EIO, "Premature end-of-file."); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (ret != ARCHIVE_OK) return (ret); } /* Record start-of-header. */ a->header_position = a->archive.file_position; ++_a->file_count; ret = (a->format->read_header)(a, entry); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (ret) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; --_a->file_count;/* Revert a file counter. */ break; case ARCHIVE_OK: a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_WARN: a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } a->read_data_output_offset = 0; a->read_data_remaining = 0; return (ret); } int archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read *a = (struct archive_read *)_a; *entryp = NULL; ret = archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } /* * Allow each registered format to bid on whether it wants to handle * the next entry. Return index of winning bidder. */ static int choose_format(struct archive_read *a) { int slots; int i; int bid, best_bid; int best_bid_slot; slots = sizeof(a->formats) / sizeof(a->formats[0]); best_bid = -1; best_bid_slot = -1; /* Set up a->format for convenience of bidders. */ a->format = &(a->formats[0]); for (i = 0; i < slots; i++, a->format++) { if (a->format->bid) { bid = (a->format->bid)(a); if (bid == ARCHIVE_FATAL) return (ARCHIVE_FATAL); if ((bid > best_bid) || (best_bid_slot < 0)) { best_bid = bid; best_bid_slot = i; } } } /* * There were no bidders; this is a serious programmer error * and demands a quick and definitive abort. */ if (best_bid_slot < 0) __archive_errx(1, "No formats were registered; you must " "invoke at least one archive_read_support_format_XXX " "function in order to successfully read an archive."); /* * There were bidders, but no non-zero bids; this means we * can't support this stream. */ if (best_bid < 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized archive format"); return (ARCHIVE_FATAL); } return (best_bid_slot); } /* * Return the file offset (within the uncompressed data stream) where * the last header started. */ int64_t archive_read_header_position(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_header_position"); return (a->header_position); } /* * Read data from an archive entry, using a read(2)-style interface. * This is a convenience routine that just calls * archive_read_data_block and copies the results into the client * buffer, filling any gaps with zero bytes. Clients using this * API can be completely ignorant of sparse-file issues; sparse files * will simply be padded with nulls. * * DO NOT intermingle calls to this function and archive_read_data_block * to read a single entry body. */ ssize_t archive_read_data(struct archive *_a, void *buff, size_t s) { struct archive_read *a = (struct archive_read *)_a; char *dest; const void *read_buf; size_t bytes_read; size_t len; int r; bytes_read = 0; dest = (char *)buff; while (s > 0) { if (a->read_data_remaining == 0) { read_buf = a->read_data_block; r = archive_read_data_block(&a->archive, &read_buf, &a->read_data_remaining, &a->read_data_offset); a->read_data_block = read_buf; if (r == ARCHIVE_EOF) return (bytes_read); /* * Error codes are all negative, so the status * return here cannot be confused with a valid * byte count. (ARCHIVE_OK is zero.) */ if (r < ARCHIVE_OK) return (r); } if (a->read_data_offset < a->read_data_output_offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encountered out-of-order sparse blocks"); return (ARCHIVE_RETRY); } /* Compute the amount of zero padding needed. */ if (a->read_data_output_offset + (off_t)s < a->read_data_offset) { len = s; } else if (a->read_data_output_offset < a->read_data_offset) { len = a->read_data_offset - a->read_data_output_offset; } else len = 0; /* Add zeroes. */ memset(dest, 0, len); s -= len; a->read_data_output_offset += len; dest += len; bytes_read += len; /* Copy data if there is any space left. */ if (s > 0) { len = a->read_data_remaining; if (len > s) len = s; memcpy(dest, a->read_data_block, len); s -= len; a->read_data_block += len; a->read_data_remaining -= len; a->read_data_output_offset += len; a->read_data_offset += len; dest += len; bytes_read += len; } } return (bytes_read); } #if ARCHIVE_API_VERSION < 3 /* * Obsolete function provided for compatibility only. Note that the API * of this function doesn't allow the caller to detect if the remaining * data from the archive entry is shorter than the buffer provided, or * even if an error occurred while reading data. */ int archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len) { archive_read_data(a, d, len); return (ARCHIVE_OK); } #endif /* * Return the amount of buffered data (data read from the client which has * not yet been passed back via archive_read_data_*), or -1 if unknown. */ ssize_t archive_read_get_backlog(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; return (a->read_data_remaining); } /* * Return the remaining length of the current archive entry, including any * padding which exists in the archive format. */ off_t archive_read_get_entryleft(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; off_t len; if (a->format->read_get_entryleft == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Archive format does not support read_get_entryleft"); return (-1); } len = (a->format->read_get_entryleft)(a); if (len < 0) return (-1); return (len + a->read_data_remaining); } /* * Advance the position within the archive entry. */ int archive_read_advance(struct archive *_a, off_t offset) { struct archive_read *a = (struct archive_read *)_a; if (a->read_data_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Called read_advance with read_data_remaining non-zero"); return (-1); } if (a->format->read_advance == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Archive format does not support read_advance"); return (-1); } return ((a->format->read_advance)(a, offset)); } /* * Skip over all remaining data in this entry. */ int archive_read_data_skip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r; const void *buff; size_t size; off_t offset; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_skip"); if (a->format->read_data_skip != NULL) r = (a->format->read_data_skip)(a); else { while ((r = archive_read_data_block(&a->archive, &buff, &size, &offset)) == ARCHIVE_OK) ; } if (r == ARCHIVE_EOF) r = ARCHIVE_OK; a->archive.state = ARCHIVE_STATE_HEADER; return (r); } /* * Read the next block of entry data from the archive. * This is a zero-copy interface; the client receives a pointer, * size, and file offset of the next available block of data. * * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if * the end of entry is encountered. */ int archive_read_data_block(struct archive *_a, const void **buff, size_t *size, off_t *offset) { struct archive_read *a = (struct archive_read *)_a; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (a->format->read_data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " "No format->read_data function registered"); return (ARCHIVE_FATAL); } return (a->format->read_data)(a, buff, size, offset); } /* * Close the file and release most resources. * * Be careful: client might just call read_new and then read_finish. * Don't assume we actually read anything or performed any non-trivial * initialization. */ static int _archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; size_t i, n; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_close"); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; /* Call cleanup functions registered by optional components. */ if (a->cleanup_archive_extract != NULL) r = (a->cleanup_archive_extract)(a); /* TODO: Clean up the formatters. */ /* Clean up the filter pipeline. */ while (a->filter != NULL) { struct archive_read_filter *t = a->filter->upstream; if (a->filter->close != NULL) { r1 = (a->filter->close)(a->filter); if (r1 < r) r = r1; } free(a->filter->buffer); free(a->filter); a->filter = t; } /* Release the bidder objects. */ n = sizeof(a->bidders)/sizeof(a->bidders[0]); for (i = 0; i < n; i++) { if (a->bidders[i].free != NULL) { r1 = (a->bidders[i].free)(&a->bidders[i]); if (r1 < r) r = r1; } } return (r); } /* * Release memory and other resources. */ int _archive_read_finish(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int i; int slots; int r = ARCHIVE_OK; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_finish"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = archive_read_close(&a->archive); /* Cleanup format-specific data. */ slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < slots; i++) { a->format = &(a->formats[i]); if (a->formats[i].cleanup) (a->formats[i].cleanup)(a); } archive_string_free(&a->archive.error_string); if (a->entry) archive_entry_free(a->entry); a->archive.magic = 0; free(a); #if ARCHIVE_API_VERSION > 1 return (r); #endif } /* * Used internally by read format handlers to register their bid and * initialization functions. */ int __archive_read_register_format(struct archive_read *a, void *format_data, const char *name, int (*bid)(struct archive_read *), int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), off_t (*read_get_entryleft)(struct archive_read *), int (*read_advance)(struct archive_read *, off_t), int (*read_data_skip)(struct archive_read *), int (*cleanup)(struct archive_read *)) { int i, number_slots; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_format"); number_slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < number_slots; i++) { if (a->formats[i].bid == bid) return (ARCHIVE_WARN); /* We've already installed */ if (a->formats[i].bid == NULL) { a->formats[i].bid = bid; a->formats[i].options = options; a->formats[i].read_header = read_header; a->formats[i].read_data = read_data; a->formats[i].read_get_entryleft = read_get_entryleft; a->formats[i].read_advance = read_advance; a->formats[i].read_data_skip = read_data_skip; a->formats[i].cleanup = cleanup; a->formats[i].data = format_data; a->formats[i].name = name; return (ARCHIVE_OK); } } __archive_errx(1, "Not enough slots for format registration"); return (ARCHIVE_FATAL); /* Never actually called. */ } /* * Used internally by decompression routines to register their bid and * initialization functions. */ struct archive_read_filter_bidder * __archive_read_get_bidder(struct archive_read *a) { int i, number_slots; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_get_bidder"); number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]); for (i = 0; i < number_slots; i++) { if (a->bidders[i].bid == NULL) { memset(a->bidders + i, 0, sizeof(a->bidders[0])); return (a->bidders + i); } } __archive_errx(1, "Not enough slots for compression registration"); return (NULL); /* Never actually executed. */ } /* * The next three functions comprise the peek/consume internal I/O * system used by archive format readers. This system allows fairly * flexible read-ahead and allows the I/O code to operate in a * zero-copy manner most of the time. * * In the ideal case, filters generate blocks of data * and __archive_read_ahead() just returns pointers directly into * those blocks. Then __archive_read_consume() just bumps those * pointers. Only if your request would span blocks does the I/O * layer use a copy buffer to provide you with a contiguous block of * data. The __archive_read_skip() is an optimization; it scans ahead * very quickly (it usually translates into a seek() operation if * you're reading uncompressed disk files). * * A couple of useful idioms: * * "I just want some data." Ask for 1 byte and pay attention to * the "number of bytes available" from __archive_read_ahead(). * You can consume more than you asked for; you just can't consume * more than is available. If you consume everything that's * immediately available, the next read_ahead() call will pull * the next block. * * "I want to output a large block of data." As above, ask for 1 byte, * emit all that's available (up to whatever limit you have), then * repeat until you're done. * * "I want to peek ahead by a large amount." Ask for 4k or so, then * double and repeat until you get an error or have enough. Note * that the I/O layer will likely end up expanding its copy buffer * to fit your request, so use this technique cautiously. This * technique is used, for example, by some of the format tasting * code that has uncertain look-ahead needs. * * TODO: Someday, provide a more generic __archive_read_seek() for * those cases where it's useful. This is tricky because there are lots * of cases where seek() is not available (reading gzip data from a * network socket, for instance), so there needs to be a good way to * communicate whether seek() is available and users of that interface * need to use non-seeking strategies whenever seek() is not available. */ /* * Looks ahead in the input stream: * * If 'avail' pointer is provided, that returns number of bytes available * in the current buffer, which may be much larger than requested. * * If end-of-file, *avail gets set to zero. * * If error, *avail gets error code. * * If request can be met, returns pointer to data, returns NULL * if request is not met. * * Note: If you just want "some data", ask for 1 byte and pay attention * to *avail, which will have the actual amount available. If you * know exactly how many bytes you need, just ask for that and treat * a NULL return as an error. * * Important: This does NOT move the file pointer. See * __archive_read_consume() below. */ /* * This is tricky. We need to provide our clients with pointers to * contiguous blocks of memory but we want to avoid copying whenever * possible. * * Mostly, this code returns pointers directly into the block of data * provided by the client's read routine. It can do this unless the * request would split across blocks. In that case, we have to copy * into an internal buffer to combine reads. */ const void * __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) { return (__archive_read_filter_ahead(a->filter, min, avail)); } const void * __archive_read_filter_ahead(struct archive_read_filter *filter, size_t min, ssize_t *avail) { ssize_t bytes_read; size_t tocopy; if (filter->fatal) { if (avail) *avail = ARCHIVE_FATAL; return (NULL); } /* * Keep pulling more data until we can satisfy the request. */ for (;;) { /* * If we can satisfy from the copy buffer, we're done. */ if (filter->avail >= min) { if (avail != NULL) *avail = filter->avail; return (filter->next); } /* * We can satisfy directly from client buffer if everything * currently in the copy buffer is still in the client buffer. */ if (filter->client_total >= filter->client_avail + filter->avail && filter->client_avail + filter->avail >= min) { /* "Roll back" to client buffer. */ filter->client_avail += filter->avail; filter->client_next -= filter->avail; /* Copy buffer is now empty. */ filter->avail = 0; filter->next = filter->buffer; /* Return data from client buffer. */ if (avail != NULL) *avail = filter->client_avail; return (filter->client_next); } /* Move data forward in copy buffer if necessary. */ if (filter->next > filter->buffer && filter->next + min > filter->buffer + filter->buffer_size) { if (filter->avail > 0) memmove(filter->buffer, filter->next, filter->avail); filter->next = filter->buffer; } /* If we've used up the client data, get more. */ if (filter->client_avail <= 0) { if (filter->end_of_file) { if (avail != NULL) *avail = 0; return (NULL); } bytes_read = (filter->read)(filter, &filter->client_buff); if (bytes_read < 0) { /* Read error. */ filter->client_total = filter->client_avail = 0; filter->client_next = filter->client_buff = NULL; filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_read == 0) { /* Premature end-of-file. */ filter->client_total = filter->client_avail = 0; filter->client_next = filter->client_buff = NULL; filter->end_of_file = 1; /* Return whatever we do have. */ if (avail != NULL) *avail = filter->avail; return (NULL); } filter->position += bytes_read; filter->client_total = bytes_read; filter->client_avail = filter->client_total; filter->client_next = filter->client_buff; } else { /* * We can't satisfy the request from the copy * buffer or the existing client data, so we * need to copy more client data over to the * copy buffer. */ /* Ensure the buffer is big enough. */ if (min > filter->buffer_size) { size_t s, t; char *p; /* Double the buffer; watch for overflow. */ s = t = filter->buffer_size; if (s == 0) s = min; while (s < min) { t *= 2; if (t <= s) { /* Integer overflow! */ archive_set_error( &filter->archive->archive, ENOMEM, "Unable to allocate copy buffer"); filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } s = t; } /* Now s >= min, so allocate a new buffer. */ p = (char *)malloc(s); if (p == NULL) { archive_set_error( &filter->archive->archive, ENOMEM, "Unable to allocate copy buffer"); filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } /* Move data into newly-enlarged buffer. */ if (filter->avail > 0) memmove(p, filter->next, filter->avail); free(filter->buffer); filter->next = filter->buffer = p; filter->buffer_size = s; } /* We can add client data to copy buffer. */ /* First estimate: copy to fill rest of buffer. */ tocopy = (filter->buffer + filter->buffer_size) - (filter->next + filter->avail); /* Don't waste time buffering more than we need to. */ if (tocopy + filter->avail > min) tocopy = min - filter->avail; /* Don't copy more than is available. */ if (tocopy > filter->client_avail) tocopy = filter->client_avail; memcpy(filter->next + filter->avail, filter->client_next, tocopy); /* Remove this data from client buffer. */ filter->client_next += tocopy; filter->client_avail -= tocopy; /* add it to copy buffer. */ filter->avail += tocopy; } } } /* * Move the file pointer forward. This should be called after * __archive_read_ahead() returns data to you. Don't try to move * ahead by more than the amount of data available according to * __archive_read_ahead(). */ /* * Mark the appropriate data as used. Note that the request here will * often be much smaller than the size of the previous read_ahead * request. */ ssize_t __archive_read_consume(struct archive_read *a, size_t request) { ssize_t r; r = __archive_read_filter_consume(a->filter, request); a->archive.file_position += r; return (r); } ssize_t __archive_read_filter_consume(struct archive_read_filter * filter, size_t request) { if (filter->avail > 0) { /* Read came from copy buffer. */ filter->next += request; filter->avail -= request; } else { /* Read came from client buffer. */ filter->client_next += request; filter->client_avail -= request; } return (request); } /* * Move the file pointer ahead by an arbitrary amount. If you're * reading uncompressed data from a disk file, this will actually * translate into a seek() operation. Even in cases where seek() * isn't feasible, this at least pushes the read-and-discard loop * down closer to the data source. */ int64_t __archive_read_skip(struct archive_read *a, int64_t request) { return (__archive_read_filter_skip(a->filter, request)); } int64_t __archive_read_filter_skip(struct archive_read_filter *filter, int64_t request) { off_t bytes_skipped, total_bytes_skipped = 0; size_t min; if (filter->fatal) return (-1); /* * If there is data in the buffers already, use that first. */ if (filter->avail > 0) { min = minimum(request, (off_t)filter->avail); bytes_skipped = __archive_read_consume(filter->archive, min); request -= bytes_skipped; total_bytes_skipped += bytes_skipped; } if (filter->client_avail > 0) { min = minimum(request, (off_t)filter->client_avail); bytes_skipped = __archive_read_consume(filter->archive, min); request -= bytes_skipped; total_bytes_skipped += bytes_skipped; } if (request == 0) return (total_bytes_skipped); /* * If a client_skipper was provided, try that first. */ #if ARCHIVE_API_VERSION < 2 if ((filter->skip != NULL) && (request < SSIZE_MAX)) { #else if (filter->skip != NULL) { #endif bytes_skipped = (filter->skip)(filter, request); if (bytes_skipped < 0) { /* error */ filter->client_total = filter->client_avail = 0; filter->client_next = filter->client_buff = NULL; filter->fatal = 1; return (bytes_skipped); } filter->archive->archive.file_position += bytes_skipped; total_bytes_skipped += bytes_skipped; request -= bytes_skipped; filter->client_next = filter->client_buff; filter->client_avail = filter->client_total = 0; } /* * Note that client_skipper will usually not satisfy the * full request (due to low-level blocking concerns), * so even if client_skipper is provided, we may still * have to use ordinary reads to finish out the request. */ while (request > 0) { ssize_t bytes_read; (void)__archive_read_ahead(filter->archive, 1, &bytes_read); if (bytes_read < 0) return (bytes_read); if (bytes_read == 0) { /* We hit EOF before we satisfied the skip request. */ archive_set_error(&filter->archive->archive, ARCHIVE_ERRNO_MISC, "Truncated input file (need to skip %jd bytes)", (intmax_t)request); return (ARCHIVE_FATAL); } min = (size_t)(minimum(bytes_read, request)); bytes_read = __archive_read_consume(filter->archive, min); total_bytes_skipped += bytes_read; request -= bytes_read; } return (total_bytes_skipped); } tarsnap-1.0.41/libarchive/archive_read_data_into_fd.c000066400000000000000000000056201476705112600226440ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_data_into_fd.c,v 1.16 2008/05/23 05:01:29 cperciva Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_private.h" /* Maximum amount of data to write at one time. */ #define MAX_WRITE (1024 * 1024) /* * This implementation minimizes copying of data and is sparse-file aware. */ int archive_read_data_into_fd(struct archive *a, int fd) { int r; const void *buff; size_t size, bytes_to_write; ssize_t bytes_written, total_written; off_t offset; off_t output_offset; __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd"); total_written = 0; output_offset = 0; while ((r = archive_read_data_block(a, &buff, &size, &offset)) == ARCHIVE_OK) { const char *p = buff; if (offset > output_offset) { output_offset = lseek(fd, offset - output_offset, SEEK_CUR); if (output_offset != offset) { archive_set_error(a, errno, "Seek error"); return (ARCHIVE_FATAL); } } while (size > 0) { bytes_to_write = size; if (bytes_to_write > MAX_WRITE) bytes_to_write = MAX_WRITE; bytes_written = write(fd, p, bytes_to_write); if (bytes_written < 0) { archive_set_error(a, errno, "Write error"); return (ARCHIVE_FATAL); } output_offset += bytes_written; total_written += bytes_written; p += bytes_written; size -= bytes_written; } } if (r != ARCHIVE_EOF) return (r); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_disk.c000066400000000000000000000135241476705112600211650ustar00rootroot00000000000000/*- * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" static int _archive_read_finish(struct archive *); static int _archive_read_close(struct archive *); static const char *trivial_lookup_gname(void *, gid_t gid); static const char *trivial_lookup_uname(void *, uid_t uid); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_finish = _archive_read_finish; av.archive_close = _archive_read_close; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, gid_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->lookup_gname != NULL) return ((*a->lookup_gname)(a->lookup_gname_data, gid)); return (NULL); } const char * archive_read_disk_uname(struct archive *_a, uid_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->lookup_uname != NULL) return ((*a->lookup_uname)(a->lookup_uname_data, uid)); return (NULL); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, gid_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, uid_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)malloc(sizeof(*a)); if (a == NULL) return (NULL); memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = archive_read_disk_vtable(); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; return (&a->archive); } static int _archive_read_finish(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); free(a); return (ARCHIVE_OK); } static int _archive_read_close(struct archive *_a) { (void)_a; /* UNUSED */ return (ARCHIVE_OK); } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; a->symlink_mode = 'L'; a->follow_symlinks = 1; return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; a->symlink_mode = 'P'; a->follow_symlinks = 0; return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; a->symlink_mode = 'H'; a->follow_symlinks = 1; /* Follow symlinks initially. */ return (ARCHIVE_OK); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, gid_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, uid_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } tarsnap-1.0.41/libarchive/archive_read_disk_entry_from_file.c000066400000000000000000000343721476705112600244340ustar00rootroot00000000000000/*- * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H /* Mac OSX requires sys/types.h before sys/acl.h. */ #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_XATTR_H #include #endif #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_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__) /* This header exists but is broken on Cygwin. */ #include #endif #ifdef HAVE_WINDOWS_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" /* * Linux and FreeBSD plug this obvious hole in POSIX.1e in * different ways. */ #if HAVE_ACL_GET_PERM #define ACL_GET_PERM acl_get_perm #elif HAVE_ACL_GET_PERM_NP #define ACL_GET_PERM acl_get_perm_np #endif static int setup_acls_posix1e(struct archive_read_disk *, struct archive_entry *, int fd); static int setup_xattrs(struct archive_read_disk *, struct archive_entry *, int fd); int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); #ifdef EXT2_IOC_GETFLAGS /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) fd = open(path, O_RDONLY | O_NONBLOCK); if (fd >= 0) { unsigned long stflags; int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) return (ARCHIVE_FATAL); } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) return (ARCHIVE_FATAL); } else #endif if (stat(path, &s) != 0) return (ARCHIVE_FATAL); st = &s; } archive_entry_copy_stat(entry, st); /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if (st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #ifdef HAVE_READLINK if (S_ISLNK(st->st_mode)) { char linkbuffer[PATH_MAX + 1]; int lnklen = readlink(path, linkbuffer, PATH_MAX); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); return (ARCHIVE_WARN); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); } #endif r = setup_acls_posix1e(a, entry, fd); r1 = setup_xattrs(a, entry, fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); } #ifdef HAVE_POSIX_ACL static void setup_acl_posix1e(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); static int setup_acls_posix1e(struct archive_read_disk *a, struct archive_entry *entry, int fd) { const char *accpath; acl_t acl; accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); archive_entry_acl_clear(entry); /* Retrieve access ACL from file. */ if (fd >= 0) acl = acl_get_fd(fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); #endif else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); if (acl != NULL) { setup_acl_posix1e(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); } /* Only directories can have default ACLs. */ if (S_ISDIR(archive_entry_mode(entry))) { acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { setup_acl_posix1e(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); } } return (ARCHIVE_OK); } /* * Translate POSIX.1e ACL into libarchive internal structure. */ static void setup_acl_posix1e(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int archive_entry_acl_type) { acl_tag_t acl_tag; acl_entry_t acl_entry; acl_permset_t acl_permset; int s, ae_id, ae_tag, ae_perm; const char *ae_name; s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); while (s == 1) { ae_id = -1; ae_name = NULL; acl_get_tag_type(acl_entry, &acl_tag); if (acl_tag == ACL_USER) { ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_uname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_USER; } else if (acl_tag == ACL_GROUP) { ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_gname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_GROUP; } else if (acl_tag == ACL_MASK) { ae_tag = ARCHIVE_ENTRY_ACL_MASK; } else if (acl_tag == ACL_USER_OBJ) { ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; } else if (acl_tag == ACL_GROUP_OBJ) { ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; } else if (acl_tag == ACL_OTHER) { ae_tag = ARCHIVE_ENTRY_ACL_OTHER; } else { /* Skip types that libarchive can't support. */ continue; } acl_get_permset(acl_entry, &acl_permset); ae_perm = 0; /* * acl_get_perm() is spelled differently on different * platforms; see above. */ if (ACL_GET_PERM(acl_permset, ACL_EXECUTE)) ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; if (ACL_GET_PERM(acl_permset, ACL_READ)) ae_perm |= ARCHIVE_ENTRY_ACL_READ; if (ACL_GET_PERM(acl_permset, ACL_WRITE)) ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; archive_entry_acl_add_entry(entry, archive_entry_acl_type, ae_perm, ae_tag, ae_id, ae_name); s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); } } #else static int setup_acls_posix1e(struct archive_read_disk *a, struct archive_entry *entry, int fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR /* * Linux extended attribute support. * * TODO: By using a stack-allocated buffer for the first * call to getxattr(), we might be able to avoid the second * call entirely. We only need the second call if the * stack-allocated buffer is too small. But a modest buffer * of 1024 bytes or so will often be big enough. Same applies * to listxattr(). */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, const char *name, int fd) { ssize_t size; void *value = NULL; const char *accpath; (void)fd; /* UNUSED */ accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); if (!a->follow_symlinks) size = lgetxattr(accpath, name, NULL, 0); else size = getxattr(accpath, name, NULL, 0); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (!a->follow_symlinks) size = lgetxattr(accpath, name, value, size); else size = getxattr(accpath, name, value, size); if (size == -1) { free(value); archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, name, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int fd) { char *list, *p; const char *path; ssize_t list_size; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (!a->follow_symlinks) list_size = llistxattr(path, NULL, 0); else list_size = listxattr(path, NULL, 0); if (list_size == -1) { if (errno == ENOTSUP) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (!a->follow_symlinks) list_size = llistxattr(path, list, list_size); else list_size = listxattr(path, list, list_size); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } for (p = list; (p - list) < list_size; p += strlen(p) + 1) { if (strncmp(p, "system.", 7) == 0 || strncmp(p, "xfsroot.", 8) == 0) continue; setup_xattr(a, entry, p, fd); } free(list); return (ARCHIVE_OK); } #elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE /* * FreeBSD extattr interface. */ /* TODO: Implement this. Follow the Linux model above, but * with FreeBSD-specific system calls, of course. Be careful * to not include the system extattrs that hold ACLs; we handle * those separately. */ int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd); int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd) { ssize_t size; void *value = NULL; const char *accpath; (void)fd; /* UNUSED */ accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, NULL, 0); else size = extattr_get_file(accpath, namespace, name, NULL, 0); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, value, size); else size = extattr_get_file(accpath, namespace, name, value, size); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, fullname, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int fd) { char buff[512]; char *list, *p; ssize_t list_size; const char *path; int namespace = EXTATTR_NAMESPACE_USER; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, NULL, 0); else list_size = extattr_list_file(path, namespace, NULL, 0); if (list_size == -1 && errno == EOPNOTSUPP) return (ARCHIVE_OK); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, list, list_size); else list_size = extattr_list_file(path, namespace, list, list_size); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } p = list; while ((p - list) < list_size) { size_t len = 255 & (int)*p; char *name; strcpy(buff, "user."); name = buff + strlen(buff); memcpy(name, p + 1, len); name[len] = '\0'; setup_xattr(a, entry, namespace, name, buff, fd); p += 1 + len; } free(list); return (ARCHIVE_OK); } #else /* * Generic (stub) extended attribute support. */ static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif tarsnap-1.0.41/libarchive/archive_read_disk_private.h000066400000000000000000000043401476705112600227200ustar00rootroot00000000000000/*- * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED #define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED struct archive_read_disk { struct archive archive; /* * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid, * following an old BSD convention. 'L' follows all symlinks, * 'P' follows none, 'H' follows symlinks only for the first * item. */ char symlink_mode; /* * Since symlink interaction changes, we need to track whether * we're following symlinks for the current item. 'L' mode above * sets this true, 'P' sets it false, 'H' changes it as we traverse. */ char follow_symlinks; /* Either 'L' or 'P'. */ const char * (*lookup_gname)(void *private, gid_t gid); void (*cleanup_gname)(void *private); void *lookup_gname_data; const char * (*lookup_uname)(void *private, gid_t gid); void (*cleanup_uname)(void *private); void *lookup_uname_data; }; #endif tarsnap-1.0.41/libarchive/archive_read_disk_set_standard_lookup.c000066400000000000000000000166061476705112600253150ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #if defined(_WIN32) && !defined(__CYGWIN__) int archive_read_disk_set_standard_lookup(struct archive *a) { archive_set_error(a, -1, "Standard lookups not available on Windows"); return (ARCHIVE_FATAL); } #else /* ! (_WIN32 && !__CYGWIN__) */ #define name_cache_size 127 static const char * const NO_NAME = "(noname)"; struct name_cache { struct archive *archive; char *buff; size_t buff_size; int probes; int hits; size_t size; struct { id_t id; const char *name; } cache[name_cache_size]; }; static const char * lookup_gname(void *, gid_t); static const char * lookup_uname(void *, uid_t); static void cleanup(void *); static const char * lookup_gname_helper(struct name_cache *, id_t gid); static const char * lookup_uname_helper(struct name_cache *, id_t uid); /* * Installs functions that use getpwuid()/getgrgid()---along with * a simple cache to accelerate such lookups---into the archive_read_disk * object. This is in a separate file because getpwuid()/getgrgid() * can pull in a LOT of library code (including NIS/LDAP functions, which * pull in DNS resolvers, etc). This can easily top 500kB, which makes * it inappropriate for some space-constrained applications. * * Applications that are size-sensitive may want to just use the * real default functions (defined in archive_read_disk.c) that just * use the uid/gid without the lookup. Or define your own custom functions * if you prefer. */ int archive_read_disk_set_standard_lookup(struct archive *a) { struct name_cache *ucache = malloc(sizeof(struct name_cache)); struct name_cache *gcache = malloc(sizeof(struct name_cache)); if (ucache == NULL || gcache == NULL) { archive_set_error(a, ENOMEM, "Can't allocate uname/gname lookup cache"); free(ucache); free(gcache); return (ARCHIVE_FATAL); } memset(ucache, 0, sizeof(*ucache)); ucache->archive = a; ucache->size = name_cache_size; memset(gcache, 0, sizeof(*gcache)); gcache->archive = a; gcache->size = name_cache_size; archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup); archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup); return (ARCHIVE_OK); } static void cleanup(void *data) { struct name_cache *cache = (struct name_cache *)data; size_t i; if (cache != NULL) { for (i = 0; i < cache->size; i++) { if (cache->cache[i].name != NULL && cache->cache[i].name != NO_NAME) free((void *)(uintptr_t)cache->cache[i].name); } free(cache->buff); free(cache); } } /* * Lookup uid/gid from uname/gname, return NULL if no match. */ static const char * lookup_name(struct name_cache *cache, const char * (*lookup_fn)(struct name_cache *, id_t), id_t id) { const char *name; int slot; cache->probes++; slot = id % cache->size; if (cache->cache[slot].name != NULL) { if (cache->cache[slot].id == id) { cache->hits++; if (cache->cache[slot].name == NO_NAME) return (NULL); return (cache->cache[slot].name); } if (cache->cache[slot].name != NO_NAME) free((void *)(uintptr_t)cache->cache[slot].name); cache->cache[slot].name = NULL; } name = (lookup_fn)(cache, id); if (name == NULL) { /* Cache and return the negative response. */ cache->cache[slot].name = NO_NAME; cache->cache[slot].id = id; return (NULL); } cache->cache[slot].name = name; cache->cache[slot].id = id; return (cache->cache[slot].name); } static const char * lookup_uname(void *data, uid_t uid) { struct name_cache *uname_cache = (struct name_cache *)data; return (lookup_name(uname_cache, &lookup_uname_helper, (id_t)uid)); } static const char * lookup_uname_helper(struct name_cache *cache, id_t id) { struct passwd pwent, *result; char * nbuff; size_t nbuff_size; int r; if (cache->buff_size == 0) { cache->buff_size = 256; cache->buff = malloc(cache->buff_size); } if (cache->buff == NULL) return (NULL); for (;;) { r = getpwuid_r((uid_t)id, &pwent, cache->buff, cache->buff_size, &result); if (r == 0) break; if (r != ERANGE) break; /* ERANGE means our buffer was too small, but POSIX * doesn't tell us how big the buffer should be, so * we just double it and try again. Because the buffer * is kept around in the cache object, we shouldn't * have to do this very often. */ nbuff_size = cache->buff_size * 2; nbuff = realloc(cache->buff, nbuff_size); if (nbuff == NULL) break; cache->buff = nbuff; cache->buff_size = nbuff_size; } if (r != 0) { archive_set_error(cache->archive, errno, "Can't lookup user for id %d", (int)id); return (NULL); } if (result == NULL) return (NULL); return strdup(result->pw_name); } static const char * lookup_gname(void *data, gid_t gid) { struct name_cache *gname_cache = (struct name_cache *)data; return (lookup_name(gname_cache, &lookup_gname_helper, (id_t)gid)); } static const char * lookup_gname_helper(struct name_cache *cache, id_t id) { struct group grent, *result; char * nbuff; size_t nbuff_size; int r; if (cache->buff_size == 0) { cache->buff_size = 256; cache->buff = malloc(cache->buff_size); } if (cache->buff == NULL) return (NULL); for (;;) { r = getgrgid_r((gid_t)id, &grent, cache->buff, cache->buff_size, &result); if (r == 0) break; if (r != ERANGE) break; /* ERANGE means our buffer was too small, but POSIX * doesn't tell us how big the buffer should be, so * we just double it and try again. */ nbuff_size = cache->buff_size * 2; nbuff = realloc(cache->buff, nbuff_size); if (nbuff == NULL) break; cache->buff = nbuff; cache->buff_size = nbuff_size; } if (r != 0) { archive_set_error(cache->archive, errno, "Can't lookup group for id %d", (int)id); return (NULL); } if (result == NULL) return (NULL); return strdup(result->gr_name); } #endif /* ! (_WIN32 && !__CYGWIN__) */ tarsnap-1.0.41/libarchive/archive_read_extract.c000066400000000000000000000126541476705112600217100ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_write_disk_private.h" struct extract { struct archive *ad; /* archive_write_disk object */ /* Progress function invoked during extract. */ void (*extract_progress)(void *); void *extract_progress_user_data; }; static int archive_read_extract_cleanup(struct archive_read *); static int copy_data(struct archive *ar, struct archive *aw); static struct extract *get_extract(struct archive_read *); static struct extract * get_extract(struct archive_read *a) { /* If we haven't initialized, do it now. */ /* This also sets up a lot of global state. */ if (a->extract == NULL) { a->extract = (struct extract *)malloc(sizeof(*a->extract)); if (a->extract == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't extract"); return (NULL); } memset(a->extract, 0, sizeof(*a->extract)); a->extract->ad = archive_write_disk_new(); if (a->extract->ad == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't extract"); return (NULL); } if (archive_write_disk_set_standard_lookup(a->extract->ad)) { archive_set_error(&a->archive, ENOMEM, "Can't extract"); return (NULL); } a->cleanup_archive_extract = archive_read_extract_cleanup; } return (a->extract); } int archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags) { struct extract *extract; extract = get_extract((struct archive_read *)_a); if (extract == NULL) return (ARCHIVE_FATAL); archive_write_disk_set_options(extract->ad, flags); return (archive_read_extract2(_a, entry, extract->ad)); } int archive_read_extract2(struct archive *_a, struct archive_entry *entry, struct archive *ad) { struct archive_read *a = (struct archive_read *)_a; int r, r2; /* Set up for this particular entry. */ archive_write_disk_set_skip_file(ad, a->skip_file_dev, a->skip_file_ino); r = archive_write_header(ad, entry); if (r < ARCHIVE_WARN) r = ARCHIVE_WARN; if (r != ARCHIVE_OK) /* If _write_header failed, copy the error. */ archive_copy_error(&a->archive, ad); else if (archive_entry_size(entry) > 0) /* Otherwise, pour data into the entry. */ r = copy_data(_a, ad); r2 = archive_write_finish_entry(ad); if (r2 < ARCHIVE_WARN) r2 = ARCHIVE_WARN; /* Use the first message. */ if (r2 != ARCHIVE_OK && r == ARCHIVE_OK) archive_copy_error(&a->archive, ad); /* Use the worst error return. */ if (r2 < r) r = r2; return (r); } void archive_read_extract_set_progress_callback(struct archive *_a, void (*progress_func)(void *), void *user_data) { struct archive_read *a = (struct archive_read *)_a; struct extract *extract = get_extract(a); if (extract != NULL) { extract->extract_progress = progress_func; extract->extract_progress_user_data = user_data; } } static int copy_data(struct archive *ar, struct archive *aw) { off_t offset; const void *buff; struct extract *extract; size_t size; int r; extract = get_extract((struct archive_read *)ar); if (extract == NULL) return (ARCHIVE_FATAL); for (;;) { r = archive_read_data_block(ar, &buff, &size, &offset); if (r == ARCHIVE_EOF) return (ARCHIVE_OK); if (r != ARCHIVE_OK) return (r); r = archive_write_data_block(aw, buff, size, offset); if (r < ARCHIVE_WARN) r = ARCHIVE_WARN; if (r != ARCHIVE_OK) { archive_set_error(ar, archive_errno(aw), "%s", archive_error_string(aw)); return (r); } if (extract->extract_progress) (extract->extract_progress) (extract->extract_progress_user_data); } } /* * Cleanup function for archive_extract. */ static int archive_read_extract_cleanup(struct archive_read *a) { int ret = ARCHIVE_OK; #if ARCHIVE_API_VERSION > 1 ret = #endif archive_write_finish(a->extract->ad); free(a->extract); a->extract = NULL; return (ret); } tarsnap-1.0.41/libarchive/archive_read_open_fd.c000066400000000000000000000120311476705112600216350ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_open_fd.c,v 1.13 2007/06/26 03:06:48 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct read_fd_data { int fd; size_t block_size; char can_skip; void *buffer; }; static int file_close(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *, void *, size_t request); #else static off_t file_skip(struct archive *, void *, off_t request); #endif int archive_read_open_fd(struct archive *a, int fd, size_t block_size) { struct stat st; struct read_fd_data *mine; void *b; if (fstat(fd, &st) != 0) { archive_set_error(a, errno, "Can't stat fd %d", fd); return (ARCHIVE_FATAL); } mine = (struct read_fd_data *)malloc(sizeof(*mine)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } mine->block_size = block_size; mine->buffer = b; mine->fd = fd; /* * Skip support is a performance optimization for anything * that supports lseek(). On FreeBSD, only regular files and * raw disk devices support lseek() and there's no portable * way to determine if a device is a raw disk device, so we * only enable this optimization for regular files. */ if (S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); mine->can_skip = 1; } else mine->can_skip = 0; return (archive_read_open2(a, mine, NULL, file_read, file_skip, file_close)); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { struct read_fd_data *mine = (struct read_fd_data *)client_data; ssize_t bytes_read; *buff = mine->buffer; bytes_read = read(mine->fd, mine->buffer, mine->block_size); if (bytes_read < 0) { archive_set_error(a, errno, "Error reading fd %d", mine->fd); } return (bytes_read); } #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *a, void *client_data, size_t request) #else static off_t file_skip(struct archive *a, void *client_data, off_t request) #endif { struct read_fd_data *mine = (struct read_fd_data *)client_data; off_t old_offset, new_offset; if (!mine->can_skip) return (0); /* Reduce request to the next smallest multiple of block_size */ request = (request / mine->block_size) * mine->block_size; if (request == 0) return (0); /* * Hurray for lazy evaluation: if the first lseek fails, the second * one will not be executed. */ if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) || ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0)) { /* If seek failed once, it will probably fail again. */ mine->can_skip = 0; if (errno == ESPIPE) { /* * Failure to lseek() can be caused by the file * descriptor pointing to a pipe, socket or FIFO. * Return 0 here, so the compression layer will use * read()s instead to advance the file descriptor. * It's slower of course, but works as well. */ return (0); } /* * There's been an error other than ESPIPE. This is most * likely caused by a programmer error (too large request) * or a corrupted archive file. */ archive_set_error(a, errno, "Error seeking"); return (-1); } return (new_offset - old_offset); } static int file_close(struct archive *a, void *client_data) { struct read_fd_data *mine = (struct read_fd_data *)client_data; (void)a; /* UNUSED */ free(mine->buffer); free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_open_file.c000066400000000000000000000105061476705112600221700ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_open_file.c,v 1.20 2007/06/26 03:06:48 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct read_FILE_data { FILE *f; size_t block_size; void *buffer; char can_skip; }; static int file_close(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *, void *, size_t request); #else static off_t file_skip(struct archive *, void *, off_t request); #endif int archive_read_open_FILE(struct archive *a, FILE *f) { struct stat st; struct read_FILE_data *mine; size_t block_size = 128 * 1024; void *b; mine = (struct read_FILE_data *)malloc(sizeof(*mine)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } mine->block_size = block_size; mine->buffer = b; mine->f = f; /* * If we can't fstat() the file, it may just be that it's not * a file. (FILE * objects can wrap many kinds of I/O * streams, some of which don't support fileno()).) */ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* Enable the seek optimization only for regular files. */ mine->can_skip = 1; } else mine->can_skip = 0; return (archive_read_open2(a, mine, NULL, file_read, file_skip, file_close)); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; size_t bytes_read; *buff = mine->buffer; bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); if (bytes_read < mine->block_size && ferror(mine->f)) { archive_set_error(a, errno, "Error reading file"); } return (bytes_read); } #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *a, void *client_data, size_t request) #else static off_t file_skip(struct archive *a, void *client_data, off_t request) #endif { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ /* * If we can't skip, return 0 as the amount we did step and * the caller will work around by reading and discarding. */ if (!mine->can_skip) return (0); if (request == 0) return (0); #if HAVE_FSEEKO if (fseeko(mine->f, request, SEEK_CUR) != 0) #else if (fseek(mine->f, request, SEEK_CUR) != 0) #endif { mine->can_skip = 0; return (0); } return (request); } static int file_close(struct archive *a, void *client_data) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ if (mine->buffer != NULL) free(mine->buffer); free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_open_filename.c000066400000000000000000000153401476705112600230320ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_open_filename.c,v 1.21 2008/02/19 06:10:48 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct read_file_data { int fd; size_t block_size; void *buffer; mode_t st_mode; /* Mode bits for opened file. */ char can_skip; /* This file supports skipping. */ char filename[1]; /* Must be last! */ }; static int file_close(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *, void *, size_t request); #else static off_t file_skip(struct archive *, void *, off_t request); #endif int archive_read_open_file(struct archive *a, const char *filename, size_t block_size) { return (archive_read_open_filename(a, filename, block_size)); } int archive_read_open_filename(struct archive *a, const char *filename, size_t block_size) { struct stat st; struct read_file_data *mine; void *b; int fd; if (filename == NULL || filename[0] == '\0') return (archive_read_open_fd(a, 0, block_size)); fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) { archive_set_error(a, errno, "Failed to open '%s'", filename); return (ARCHIVE_FATAL); } if (fstat(fd, &st) != 0) { archive_set_error(a, errno, "Can't stat '%s'", filename); close(fd); return (ARCHIVE_FATAL); } mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); close(fd); free(mine); free(b); return (ARCHIVE_FATAL); } strcpy(mine->filename, filename); mine->block_size = block_size; mine->buffer = b; mine->fd = fd; /* Remember mode so close can decide whether to flush. */ mine->st_mode = st.st_mode; /* If we're reading a file from disk, ensure that we don't overwrite it with an extracted file. */ if (S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* * Skip is a performance optimization for anything * that supports lseek(). Generally, that only * includes regular files and possibly raw disk * devices, but there's no good portable way to detect * raw disks. */ mine->can_skip = 1; } else mine->can_skip = 0; return (archive_read_open2(a, mine, NULL, file_read, file_skip, file_close)); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { struct read_file_data *mine = (struct read_file_data *)client_data; ssize_t bytes_read; *buff = mine->buffer; bytes_read = read(mine->fd, mine->buffer, mine->block_size); if (bytes_read < 0) { archive_set_error(a, errno, "Error reading '%s'", mine->filename); } return (bytes_read); } #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *a, void *client_data, size_t request) #else static off_t file_skip(struct archive *a, void *client_data, off_t request) #endif { struct read_file_data *mine = (struct read_file_data *)client_data; off_t old_offset, new_offset; if (!mine->can_skip) /* We can't skip, so ... */ return (0); /* ... skip zero bytes. */ /* Reduce request to the next smallest multiple of block_size */ request = (request / mine->block_size) * mine->block_size; if (request == 0) return (0); /* * Hurray for lazy evaluation: if the first lseek fails, the second * one will not be executed. */ if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) || ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0)) { /* If skip failed once, it will probably fail again. */ mine->can_skip = 0; if (errno == ESPIPE) { /* * Failure to lseek() can be caused by the file * descriptor pointing to a pipe, socket or FIFO. * Return 0 here, so the compression layer will use * read()s instead to advance the file descriptor. * It's slower of course, but works as well. */ return (0); } /* * There's been an error other than ESPIPE. This is most * likely caused by a programmer error (too large request) * or a corrupted archive file. */ archive_set_error(a, errno, "Error seeking in '%s'", mine->filename); return (-1); } return (new_offset - old_offset); } static int file_close(struct archive *a, void *client_data) { struct read_file_data *mine = (struct read_file_data *)client_data; (void)a; /* UNUSED */ /* Only flush and close if open succeeded. */ if (mine->fd >= 0) { /* * Sometimes, we should flush the input before closing. * Regular files: faster to just close without flush. * Devices: must not flush (user might need to * read the "next" item on a non-rewind device). * Pipes and sockets: must flush (otherwise, the * program feeding the pipe or socket may complain). * Here, I flush everything except for regular files and * device nodes. */ if (!S_ISREG(mine->st_mode) && !S_ISCHR(mine->st_mode) && !S_ISBLK(mine->st_mode)) { ssize_t bytesRead; do { bytesRead = read(mine->fd, mine->buffer, mine->block_size); } while (bytesRead > 0); } close(mine->fd); } free(mine->buffer); free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_open_memory.c000066400000000000000000000115001476705112600225540ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $"); #include #include #include #include "archive.h" /* * Glue to read an archive from a block of memory. * * This is mostly a huge help in building test harnesses; * test programs can build archives in memory and read them * back again without having to mess with files on disk. */ struct read_memory_data { unsigned char *buffer; unsigned char *end; ssize_t read_size; }; static int memory_read_close(struct archive *, void *); static int memory_read_open(struct archive *, void *); #if ARCHIVE_API_VERSION < 2 static ssize_t memory_read_skip(struct archive *, void *, size_t request); #else static off_t memory_read_skip(struct archive *, void *, off_t request); #endif static ssize_t memory_read(struct archive *, void *, const void **buff); int archive_read_open_memory(struct archive *a, void *buff, size_t size) { return archive_read_open_memory2(a, buff, size, size); } /* * Don't use _open_memory2() in production code; the archive_read_open_memory() * version is the one you really want. This is just here so that * test harnesses can exercise block operations inside the library. */ int archive_read_open_memory2(struct archive *a, void *buff, size_t size, size_t read_size) { struct read_memory_data *mine; mine = (struct read_memory_data *)malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } memset(mine, 0, sizeof(*mine)); mine->buffer = (unsigned char *)buff; mine->end = mine->buffer + size; mine->read_size = read_size; return (archive_read_open2(a, mine, memory_read_open, memory_read, memory_read_skip, memory_read_close)); } /* * There's nothing to open. */ static int memory_read_open(struct archive *a, void *client_data) { (void)a; /* UNUSED */ (void)client_data; /* UNUSED */ return (ARCHIVE_OK); } /* * This is scary simple: Just advance a pointer. Limiting * to read_size is not technically necessary, but it exercises * more of the internal logic when used with a small block size * in a test harness. Production use should not specify a block * size; then this is much faster. */ static ssize_t memory_read(struct archive *a, void *client_data, const void **buff) { struct read_memory_data *mine = (struct read_memory_data *)client_data; ssize_t size; (void)a; /* UNUSED */ *buff = mine->buffer; size = mine->end - mine->buffer; if (size > mine->read_size) size = mine->read_size; mine->buffer += size; return (size); } /* * Advancing is just as simple. Again, this is doing more than * necessary in order to better exercise internal code when used * as a test harness. */ #if ARCHIVE_API_VERSION < 2 static ssize_t memory_read_skip(struct archive *a, void *client_data, size_t skip) #else static off_t memory_read_skip(struct archive *a, void *client_data, off_t skip) #endif { struct read_memory_data *mine = (struct read_memory_data *)client_data; (void)a; /* UNUSED */ if ((off_t)skip > (off_t)(mine->end - mine->buffer)) skip = mine->end - mine->buffer; /* Round down to block size. */ skip /= mine->read_size; skip *= mine->read_size; mine->buffer += skip; return (skip); } /* * Close is just cleaning up our one small bit of data. */ static int memory_read_close(struct archive *a, void *client_data) { struct read_memory_data *mine = (struct read_memory_data *)client_data; (void)a; /* UNUSED */ free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_private.h000066400000000000000000000162451476705112600217150ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.7 2008/12/06 06:45:15 kientzle Exp $ */ #ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED #define ARCHIVE_READ_PRIVATE_H_INCLUDED #include "archive.h" #include "archive_string.h" #include "archive_private.h" struct archive_read; struct archive_read_filter_bidder; struct archive_read_filter; /* * How bidding works for filters: * * The bid manager reads the first block from the current source. * * It shows that block to each registered bidder. * * The bid manager creates a new filter structure for the winning * bidder and gives the winning bidder a chance to initialize it. * * The new filter becomes the top filter in the archive_read structure * and we repeat the process. * This ends only when no bidder provides a non-zero bid. */ struct archive_read_filter_bidder { /* Configuration data for the bidder. */ void *data; /* Taste the upstream filter to see if we handle this. */ int (*bid)(struct archive_read_filter_bidder *, struct archive_read_filter *); /* Initialize a newly-created filter. */ int (*init)(struct archive_read_filter *); /* Set an option for the filter bidder. */ int (*options)(struct archive_read_filter_bidder *, const char *key, const char *value); /* Release the bidder's configuration data. */ int (*free)(struct archive_read_filter_bidder *); }; /* * This structure is allocated within the archive_read core * and initialized by archive_read and the init() method of the * corresponding bidder above. */ struct archive_read_filter { /* Essentially all filters will need these values, so * just declare them here. */ struct archive_read_filter_bidder *bidder; /* My bidder. */ struct archive_read_filter *upstream; /* Who I read from. */ struct archive_read *archive; /* Associated archive. */ /* Return next block. */ ssize_t (*read)(struct archive_read_filter *, const void **); /* Skip forward this many bytes. */ int64_t (*skip)(struct archive_read_filter *self, int64_t request); /* Close (just this filter) and free(self). */ int (*close)(struct archive_read_filter *self); /* My private data. */ void *data; const char *name; int code; /* Used by reblocking logic. */ char *buffer; size_t buffer_size; char *next; /* Current read location. */ size_t avail; /* Bytes in my buffer. */ const void *client_buff; /* Client buffer information. */ size_t client_total; const char *client_next; size_t client_avail; int64_t position; char end_of_file; char fatal; }; /* * The client looks a lot like a filter, so we just wrap it here. * * TODO: Make archive_read_filter and archive_read_client identical so * that users of the library can easily register their own * transformation filters. This will probably break the API/ABI and * so should be deferred at least until libarchive 3.0. */ struct archive_read_client { archive_read_callback *reader; archive_skip_callback *skipper; archive_close_callback *closer; }; struct archive_read { struct archive archive; struct archive_entry *entry; /* Dev/ino of the archive being read/written. */ dev_t skip_file_dev; ino_t skip_file_ino; /* * Used by archive_read_data() to track blocks and copy * data to client buffers, filling gaps with zero bytes. */ const char *read_data_block; off_t read_data_offset; off_t read_data_output_offset; size_t read_data_remaining; /* Callbacks to open/read/write/close client archive stream. */ struct archive_read_client client; /* Registered filter bidders. */ struct archive_read_filter_bidder bidders[8]; /* Last filter in chain */ struct archive_read_filter *filter; /* File offset of beginning of most recently-read header. */ off_t header_position; /* * Format detection is mostly the same as compression * detection, with one significant difference: The bidders * use the read_ahead calls above to examine the stream rather * than having the supervisor hand them a block of data to * examine. */ struct archive_format_descriptor { void *data; const char *name; int (*bid)(struct archive_read *); int (*options)(struct archive_read *, const char *key, const char *value); int (*read_header)(struct archive_read *, struct archive_entry *); int (*read_data)(struct archive_read *, const void **, size_t *, off_t *); off_t (*read_get_entryleft)(struct archive_read *); int (*read_advance)(struct archive_read *, off_t); int (*read_data_skip)(struct archive_read *); int (*cleanup)(struct archive_read *); } formats[8]; struct archive_format_descriptor *format; /* Active format. */ /* * Various information needed by archive_extract. */ struct extract *extract; int (*cleanup_archive_extract)(struct archive_read *); }; int __archive_read_register_format(struct archive_read *a, void *format_data, const char *name, int (*bid)(struct archive_read *), int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), off_t (*read_get_entryleft)(struct archive_read *), int (*read_advance)(struct archive_read *, off_t), int (*read_data_skip)(struct archive_read *), int (*cleanup)(struct archive_read *)); struct archive_read_filter_bidder *__archive_read_get_bidder(struct archive_read *a); const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *); const void *__archive_read_filter_ahead(struct archive_read_filter *, size_t, ssize_t *); ssize_t __archive_read_consume(struct archive_read *, size_t); ssize_t __archive_read_filter_consume(struct archive_read_filter *, size_t); int64_t __archive_read_skip(struct archive_read *, int64_t); int64_t __archive_read_filter_skip(struct archive_read_filter *, int64_t); int __archive_read_program(struct archive_read_filter *, const char *); #endif tarsnap-1.0.41/libarchive/archive_read_support_compression_all.c000066400000000000000000000050741476705112600252210ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_compression_all.c,v 1.7 2008/12/06 06:45:15 kientzle Exp $"); #include "archive.h" int archive_read_support_compression_all(struct archive *a) { /* Bzip falls back to "bunzip2" command-line */ archive_read_support_compression_bzip2(a); /* The decompress code doesn't use an outside library. */ archive_read_support_compression_compress(a); /* Gzip decompress falls back to "gunzip" command-line. */ archive_read_support_compression_gzip(a); /* The LZMA file format has a very weak signature, so it * may not be feasible to keep this here, but we'll try. * This will come back out if there are problems. */ /* Lzma falls back to "unlzma" command-line program. */ archive_read_support_compression_lzma(a); /* Xz falls back to "unxz" command-line program. */ archive_read_support_compression_xz(a); /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable * "as much as possible." Clients who need specific * compression should enable those individually so they can * verify the level of support. */ /* Clear any warning messages set by the above functions. */ archive_clear_error(a); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_support_compression_bzip2.c000066400000000000000000000230051476705112600254710ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.19 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #if HAVE_BZLIB_H struct private_data { bz_stream stream; char *out_block; size_t out_block_size; char valid; /* True = decompressor is initialized */ char eof; /* True = found end of compressed data. */ }; /* Bzip2 filter */ static ssize_t bzip2_filter_read(struct archive_read_filter *, const void **); static int bzip2_filter_close(struct archive_read_filter *); #endif /* * Note that we can detect bzip2 archives even if we can't decompress * them. (In fact, we like detecting them because we can give better * error messages.) So the bid framework here gets compiled even * if bzlib is unavailable. */ static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int bzip2_reader_init(struct archive_read_filter *); static int bzip2_reader_free(struct archive_read_filter_bidder *); int archive_read_support_compression_bzip2(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *reader = __archive_read_get_bidder(a); if (reader == NULL) return (ARCHIVE_FATAL); reader->data = NULL; reader->bid = bzip2_reader_bid; reader->init = bzip2_reader_init; reader->options = NULL; reader->free = bzip2_reader_free; #if HAVE_BZLIB_H return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external bunzip2 program"); return (ARCHIVE_WARN); #endif } static int bzip2_reader_free(struct archive_read_filter_bidder *self){ (void)self; /* UNUSED */ return (ARCHIVE_OK); } /* * Test whether we can handle this data. * * This logic returns zero if any part of the signature fails. It * also tries to Do The Right Thing if a very short buffer prevents us * from verifying as much as we would like. */ static int bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; int bits_checked; (void)self; /* UNUSED */ /* Minimal bzip2 archive is 14 bytes. */ buffer = __archive_read_filter_ahead(filter, 14, &avail); if (buffer == NULL) return (0); /* First three bytes must be "BZh" */ bits_checked = 0; if (buffer[0] != 'B' || buffer[1] != 'Z' || buffer[2] != 'h') return (0); bits_checked += 24; /* Next follows a compression flag which must be an ASCII digit. */ if (buffer[3] < '1' || buffer[3] > '9') return (0); bits_checked += 5; /* After BZh[1-9], there must be either a data block * which begins with 0x314159265359 or an end-of-data * marker of 0x177245385090. */ if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0) bits_checked += 48; else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0) bits_checked += 48; else return (0); return (bits_checked); } #ifndef HAVE_BZLIB_H /* * If we don't have the library on this system, we can't actually do the * decompression. We can, however, still detect compressed archives * and emit a useful message. */ static int bzip2_reader_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "bunzip2"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_COMPRESSION_BZIP2; self->name = "bzip2"; return (r); } #else /* * Setup the callbacks. */ static int bzip2_reader_init(struct archive_read_filter *self) { static const size_t out_block_size = 64 * 1024; void *out_block; struct private_data *state; self->code = ARCHIVE_COMPRESSION_BZIP2; self->name = "bzip2"; state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for bzip2 decompression"); free(out_block); free(state); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; self->read = bzip2_filter_read; self->skip = NULL; /* not supported */ self->close = bzip2_filter_close; return (ARCHIVE_OK); } /* * Return the next block of decompressed data. */ static ssize_t bzip2_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; const char *read_buf; ssize_t ret; state = (struct private_data *)self->data; if (state->eof) { *p = NULL; return (0); } /* Empty our output buffer. */ state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Try to fill the output buffer. */ for (;;) { if (!state->valid) { if (bzip2_reader_bid(self->bidder, self->upstream) == 0) { state->eof = 1; *p = state->out_block; decompressed = state->stream.next_out - state->out_block; return (decompressed); } /* Initialize compression library. */ ret = BZ2_bzDecompressInit(&(state->stream), 0 /* library verbosity */, 0 /* don't use low-mem algorithm */); /* If init fails, try low-memory algorithm instead. */ if (ret == BZ_MEM_ERROR) ret = BZ2_bzDecompressInit(&(state->stream), 0 /* library verbosity */, 1 /* do use low-mem algo */); if (ret != BZ_OK) { const char *detail = NULL; int err = ARCHIVE_ERRNO_MISC; switch (ret) { case BZ_PARAM_ERROR: detail = "invalid setup parameter"; break; case BZ_MEM_ERROR: err = ENOMEM; detail = "out of memory"; break; case BZ_CONFIG_ERROR: detail = "mis-compiled library"; break; } archive_set_error(&self->archive->archive, err, "Internal error initializing decompressor%s%s", detail == NULL ? "" : ": ", detail); return (ARCHIVE_FATAL); } state->valid = 1; } /* stream.next_in is really const, but bzlib * doesn't declare it so. */ read_buf = __archive_read_filter_ahead(self->upstream, 1, &ret); if (read_buf == NULL) return (ARCHIVE_FATAL); state->stream.next_in = (char *)(uintptr_t)read_buf; state->stream.avail_in = ret; /* There is no more data, return whatever we have. */ if (ret == 0) { state->eof = 1; *p = state->out_block; decompressed = state->stream.next_out - state->out_block; return (decompressed); } /* Decompress as much as we can in one pass. */ ret = BZ2_bzDecompress(&(state->stream)); __archive_read_filter_consume(self->upstream, state->stream.next_in - read_buf); switch (ret) { case BZ_STREAM_END: /* Found end of stream. */ switch (BZ2_bzDecompressEnd(&(state->stream))) { case BZ_OK: break; default: archive_set_error(&(self->archive->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FATAL); } state->valid = 0; /* FALLTHROUGH */ case BZ_OK: /* Decompressor made some progress. */ /* If we filled our buffer, update stats and return. */ if (state->stream.avail_out == 0) { *p = state->out_block; decompressed = state->stream.next_out - state->out_block; return (decompressed); } break; default: /* Return an error. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FATAL); } } } /* * Clean up the decompressor. */ static int bzip2_filter_close(struct archive_read_filter *self) { struct private_data *state; int ret = ARCHIVE_OK; state = (struct private_data *)self->data; if (state->valid) { switch (BZ2_bzDecompressEnd(&state->stream)) { case BZ_OK: break; default: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); ret = ARCHIVE_FATAL; } state->valid = 0; } free(state->out_block); free(state); return (ret); } #endif /* HAVE_BZLIB_H */ tarsnap-1.0.41/libarchive/archive_read_support_compression_compress.c000066400000000000000000000314531476705112600263040ustar00rootroot00000000000000/*- * 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. */ /* * This code borrows heavily from "compress" source code, which is * protected by the following copyright. (Clause 3 dropped by request * of the Regents.) */ /*- * Copyright (c) 1985, 1986, 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Diomidis Spinellis and James A. Woods, derived from original * work by Spencer Thomas and Joseph Orost. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.11 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" /* * Because LZW decompression is pretty simple, I've just implemented * the whole decompressor here (cribbing from "compress" source code, * of course), rather than relying on an external library. I have * made an effort to clarify and simplify the algorithm, so the * names and structure here don't exactly match those used by compress. */ struct private_data { /* Input variables. */ const unsigned char *next_in; size_t avail_in; int bit_buffer; int bits_avail; size_t bytes_in_section; /* Output variables. */ size_t out_block_size; void *out_block; /* Decompression status variables. */ int use_reset_code; int end_of_stream; /* EOF status. */ int maxcode; /* Largest code. */ int maxcode_bits; /* Length of largest code. */ int section_end_code; /* When to increase bits. */ int bits; /* Current code length. */ int oldcode; /* Previous code. */ int finbyte; /* Last byte of prev code. */ /* Dictionary. */ int free_ent; /* Next dictionary entry. */ unsigned char suffix[65536]; uint16_t prefix[65536]; /* * Scratch area for expanding dictionary entries. Note: * "worst" case here comes from compressing /dev/zero: the * last code in the dictionary will code a sequence of * 65536-256 zero bytes. Thus, we need stack space to expand * a 65280-byte dictionary entry. (Of course, 32640:1 * compression could also be considered the "best" case. ;-) */ unsigned char *stackp; unsigned char stack[65300]; }; static int compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int compress_bidder_init(struct archive_read_filter *); static int compress_bidder_free(struct archive_read_filter_bidder *); static ssize_t compress_filter_read(struct archive_read_filter *, const void **); static int compress_filter_close(struct archive_read_filter *); static int getbits(struct archive_read_filter *, int n); static int next_code(struct archive_read_filter *); int archive_read_support_compression_compress(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); if (bidder == NULL) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->bid = compress_bidder_bid; bidder->init = compress_bidder_init; bidder->options = NULL; bidder->free = compress_bidder_free; return (ARCHIVE_OK); } /* * Test whether we can handle this data. * * This logic returns zero if any part of the signature fails. It * also tries to Do The Right Thing if a very short buffer prevents us * from verifying as much as we would like. */ static int compress_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; int bits_checked; (void)self; /* UNUSED */ buffer = __archive_read_filter_ahead(filter, 2, &avail); if (buffer == NULL) return (0); bits_checked = 0; if (buffer[0] != 037) /* Verify first ID byte. */ return (0); bits_checked += 8; if (buffer[1] != 0235) /* Verify second ID byte. */ return (0); bits_checked += 8; /* * TODO: Verify more. */ return (bits_checked); } /* * Setup the callbacks. */ static int compress_bidder_init(struct archive_read_filter *self) { struct private_data *state; static const size_t out_block_size = 64 * 1024; void *out_block; int code; self->code = ARCHIVE_COMPRESSION_COMPRESS; self->name = "compress (.Z)"; state = (struct private_data *)calloc(1, sizeof(*state)); out_block = malloc(out_block_size); if (state == NULL || out_block == NULL) { free(out_block); free(state); archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for %s decompression", self->name); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; self->read = compress_filter_read; self->skip = NULL; /* not supported */ self->close = compress_filter_close; /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */ (void)getbits(self, 8); /* Skip first signature byte. */ (void)getbits(self, 8); /* Skip second signature byte. */ code = getbits(self, 8); state->maxcode_bits = code & 0x1f; state->maxcode = (1 << state->maxcode_bits); state->use_reset_code = code & 0x80; /* Initialize decompressor. */ state->free_ent = 256; state->stackp = state->stack; if (state->use_reset_code) state->free_ent++; state->bits = 9; state->section_end_code = (1<bits) - 1; state->oldcode = -1; for (code = 255; code >= 0; code--) { state->prefix[code] = 0; state->suffix[code] = code; } next_code(self); return (ARCHIVE_OK); } /* * Return a block of data from the decompression buffer. Decompress more * as necessary. */ static ssize_t compress_filter_read(struct archive_read_filter *self, const void **pblock) { struct private_data *state; unsigned char *p, *start, *end; int ret; state = (struct private_data *)self->data; if (state->end_of_stream) { *pblock = NULL; return (0); } p = start = (unsigned char *)state->out_block; end = start + state->out_block_size; while (p < end && !state->end_of_stream) { if (state->stackp > state->stack) { *p++ = *--state->stackp; } else { ret = next_code(self); if (ret == -1) state->end_of_stream = ret; else if (ret != ARCHIVE_OK) return (ret); } } *pblock = start; return (p - start); } /* * Clean up the reader. */ static int compress_bidder_free(struct archive_read_filter_bidder *self) { self->data = NULL; return (ARCHIVE_OK); } /* * Close and release the filter. */ static int compress_filter_close(struct archive_read_filter *self) { struct private_data *state = (struct private_data *)self->data; free(state->out_block); free(state); return (ARCHIVE_OK); } /* * Process the next code and fill the stack with the expansion * of the code. Returns ARCHIVE_FATAL if there is a fatal I/O or * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise. */ static int next_code(struct archive_read_filter *self) { struct private_data *state = (struct private_data *)self->data; int code, newcode; static int debug_buff[1024]; static unsigned debug_index; code = newcode = getbits(self, state->bits); if (code < 0) return (code); debug_buff[debug_index++] = code; if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0])) debug_index = 0; /* If it's a reset code, reset the dictionary. */ if ((code == 256) && state->use_reset_code) { /* * The original 'compress' implementation blocked its * I/O in a manner that resulted in junk bytes being * inserted after every reset. The next section skips * this junk. (Yes, the number of *bytes* to skip is * a function of the current *bit* length.) */ int skip_bytes = state->bits - (state->bytes_in_section % state->bits); skip_bytes %= state->bits; state->bits_avail = 0; /* Discard rest of this byte. */ while (skip_bytes-- > 0) { code = getbits(self, 8); if (code < 0) return (code); } /* Now, actually do the reset. */ state->bytes_in_section = 0; state->bits = 9; state->section_end_code = (1 << state->bits) - 1; state->free_ent = 257; state->oldcode = -1; return (next_code(self)); } if (code > state->free_ent) { /* An invalid code is a fatal error. */ archive_set_error(&(self->archive->archive), -1, "Invalid compressed data"); return (ARCHIVE_FATAL); } /* Special case for KwKwK string. */ if (code >= state->free_ent) { *state->stackp++ = state->finbyte; code = state->oldcode; } /* Generate output characters in reverse order. */ while (code >= 256) { *state->stackp++ = state->suffix[code]; code = state->prefix[code]; } *state->stackp++ = state->finbyte = code; /* Generate the new entry. */ code = state->free_ent; if (code < state->maxcode && state->oldcode >= 0) { state->prefix[code] = state->oldcode; state->suffix[code] = state->finbyte; ++state->free_ent; } if (state->free_ent > state->section_end_code) { state->bits++; state->bytes_in_section = 0; if (state->bits == state->maxcode_bits) state->section_end_code = state->maxcode; else state->section_end_code = (1 << state->bits) - 1; } /* Remember previous code. */ state->oldcode = newcode; return (ARCHIVE_OK); } /* * Return next 'n' bits from stream. * * -1 indicates end of available data. */ static int getbits(struct archive_read_filter *self, int n) { struct private_data *state = (struct private_data *)self->data; int code; ssize_t ret; static const int mask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; while (state->bits_avail < n) { if (state->avail_in <= 0) { state->next_in = __archive_read_filter_ahead(self->upstream, 1, &ret); if (ret == 0) return (-1); if (ret < 0 || state->next_in == NULL) return (ARCHIVE_FATAL); state->avail_in = ret; __archive_read_filter_consume(self->upstream, ret); } state->bit_buffer |= *state->next_in++ << state->bits_avail; state->avail_in--; state->bits_avail += 8; state->bytes_in_section++; } code = state->bit_buffer; state->bit_buffer >>= n; state->bits_avail -= n; return (code & mask[n]); } tarsnap-1.0.41/libarchive/archive_read_support_compression_gzip.c000066400000000000000000000275661476705112600254340ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.17 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #ifdef HAVE_ZLIB_H struct private_data { z_stream stream; char in_stream; unsigned char *out_block; size_t out_block_size; int64_t total_out; unsigned long crc; char eof; /* True = found end of compressed data. */ }; /* Gzip Filter. */ static ssize_t gzip_filter_read(struct archive_read_filter *, const void **); static int gzip_filter_close(struct archive_read_filter *); #endif /* * Note that we can detect gzip archives even if we can't decompress * them. (In fact, we like detecting them because we can give better * error messages.) So the bid framework here gets compiled even * if zlib is unavailable. * * TODO: If zlib is unavailable, gzip_bidder_init() should * use the compress_program framework to try to fire up an external * gunzip program. */ static int gzip_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int gzip_bidder_init(struct archive_read_filter *); int archive_read_support_compression_gzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); if (bidder == NULL) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->bid = gzip_bidder_bid; bidder->init = gzip_bidder_init; bidder->options = NULL; bidder->free = NULL; /* No data, so no cleanup necessary. */ /* Signal the extent of gzip support with the return value here. */ #if HAVE_ZLIB_H return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external gunzip program"); return (ARCHIVE_WARN); #endif } /* * Read and verify the header. * * Returns zero if the header couldn't be validated, else returns * number of bytes in header. If pbits is non-NULL, it receives a * count of bits verified, suitable for use by bidder. */ static int peek_at_header(struct archive_read_filter *filter, int *pbits) { const unsigned char *p; ssize_t avail, len; int bits = 0; int header_flags; /* Start by looking at the first ten bytes of the header, which * is all fixed layout. */ len = 10; p = __archive_read_filter_ahead(filter, len, &avail); if (p == NULL || avail == 0) return (0); if (p[0] != 037) return (0); bits += 8; if (p[1] != 0213) return (0); bits += 8; if (p[2] != 8) /* We only support deflation. */ return (0); bits += 8; if ((p[3] & 0xE0)!= 0) /* No reserved flags set. */ return (0); bits += 3; header_flags = p[3]; /* Bytes 4-7 are mod time. */ /* Byte 8 is deflate flags. */ /* XXXX TODO: return deflate flags back to consume_header for use in initializing the decompressor. */ /* Byte 9 is OS. */ /* Optional extra data: 2 byte length plus variable body. */ if (header_flags & 4) { p = __archive_read_filter_ahead(filter, len + 2, &avail); if (p == NULL) return (0); len += ((int)p[len + 1] << 8) | (int)p[len]; } /* Null-terminated optional filename. */ if (header_flags & 8) { do { ++len; if (avail < len) p = __archive_read_filter_ahead(filter, len, &avail); if (p == NULL) return (0); } while (p[len - 1] != 0); } /* Null-terminated optional comment. */ if (header_flags & 16) { do { ++len; if (avail < len) p = __archive_read_filter_ahead(filter, len, &avail); if (p == NULL) return (0); } while (p[len - 1] != 0); } /* Optional header CRC */ if ((header_flags & 2)) { p = __archive_read_filter_ahead(filter, len + 2, &avail); if (p == NULL) return (0); #if 0 int hcrc = ((int)p[len + 1] << 8) | (int)p[len]; int crc = /* XXX TODO: Compute header CRC. */; if (crc != hcrc) return (0); bits += 16; #endif len += 2; } if (pbits != NULL) *pbits = bits; return (len); } /* * Bidder just verifies the header and returns the number of verified bits. */ static int gzip_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { int bits_checked; (void)self; /* UNUSED */ if (peek_at_header(filter, &bits_checked)) return (bits_checked); return (0); } #ifndef HAVE_ZLIB_H /* * If we don't have the library on this system, we can't do the * decompression directly. We can, however, try to run gunzip * in case that's available. */ static int gzip_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "gunzip"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_COMPRESSION_GZIP; self->name = "gzip"; return (r); } #else /* * Initialize the filter object. */ static int gzip_bidder_init(struct archive_read_filter *self) { struct private_data *state; static const size_t out_block_size = 64 * 1024; void *out_block; self->code = ARCHIVE_COMPRESSION_GZIP; self->name = "gzip"; state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { free(out_block); free(state); archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for gzip decompression"); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; self->read = gzip_filter_read; self->skip = NULL; /* not supported */ self->close = gzip_filter_close; state->in_stream = 0; /* We're not actually within a stream yet. */ return (ARCHIVE_OK); } static int consume_header(struct archive_read_filter *self) { struct private_data *state; ssize_t avail; size_t len; int ret; state = (struct private_data *)self->data; /* If this is a real header, consume it. */ len = peek_at_header(self->upstream, NULL); if (len == 0) return (ARCHIVE_EOF); __archive_read_filter_consume(self->upstream, len); /* Initialize CRC accumulator. */ state->crc = crc32(0L, NULL, 0); /* Initialize compression library. */ state->stream.next_in = (unsigned char *)(uintptr_t) __archive_read_filter_ahead(self->upstream, 1, &avail); state->stream.avail_in = avail; ret = inflateInit2(&(state->stream), -15 /* Don't check for zlib header */); /* Decipher the error code. */ switch (ret) { case Z_OK: state->in_stream = 1; return (ARCHIVE_OK); case Z_STREAM_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid setup parameter"); break; case Z_MEM_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Internal error initializing compression library: " "out of memory"); break; case Z_VERSION_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid library version"); break; default: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " " Zlib error %d", ret); break; } return (ARCHIVE_FATAL); } static int consume_trailer(struct archive_read_filter *self) { struct private_data *state; const unsigned char *p; ssize_t avail; state = (struct private_data *)self->data; state->in_stream = 0; switch (inflateEnd(&(state->stream))) { case Z_OK: break; default: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up gzip decompressor"); return (ARCHIVE_FATAL); } /* GZip trailer is a fixed 8 byte structure. */ p = __archive_read_filter_ahead(self->upstream, 8, &avail); if (p == NULL || avail == 0) return (ARCHIVE_FATAL); /* XXX TODO: Verify the length and CRC. */ /* We've verified the trailer, so consume it now. */ __archive_read_filter_consume(self->upstream, 8); return (ARCHIVE_OK); } static ssize_t gzip_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; ssize_t avail_in; int ret; state = (struct private_data *)self->data; /* Empty our output buffer. */ state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Try to fill the output buffer. */ while (state->stream.avail_out > 0 && !state->eof) { /* If we're not in a stream, read a header * and initialize the decompression library. */ if (!state->in_stream) { ret = consume_header(self); if (ret == ARCHIVE_EOF) { state->eof = 1; break; } if (ret < ARCHIVE_OK) return (ret); } /* Peek at the next available data. */ /* ZLib treats stream.next_in as const but doesn't declare * it so, hence this ugly cast. */ state->stream.next_in = (unsigned char *)(uintptr_t) __archive_read_filter_ahead(self->upstream, 1, &avail_in); if (state->stream.next_in == NULL) return (ARCHIVE_FATAL); state->stream.avail_in = avail_in; /* Decompress and consume some of that data. */ ret = inflate(&(state->stream), 0); switch (ret) { case Z_OK: /* Decompressor made some progress. */ __archive_read_filter_consume(self->upstream, avail_in - state->stream.avail_in); break; case Z_STREAM_END: /* Found end of stream. */ __archive_read_filter_consume(self->upstream, avail_in - state->stream.avail_in); /* Consume the stream trailer; release the * decompression library. */ ret = consume_trailer(self); break; default: /* Return an error. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "gzip decompression failed"); return (ARCHIVE_FATAL); } } /* We've read as much as we can. */ decompressed = state->stream.next_out - state->out_block; state->total_out += decompressed; if (decompressed == 0) *p = NULL; else *p = state->out_block; return (decompressed); } /* * Clean up the decompressor. */ static int gzip_filter_close(struct archive_read_filter *self) { struct private_data *state; int ret; state = (struct private_data *)self->data; ret = ARCHIVE_OK; if (state->in_stream) { switch (inflateEnd(&(state->stream))) { case Z_OK: break; default: archive_set_error(&(self->archive->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up gzip compressor"); ret = ARCHIVE_FATAL; } } free(state->out_block); free(state); return (ret); } #endif /* HAVE_ZLIB_H */ tarsnap-1.0.41/libarchive/archive_read_support_compression_none.c000066400000000000000000000032631476705112600254060ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_compression_none.c,v 1.20 2008/12/06 06:45:15 kientzle Exp $"); #include "archive.h" /* * Uncompressed streams are handled implicitly by the read core, * so this is now a no-op. */ int archive_read_support_compression_none(struct archive *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_support_compression_program.c000066400000000000000000000307401476705112600261160ustar00rootroot00000000000000/*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.6 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_SIGNAL_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" int archive_read_support_compression_program(struct archive *a, const char *cmd) { return (archive_read_support_compression_program_signature(a, cmd, NULL, 0)); } /* This capability is only available on POSIX systems. */ #if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) /* * On non-Posix systems, allow the program to build, but choke if * this function is actually invoked. */ int archive_read_support_compression_program_signature(struct archive *_a, const char *cmd, void *signature, size_t signature_len) { (void)_a; /* UNUSED */ (void)cmd; /* UNUSED */ (void)signature; /* UNUSED */ (void)signature_len; /* UNUSED */ archive_set_error(_a, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } int __archive_read_program(struct archive_read_filter *self, const char *cmd) { (void)self; /* UNUSED */ (void)cmd; /* UNUSED */ archive_set_error(&self->archive->archive, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } #else #include "filter_fork.h" /* * The bidder object stores the command and the signature to watch for. * The 'inhibit' entry here is used to ensure that unchecked filters never * bid twice in the same pipeline. */ struct program_bidder { char *cmd; void *signature; size_t signature_len; int inhibit; }; static int program_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *upstream); static int program_bidder_init(struct archive_read_filter *); static int program_bidder_free(struct archive_read_filter_bidder *); /* * The actual filter needs to track input and output data. */ struct program_filter { char *description; pid_t child; int exit_status; int waitpid_return; int child_stdin, child_stdout; char *out_buf; size_t out_buf_len; }; static ssize_t program_filter_read(struct archive_read_filter *, const void **); static int program_filter_close(struct archive_read_filter *); int archive_read_support_compression_program_signature(struct archive *_a, const char *cmd, const void *signature, size_t signature_len) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder; struct program_bidder *state; /* * Get a bidder object from the read core. */ bidder = __archive_read_get_bidder(a); if (bidder == NULL) return (ARCHIVE_FATAL); /* * Allocate our private state. */ state = (struct program_bidder *)calloc(1, sizeof (*state)); if (state == NULL) goto memerr; state->cmd = strdup(cmd); if (state->cmd == NULL) goto memerr; if (signature != NULL && signature_len > 0) { state->signature_len = signature_len; state->signature = malloc(signature_len); memcpy(state->signature, signature, signature_len); } /* * Fill in the bidder object. */ bidder->data = state; bidder->bid = program_bidder_bid; bidder->init = program_bidder_init; bidder->options = NULL; bidder->free = program_bidder_free; return (ARCHIVE_OK); memerr: free(state); archive_set_error(_a, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } static int program_bidder_free(struct archive_read_filter_bidder *self) { struct program_bidder *state = (struct program_bidder *)self->data; free(state->cmd); free(state->signature); free(self->data); return (ARCHIVE_OK); } /* * If we do have a signature, bid only if that matches. * * If there's no signature, we bid INT_MAX the first time * we're called, then never bid again. */ static int program_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *upstream) { struct program_bidder *state = self->data; const char *p; /* If we have a signature, use that to match. */ if (state->signature_len > 0) { p = __archive_read_filter_ahead(upstream, state->signature_len, NULL); if (p == NULL) return (0); /* No match, so don't bid. */ if (memcmp(p, state->signature, state->signature_len) != 0) return (0); return (state->signature_len * 8); } /* Otherwise, bid once and then never bid again. */ if (state->inhibit) return (0); state->inhibit = 1; return (INT_MAX); } /* * Shut down the child, return ARCHIVE_OK if it exited normally. * * Note that the return value is sticky; if we're called again, * we won't reap the child again, but we will return the same status * (including error message if the child came to a bad end). */ static int child_stop(struct archive_read_filter *self, struct program_filter *state) { /* Close our side of the I/O with the child. */ if (state->child_stdin != -1) { close(state->child_stdin); state->child_stdin = -1; } if (state->child_stdout != -1) { close(state->child_stdout); state->child_stdout = -1; } if (state->child != 0) { /* Reap the child. */ do { state->waitpid_return = waitpid(state->child, &state->exit_status, 0); } while (state->waitpid_return == -1 && errno == EINTR); state->child = 0; } if (state->waitpid_return < 0) { /* waitpid() failed? This is ugly. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Child process exited badly"); return (ARCHIVE_WARN); } if (WIFSIGNALED(state->exit_status)) { #ifdef SIGPIPE /* If the child died because we stopped reading before * it was done, that's okay. Some archive formats * have padding at the end that we routinely ignore. */ /* The alternative to this would be to add a step * before close(child_stdout) above to read from the * child until the child has no more to write. */ if (WTERMSIG(state->exit_status) == SIGPIPE) return (ARCHIVE_OK); #endif archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Child process exited with signal %d", WTERMSIG(state->exit_status)); return (ARCHIVE_WARN); } if (WIFEXITED(state->exit_status)) { if (WEXITSTATUS(state->exit_status) == 0) return (ARCHIVE_OK); archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Child process exited with status %d", WEXITSTATUS(state->exit_status)); return (ARCHIVE_WARN); } return (ARCHIVE_WARN); } /* * Use select() to decide whether the child is ready for read or write. */ static ssize_t child_read(struct archive_read_filter *self, char *buf, size_t buf_len) { struct program_filter *state = self->data; ssize_t ret, requested, avail; const char *p; int saved_errno; requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; for (;;) { do { ret = read(state->child_stdout, buf, requested); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0 || (ret == -1 && errno == EPIPE)) /* Child has closed its output; reap the child * and return the status. */ return (child_stop(self, state)); if (ret == -1 && errno != EAGAIN) return (-1); if (state->child_stdin == -1) { /* Block until child has some I/O ready. */ __archive_check_child(state->child_stdin, state->child_stdout); continue; } /* Get some more data from upstream. */ p = __archive_read_filter_ahead(self->upstream, 1, &avail); if (p == NULL) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); if (avail < 0) return (avail); continue; } do { ret = write(state->child_stdin, p, avail); } while (ret == -1 && errno == EINTR); if (ret > 0) { /* Consume whatever we managed to write. */ __archive_read_filter_consume(self->upstream, ret); } else if (ret == -1 && errno == EAGAIN) { /* Block until child has some I/O ready. */ __archive_check_child(state->child_stdin, state->child_stdout); } else { /* Save errno from the write. */ saved_errno = errno; /* Write failed. */ close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); /* Restore errno. */ errno = saved_errno; /* If it was a bad error, we're done; otherwise * it was EPIPE, and we can still read * from the child. */ if (ret == -1 && errno != EPIPE) return (-1); } } } int __archive_read_program(struct archive_read_filter *self, const char *cmd) { struct program_filter *state; static const size_t out_buf_len = 65536; char *out_buf; char *description; const char *prefix = "Program: "; state = (struct program_filter *)calloc(1, sizeof(*state)); out_buf = (char *)malloc(out_buf_len); description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); if (state == NULL || out_buf == NULL || description == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate input data"); free(state); free(out_buf); free(description); return (ARCHIVE_FATAL); } self->code = ARCHIVE_COMPRESSION_PROGRAM; state->description = description; strcpy(state->description, prefix); strcat(state->description, cmd); self->name = state->description; state->out_buf = out_buf; state->out_buf_len = out_buf_len; if ((state->child = __archive_create_child(cmd, &state->child_stdin, &state->child_stdout)) == -1) { free(state->out_buf); free(state); archive_set_error(&self->archive->archive, EINVAL, "Can't initialise filter"); return (ARCHIVE_FATAL); } self->data = state; self->read = program_filter_read; self->skip = NULL; self->close = program_filter_close; /* XXX Check that we can read at least one byte? */ return (ARCHIVE_OK); } static int program_bidder_init(struct archive_read_filter *self) { struct program_bidder *bidder_state; bidder_state = (struct program_bidder *)self->bidder->data; return (__archive_read_program(self, bidder_state->cmd)); } static ssize_t program_filter_read(struct archive_read_filter *self, const void **buff) { struct program_filter *state; ssize_t bytes; size_t total; char *p; state = (struct program_filter *)self->data; total = 0; p = state->out_buf; while (state->child_stdout != -1 && total < state->out_buf_len) { bytes = child_read(self, p, state->out_buf_len - total); if (bytes < 0) /* No recovery is possible if we can no longer * read from the child. */ return (ARCHIVE_FATAL); if (bytes == 0) /* We got EOF from the child. */ break; total += bytes; p += bytes; } *buff = state->out_buf; return (total); } static int program_filter_close(struct archive_read_filter *self) { struct program_filter *state; int e; state = (struct program_filter *)self->data; e = child_stop(self, state); /* Release our private data. */ free(state->out_buf); free(state->description); free(state); return (e); } #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ tarsnap-1.0.41/libarchive/archive_read_support_compression_xz.c000066400000000000000000000420651476705112600251130ustar00rootroot00000000000000/*- * Copyright (c) 2009 Michihiro NAKAJIMA * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #if HAVE_LZMA_H #include #elif HAVE_LZMADEC_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #if HAVE_LZMA_H && HAVE_LIBLZMA struct private_data { lzma_stream stream; unsigned char *out_block; size_t out_block_size; int64_t total_out; char eof; /* True = found end of compressed data. */ }; /* Combined lzma/xz filter */ static ssize_t xz_filter_read(struct archive_read_filter *, const void **); static int xz_filter_close(struct archive_read_filter *); static int xz_lzma_bidder_init(struct archive_read_filter *); #elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC struct private_data { lzmadec_stream stream; unsigned char *out_block; size_t out_block_size; int64_t total_out; char eof; /* True = found end of compressed data. */ }; /* Lzma-only filter */ static ssize_t lzma_filter_read(struct archive_read_filter *, const void **); static int lzma_filter_close(struct archive_read_filter *); #endif /* * Note that we can detect xz and lzma compressed files even if we * can't decompress them. (In fact, we like detecting them because we * can give better error messages.) So the bid framework here gets * compiled even if no lzma library is available. */ static int xz_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int xz_bidder_init(struct archive_read_filter *); static int lzma_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int lzma_bidder_init(struct archive_read_filter *); int archive_read_support_compression_xz(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); if (bidder == NULL) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->bid = xz_bidder_bid; bidder->init = xz_bidder_init; bidder->options = NULL; bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external unxz program for xz decompression"); return (ARCHIVE_WARN); #endif } int archive_read_support_compression_lzma(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); if (bidder == NULL) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->bid = lzma_bidder_bid; bidder->init = lzma_bidder_init; bidder->options = NULL; bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); #elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external unlzma program for lzma decompression"); return (ARCHIVE_WARN); #endif } /* * Test whether we can handle this data. */ static int xz_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; int bits_checked; (void)self; /* UNUSED */ buffer = __archive_read_filter_ahead(filter, 6, &avail); if (buffer == NULL) return (0); /* * Verify Header Magic Bytes : FD 37 7A 58 5A 00 */ bits_checked = 0; if (buffer[0] != 0xFD) return (0); bits_checked += 8; if (buffer[1] != 0x37) return (0); bits_checked += 8; if (buffer[2] != 0x7A) return (0); bits_checked += 8; if (buffer[3] != 0x58) return (0); bits_checked += 8; if (buffer[4] != 0x5A) return (0); bits_checked += 8; if (buffer[5] != 0x00) return (0); bits_checked += 8; return (bits_checked); } /* * Test whether we can handle this data. * * LZMA has a rather poor file signature. Zeros do not * make good signature bytes as a rule, and the only non-zero byte * here is an ASCII character. For example, an uncompressed tar * archive whose first file is ']' would satisfy this check. It may * be necessary to exclude LZMA from compression_all() because of * this. Clients of libarchive would then have to explicitly enable * LZMA checking instead of (or in addition to) compression_all() when * they have other evidence (file name, command-line option) to go on. */ static int lzma_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; int bits_checked; (void)self; /* UNUSED */ buffer = __archive_read_filter_ahead(filter, 6, &avail); if (buffer == NULL) return (0); /* First byte of raw LZMA stream is always 0x5d. */ bits_checked = 0; if (buffer[0] != 0x5d) return (0); bits_checked += 8; /* Second through fifth bytes are dictionary code, stored in * little-endian order. The two least-significant bytes are * always zero. */ if (buffer[1] != 0 || buffer[2] != 0) return (0); bits_checked += 16; /* ??? TODO: Fix this. ??? */ /* NSIS format check uses this, but I've seen tar.lzma * archives where this byte is 0xff, not 0. Can it * ever be anything other than 0 or 0xff? */ #if 0 if (buffer[5] != 0) return (0); bits_checked += 8; #endif /* TODO: The above test is still very weak. It would be * good to do better. */ return (bits_checked); } #if HAVE_LZMA_H && HAVE_LIBLZMA /* * liblzma 4.999.7 and later support both lzma and xz streams. */ static int xz_bidder_init(struct archive_read_filter *self) { self->code = ARCHIVE_COMPRESSION_XZ; self->name = "xz"; return (xz_lzma_bidder_init(self)); } static int lzma_bidder_init(struct archive_read_filter *self) { self->code = ARCHIVE_COMPRESSION_LZMA; self->name = "lzma"; return (xz_lzma_bidder_init(self)); } /* * Setup the callbacks. */ static int xz_lzma_bidder_init(struct archive_read_filter *self) { static const size_t out_block_size = 64 * 1024; void *out_block; struct private_data *state; int ret; state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for xz decompression"); free(out_block); free(state); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; self->read = xz_filter_read; self->skip = NULL; /* not supported */ self->close = xz_filter_close; state->stream.avail_in = 0; state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Initialize compression library. * TODO: I don't know what value is best for memlimit. * maybe, it needs to check memory size which * running system has. */ if (self->code == ARCHIVE_COMPRESSION_XZ) ret = lzma_stream_decoder(&(state->stream), (1U << 23) + (1U << 21),/* memlimit */ LZMA_CONCATENATED); else ret = lzma_alone_decoder(&(state->stream), (1U << 23) + (1U << 21));/* memlimit */ if (ret == LZMA_OK) return (ARCHIVE_OK); /* Library setup failed: Choose an error message and clean up. */ switch (ret) { case LZMA_MEM_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Internal error initializing compression library: " "Cannot allocate memory"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "Invalid or unsupported options"); break; default: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing lzma library"); break; } free(state->out_block); free(state); self->data = NULL; return (ARCHIVE_FATAL); } /* * Return the next block of decompressed data. */ static ssize_t xz_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; ssize_t avail_in; int ret; state = (struct private_data *)self->data; /* Empty our output buffer. */ state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Try to fill the output buffer. */ while (state->stream.avail_out > 0 && !state->eof) { state->stream.next_in = __archive_read_filter_ahead(self->upstream, 1, &avail_in); if (state->stream.next_in == NULL && avail_in < 0) return (ARCHIVE_FATAL); state->stream.avail_in = avail_in; /* Decompress as much as we can in one pass. */ ret = lzma_code(&(state->stream), (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN); switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ state->eof = 1; /* FALL THROUGH */ case LZMA_OK: /* Decompressor made some progress. */ __archive_read_filter_consume(self->upstream, avail_in - state->stream.avail_in); break; case LZMA_MEM_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Lzma library error: Cannot allocate memory"); return (ARCHIVE_FATAL); case LZMA_MEMLIMIT_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Lzma library error: Out of memory"); return (ARCHIVE_FATAL); case LZMA_FORMAT_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: format not recognized"); return (ARCHIVE_FATAL); case LZMA_OPTIONS_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Invalid options"); return (ARCHIVE_FATAL); case LZMA_DATA_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Corrupted input data"); return (ARCHIVE_FATAL); case LZMA_BUF_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: No progress is possible"); return (ARCHIVE_FATAL); default: /* Return an error. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed: Unknown error"); return (ARCHIVE_FATAL); } } decompressed = state->stream.next_out - state->out_block; state->total_out += decompressed; if (decompressed == 0) *p = NULL; else *p = state->out_block; return (decompressed); } /* * Clean up the decompressor. */ static int xz_filter_close(struct archive_read_filter *self) { struct private_data *state; state = (struct private_data *)self->data; lzma_end(&(state->stream)); free(state->out_block); free(state); return (ARCHIVE_OK); } #else #if HAVE_LZMADEC_H && HAVE_LIBLZMADEC /* * If we have the older liblzmadec library, then we can handle * LZMA streams but not XZ streams. */ /* * Setup the callbacks. */ static int lzma_bidder_init(struct archive_read_filter *self) { static const size_t out_block_size = 64 * 1024; void *out_block; struct private_data *state; ssize_t ret, avail_in; self->code = ARCHIVE_COMPRESSION_LZMA; self->name = "lzma"; state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for lzma decompression"); free(out_block); free(state); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; self->read = lzma_filter_read; self->skip = NULL; /* not supported */ self->close = lzma_filter_close; /* Prime the lzma library with 18 bytes of input. */ state->stream.next_in = (unsigned char *)(uintptr_t) __archive_read_filter_ahead(self->upstream, 18, &avail_in); if (state->stream.next_in == NULL) return (ARCHIVE_FATAL); state->stream.avail_in = avail_in; state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Initialize compression library. */ ret = lzmadec_init(&(state->stream)); __archive_read_filter_consume(self->upstream, avail_in - state->stream.avail_in); if (ret == LZMADEC_OK) return (ARCHIVE_OK); /* Library setup failed: Clean up. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing lzma library"); /* Override the error message if we know what really went wrong. */ switch (ret) { case LZMADEC_HEADER_ERROR: archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid header"); break; case LZMADEC_MEM_ERROR: archive_set_error(&self->archive->archive, ENOMEM, "Internal error initializing compression library: " "out of memory"); break; } free(state->out_block); free(state); self->data = NULL; return (ARCHIVE_FATAL); } /* * Return the next block of decompressed data. */ static ssize_t lzma_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; ssize_t avail_in, ret; state = (struct private_data *)self->data; /* Empty our output buffer. */ state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; /* Try to fill the output buffer. */ while (state->stream.avail_out > 0 && !state->eof) { state->stream.next_in = (unsigned char *)(uintptr_t) __archive_read_filter_ahead(self->upstream, 1, &avail_in); if (state->stream.next_in == NULL && avail_in < 0) return (ARCHIVE_FATAL); state->stream.avail_in = avail_in; /* Decompress as much as we can in one pass. */ ret = lzmadec_decode(&(state->stream), avail_in == 0); switch (ret) { case LZMADEC_STREAM_END: /* Found end of stream. */ state->eof = 1; /* FALL THROUGH */ case LZMADEC_OK: /* Decompressor made some progress. */ __archive_read_filter_consume(self->upstream, avail_in - state->stream.avail_in); break; case LZMADEC_BUF_ERROR: /* Insufficient input data? */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); default: /* Return an error. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed"); return (ARCHIVE_FATAL); } } decompressed = state->stream.next_out - state->out_block; state->total_out += decompressed; if (decompressed == 0) *p = NULL; else *p = state->out_block; return (decompressed); } /* * Clean up the decompressor. */ static int lzma_filter_close(struct archive_read_filter *self) { struct private_data *state; int ret; state = (struct private_data *)self->data; ret = ARCHIVE_OK; switch (lzmadec_end(&(state->stream))) { case LZMADEC_OK: break; default: archive_set_error(&(self->archive->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up %s compressor", self->archive->archive.compression_name); ret = ARCHIVE_FATAL; } free(state->out_block); free(state); return (ret); } #else /* * * If we have no suitable library on this system, we can't actually do * the decompression. We can, however, still detect compressed * archives and emit a useful message. * */ static int lzma_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "unlzma"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_COMPRESSION_LZMA; self->name = "lzma"; return (r); } #endif /* HAVE_LZMADEC_H */ static int xz_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "unxz"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_COMPRESSION_XZ; self->name = "xz"; return (r); } #endif /* HAVE_LZMA_H */ tarsnap-1.0.41/libarchive/archive_read_support_format_all.c000066400000000000000000000034701476705112600241460ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_format_all.c,v 1.10 2007/12/30 04:58:21 kientzle Exp $"); #include "archive.h" int archive_read_support_format_all(struct archive *a) { archive_read_support_format_ar(a); archive_read_support_format_cpio(a); archive_read_support_format_empty(a); archive_read_support_format_iso9660(a); archive_read_support_format_mtree(a); archive_read_support_format_tar(a); archive_read_support_format_zip(a); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_read_support_format_ar.c000066400000000000000000000405601476705112600240010ustar00rootroot00000000000000/*- * Copyright (c) 2007 Kai Wang * Copyright (c) 2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.12 2008/12/17 19:02:42 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" struct ar { off_t entry_bytes_remaining; off_t entry_offset; off_t entry_padding; char *strtab; size_t strtab_size; }; /* * Define structure of the "ar" header. */ #define AR_name_offset 0 #define AR_name_size 16 #define AR_date_offset 16 #define AR_date_size 12 #define AR_uid_offset 28 #define AR_uid_size 6 #define AR_gid_offset 34 #define AR_gid_size 6 #define AR_mode_offset 40 #define AR_mode_size 8 #define AR_size_offset 48 #define AR_size_size 10 #define AR_fmag_offset 58 #define AR_fmag_size 2 static int archive_read_format_ar_bid(struct archive_read *a); static int archive_read_format_ar_cleanup(struct archive_read *a); static int archive_read_format_ar_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset); static int archive_read_format_ar_skip(struct archive_read *a); static int archive_read_format_ar_read_header(struct archive_read *a, struct archive_entry *e); static uint64_t ar_atol8(const char *p, unsigned char_cnt); static uint64_t ar_atol10(const char *p, unsigned char_cnt); static int ar_parse_gnu_filename_table(struct archive_read *a); static int ar_parse_common_header(struct ar *ar, struct archive_entry *, const char *h); int archive_read_support_format_ar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct ar *ar; int r; ar = (struct ar *)malloc(sizeof(*ar)); if (ar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); return (ARCHIVE_FATAL); } memset(ar, 0, sizeof(*ar)); ar->strtab = NULL; r = __archive_read_register_format(a, ar, "ar", archive_read_format_ar_bid, NULL, archive_read_format_ar_read_header, archive_read_format_ar_read_data, NULL, NULL, archive_read_format_ar_skip, archive_read_format_ar_cleanup); if (r != ARCHIVE_OK) { free(ar); return (r); } return (ARCHIVE_OK); } static int archive_read_format_ar_cleanup(struct archive_read *a) { struct ar *ar; ar = (struct ar *)(a->format->data); if (ar->strtab) free(ar->strtab); free(ar); (a->format->data) = NULL; return (ARCHIVE_OK); } static int archive_read_format_ar_bid(struct archive_read *a) { const void *h; if (a->archive.archive_format != 0 && (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) != ARCHIVE_FORMAT_AR) return(0); /* * Verify the 8-byte file signature. * TODO: Do we need to check more than this? */ if ((h = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (strncmp((const char*)h, "!\n", 8) == 0) { return (64); } return (-1); } static int archive_read_format_ar_read_header(struct archive_read *a, struct archive_entry *entry) { char filename[AR_name_size + 1]; struct ar *ar; uint64_t number; /* Used to hold parsed numbers before validation. */ ssize_t bytes_read; size_t bsd_name_length, entry_size; char *p, *st; const void *b; const char *h; int r; ar = (struct ar*)(a->format->data); if (a->archive.file_position == 0) { /* * We are now at the beginning of the archive, * so we need first consume the ar global header. */ __archive_read_consume(a, 8); /* Set a default format code for now. */ a->archive.archive_format = ARCHIVE_FORMAT_AR; } /* Read the header for the next file entry. */ if ((b = __archive_read_ahead(a, 60, &bytes_read)) == NULL) /* Broken header. */ return (ARCHIVE_EOF); __archive_read_consume(a, 60); h = (const char *)b; /* Verify the magic signature on the file header. */ if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { archive_set_error(&a->archive, EINVAL, "Incorrect file header signature"); return (ARCHIVE_WARN); } /* Copy filename into work buffer. */ strncpy(filename, h + AR_name_offset, AR_name_size); filename[AR_name_size] = '\0'; /* * Guess the format variant based on the filename. */ if (a->archive.archive_format == ARCHIVE_FORMAT_AR) { /* We don't already know the variant, so let's guess. */ /* * Biggest clue is presence of '/': GNU starts special * filenames with '/', appends '/' as terminator to * non-special names, so anything with '/' should be * GNU except for BSD long filenames. */ if (strncmp(filename, "#1/", 3) == 0) a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; else if (strchr(filename, '/') != NULL) a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU; else if (strncmp(filename, "__.SYMDEF", 9) == 0) a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; /* * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/' * if name exactly fills 16-byte field? If so, we * can't assume entries without '/' are BSD. XXX */ } /* Update format name from the code. */ if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) a->archive.archive_format_name = "ar (GNU/SVR4)"; else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) a->archive.archive_format_name = "ar (BSD)"; else a->archive.archive_format_name = "ar"; /* * Remove trailing spaces from the filename. GNU and BSD * variants both pad filename area out with spaces. * This will only be wrong if GNU/SVR4 'ar' implementations * omit trailing '/' for 16-char filenames and we have * a 16-char filename that ends in ' '. */ p = filename + AR_name_size - 1; while (p >= filename && *p == ' ') { *p = '\0'; p--; } /* * Remove trailing slash unless first character is '/'. * (BSD entries never end in '/', so this will only trim * GNU-format entries. GNU special entries start with '/' * and are not terminated in '/', so we don't trim anything * that starts with '/'.) */ if (filename[0] != '/' && p > filename && *p == '/') { *p = '\0'; } if (p < filename) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found entry with empty filename"); return (ARCHIVE_FATAL); } /* * '//' is the GNU filename table. * Later entries can refer to names in this table. */ if (strcmp(filename, "//") == 0) { /* This must come before any call to _read_ahead. */ ar_parse_common_header(ar, entry, h); archive_entry_copy_pathname(entry, filename); archive_entry_set_filetype(entry, AE_IFREG); /* Get the size of the filename table. */ number = ar_atol10(h + AR_size_offset, AR_size_size); if (number > SIZE_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filename table too large"); return (ARCHIVE_FATAL); } entry_size = (size_t)number; if (entry_size == 0) { archive_set_error(&a->archive, EINVAL, "Invalid string table"); return (ARCHIVE_WARN); } if (ar->strtab != NULL) { archive_set_error(&a->archive, EINVAL, "More than one string tables exist"); return (ARCHIVE_WARN); } /* Read the filename table into memory. */ st = malloc(entry_size); if (st == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate filename table buffer"); return (ARCHIVE_FATAL); } ar->strtab = st; ar->strtab_size = entry_size; if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL) return (ARCHIVE_FATAL); memcpy(st, b, entry_size); __archive_read_consume(a, entry_size); /* All contents are consumed. */ ar->entry_bytes_remaining = 0; archive_entry_set_size(entry, ar->entry_bytes_remaining); /* Parse the filename table. */ return (ar_parse_gnu_filename_table(a)); } /* * GNU variant handles long filenames by storing / * to indicate a name stored in the filename table. * XXX TODO: Verify that it's all digits... Don't be fooled * by "/9xyz" XXX */ if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') { number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1); /* * If we can't look up the real name, warn and return * the entry with the wrong name. */ if (ar->strtab == NULL || number > ar->strtab_size) { archive_set_error(&a->archive, EINVAL, "Can't find long filename for entry"); archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ ar_parse_common_header(ar, entry, h); return (ARCHIVE_WARN); } archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]); /* Parse the time, owner, mode, size fields. */ return (ar_parse_common_header(ar, entry, h)); } /* * BSD handles long filenames by storing "#1/" followed by the * length of filename as a decimal number, then prepends the * the filename to the file contents. */ if (strncmp(filename, "#1/", 3) == 0) { /* Parse the time, owner, mode, size fields. */ /* This must occur before _read_ahead is called again. */ ar_parse_common_header(ar, entry, h); /* Parse the size of the name, adjust the file size. */ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3); bsd_name_length = (size_t)number; /* Guard against the filename + trailing NUL * overflowing a size_t and against the filename size * being larger than the entire entry. */ if (number > (uint64_t)(bsd_name_length + 1) || (off_t)bsd_name_length > ar->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad input file size"); return (ARCHIVE_FATAL); } ar->entry_bytes_remaining -= bsd_name_length; /* Adjust file size reported to client. */ archive_entry_set_size(entry, ar->entry_bytes_remaining); /* Read the long name into memory. */ if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); return (ARCHIVE_FATAL); } __archive_read_consume(a, bsd_name_length); /* Store it in the entry. */ p = (char *)malloc(bsd_name_length + 1); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate fname buffer"); return (ARCHIVE_FATAL); } strncpy(p, b, bsd_name_length); p[bsd_name_length] = '\0'; archive_entry_copy_pathname(entry, p); free(p); return (ARCHIVE_OK); } /* * "/" is the SVR4/GNU archive symbol table. */ if (strcmp(filename, "/") == 0) { archive_entry_copy_pathname(entry, "/"); /* Parse the time, owner, mode, size fields. */ r = ar_parse_common_header(ar, entry, h); /* Force the file type to a regular file. */ archive_entry_set_filetype(entry, AE_IFREG); return (r); } /* * "__.SYMDEF" is a BSD archive symbol table. */ if (strcmp(filename, "__.SYMDEF") == 0) { archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ return (ar_parse_common_header(ar, entry, h)); } /* * Otherwise, this is a standard entry. The filename * has already been trimmed as much as possible, based * on our current knowledge of the format. */ archive_entry_copy_pathname(entry, filename); return (ar_parse_common_header(ar, entry, h)); } static int ar_parse_common_header(struct ar *ar, struct archive_entry *entry, const char *h) { uint64_t n; /* Copy remaining header */ archive_entry_set_mtime(entry, (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L); archive_entry_set_uid(entry, (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size)); archive_entry_set_gid(entry, (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size)); archive_entry_set_mode(entry, (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size)); n = ar_atol10(h + AR_size_offset, AR_size_size); ar->entry_offset = 0; ar->entry_padding = n % 2; archive_entry_set_size(entry, n); ar->entry_bytes_remaining = n; return (ARCHIVE_OK); } static int archive_read_format_ar_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { ssize_t bytes_read; struct ar *ar; ar = (struct ar *)(a->format->data); if (ar->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated ar archive"); return (ARCHIVE_FATAL); } if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read > ar->entry_bytes_remaining) bytes_read = (ssize_t)ar->entry_bytes_remaining; *size = bytes_read; *offset = ar->entry_offset; ar->entry_offset += bytes_read; ar->entry_bytes_remaining -= bytes_read; __archive_read_consume(a, (size_t)bytes_read); return (ARCHIVE_OK); } else { while (ar->entry_padding > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > ar->entry_padding) bytes_read = (ssize_t)ar->entry_padding; __archive_read_consume(a, (size_t)bytes_read); ar->entry_padding -= bytes_read; } *buff = NULL; *size = 0; *offset = ar->entry_offset; return (ARCHIVE_EOF); } } static int archive_read_format_ar_skip(struct archive_read *a) { off_t bytes_skipped; struct ar* ar; ar = (struct ar *)(a->format->data); bytes_skipped = __archive_read_skip(a, ar->entry_bytes_remaining + ar->entry_padding); if (bytes_skipped < 0) return (ARCHIVE_FATAL); ar->entry_bytes_remaining = 0; ar->entry_padding = 0; return (ARCHIVE_OK); } static int ar_parse_gnu_filename_table(struct archive_read *a) { struct ar *ar; char *p; size_t size; ar = (struct ar*)(a->format->data); size = ar->strtab_size; for (p = ar->strtab; p < ar->strtab + size - 1; ++p) { if (*p == '/') { *p++ = '\0'; if (*p != '\n') goto bad_string_table; *p = '\0'; } } /* * GNU ar always pads the table to an even size. * The pad character is either '\n' or '`'. */ if (p != ar->strtab + size && *p != '\n' && *p != '`') goto bad_string_table; /* Enforce zero termination. */ ar->strtab[size - 1] = '\0'; return (ARCHIVE_OK); bad_string_table: archive_set_error(&a->archive, EINVAL, "Invalid string table"); free(ar->strtab); ar->strtab = NULL; return (ARCHIVE_WARN); } static uint64_t ar_atol8(const char *p, unsigned char_cnt) { uint64_t l, limit, last_digit_limit; unsigned int digit, base; base = 8; limit = UINT64_MAX / base; last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; while (*p >= '0' && digit < base && char_cnt-- > 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (l); } static uint64_t ar_atol10(const char *p, unsigned char_cnt) { uint64_t l, limit, last_digit_limit; unsigned int base, digit; base = 10; limit = UINT64_MAX / base; last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; while (*p >= '0' && digit < base && char_cnt-- > 0) { if (l > limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (l); } tarsnap-1.0.41/libarchive/archive_read_support_format_cpio.c000066400000000000000000000554451476705112600243410ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.27 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" struct cpio_bin_header { unsigned char c_magic[2]; unsigned char c_dev[2]; unsigned char c_ino[2]; unsigned char c_mode[2]; unsigned char c_uid[2]; unsigned char c_gid[2]; unsigned char c_nlink[2]; unsigned char c_rdev[2]; unsigned char c_mtime[4]; unsigned char c_namesize[2]; unsigned char c_filesize[4]; }; struct cpio_odc_header { char c_magic[6]; char c_dev[6]; char c_ino[6]; char c_mode[6]; char c_uid[6]; char c_gid[6]; char c_nlink[6]; char c_rdev[6]; char c_mtime[11]; char c_namesize[6]; char c_filesize[11]; }; struct cpio_newc_header { char c_magic[6]; char c_ino[8]; char c_mode[8]; char c_uid[8]; char c_gid[8]; char c_nlink[8]; char c_mtime[8]; char c_filesize[8]; char c_devmajor[8]; char c_devminor[8]; char c_rdevmajor[8]; char c_rdevminor[8]; char c_namesize[8]; char c_crc[8]; }; struct links_entry { struct links_entry *next; struct links_entry *previous; int links; dev_t dev; ino_t ino; char *name; }; #define CPIO_MAGIC 0x13141516 struct cpio { int magic; int (*read_header)(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); struct links_entry *links_head; struct archive_string entry_name; struct archive_string entry_linkname; off_t entry_bytes_remaining; off_t entry_offset; off_t entry_padding; }; static int64_t atol16(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive_read *); static int archive_read_format_cpio_cleanup(struct archive_read *); static int archive_read_format_cpio_read_data(struct archive_read *, const void **, size_t *, off_t *); static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int be4(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_bin_le(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_newc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_odc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); static int le4(const unsigned char *); static void record_hardlink(struct cpio *cpio, struct archive_entry *entry); int archive_read_support_format_cpio(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cpio *cpio; int r; cpio = (struct cpio *)malloc(sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } memset(cpio, 0, sizeof(*cpio)); cpio->magic = CPIO_MAGIC; r = __archive_read_register_format(a, cpio, "cpio", archive_read_format_cpio_bid, NULL, archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, NULL, NULL, NULL, archive_read_format_cpio_cleanup); if (r != ARCHIVE_OK) free(cpio); return (ARCHIVE_OK); } static int archive_read_format_cpio_bid(struct archive_read *a) { const void *h; const unsigned char *p; struct cpio *cpio; int bid; cpio = (struct cpio *)(a->format->data); if ((h = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); p = (const unsigned char *)h; bid = 0; if (memcmp(p, "070707", 6) == 0) { /* ASCII cpio archive (odc, POSIX.1) */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070702", 6) == 0) { /* ASCII cpio archive (SVR4 with CRC) */ /* XXX TODO: Flag that we should check the CRC. XXX */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (p[0] * 256 + p[1] == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ } else if (p[0] + p[1] * 256 == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; /* Is more verification possible here? */ } else return (ARCHIVE_WARN); return (bid); } static int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; const void *h; size_t namelength; size_t name_pad; int r; cpio = (struct cpio *)(a->format->data); r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); if (r < ARCHIVE_WARN) return (r); /* Read name from buffer. */ h = __archive_read_ahead(a, namelength + name_pad, NULL); if (h == NULL) return (ARCHIVE_FATAL); __archive_read_consume(a, namelength + name_pad); archive_strncpy(&cpio->entry_name, (const char *)h, namelength); archive_entry_set_pathname(entry, cpio->entry_name.s); cpio->entry_offset = 0; /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { h = __archive_read_ahead(a, cpio->entry_bytes_remaining, NULL); if (h == NULL) return (ARCHIVE_FATAL); __archive_read_consume(a, cpio->entry_bytes_remaining); archive_strncpy(&cpio->entry_linkname, (const char *)h, cpio->entry_bytes_remaining); archive_entry_set_symlink(entry, cpio->entry_linkname.s); cpio->entry_bytes_remaining = 0; } /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) { /* TODO: Store file location of start of block. */ archive_set_error(&a->archive, 0, NULL); return (ARCHIVE_EOF); } /* Detect and record hardlinks to previously-extracted entries. */ record_hardlink(cpio, entry); return (r); } static int archive_read_format_cpio_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { ssize_t bytes_read; struct cpio *cpio; cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) bytes_read = cpio->entry_bytes_remaining; *size = bytes_read; *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } else { while (cpio->entry_padding > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_padding) bytes_read = cpio->entry_padding; __archive_read_consume(a, bytes_read); cpio->entry_padding -= bytes_read; } *buff = NULL; *size = 0; *offset = cpio->entry_offset; return (ARCHIVE_EOF); } } /* * Skip forward to the next cpio newc header by searching for the * 07070[12] string. This should be generalized and merged with * find_odc_header below. */ static int is_hex(const char *p, size_t len) { while (len-- > 0) { if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')) ++p; else return (0); } return (1); } static int find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("07070", p, 5) == 0 && (p[5] == '1' || p[5] == '2') && is_hex(p, sizeof(struct cpio_newc_header))) return (ARCHIVE_OK); /* * Scan ahead until we find something that looks * like an odc header. */ while (p + sizeof(struct cpio_newc_header) < q) { switch (p[5]) { case '1': case '2': if (memcmp("07070", p, 5) == 0 && is_hex(p, sizeof(struct cpio_newc_header))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_newc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const struct cpio_newc_header *header; int r; r = find_newc_header(a); if (r < ARCHIVE_WARN) return (r); /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), NULL); if (h == NULL) return (ARCHIVE_FATAL); __archive_read_consume(a, sizeof(struct cpio_newc_header)); /* Parse out hex fields. */ header = (const struct cpio_newc_header *)h; if (memcmp(header->c_magic, "070701", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)"; } else if (memcmp(header->c_magic, "070702", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)"; } else { /* TODO: Abort here? */ } archive_entry_set_devmajor(entry, atol16(header->c_devmajor, sizeof(header->c_devmajor))); archive_entry_set_devminor(entry, atol16(header->c_devminor, sizeof(header->c_devminor))); archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino))); archive_entry_set_mode(entry, atol16(header->c_mode, sizeof(header->c_mode))); archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid))); archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid))); archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink))); archive_entry_set_rdevmajor(entry, atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor))); archive_entry_set_rdevminor(entry, atol16(header->c_rdevminor, sizeof(header->c_rdevminor))); archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0); *namelength = atol16(header->c_namesize, sizeof(header->c_namesize)); /* Pad name to 2 more than a multiple of 4. */ *name_pad = (2 - *namelength) & 3; /* Make sure that the padded name length fits into size_t. */ if ((size_t)(*namelength + *name_pad) < *namelength) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cpio archive has invalid namelength"); return (ARCHIVE_FATAL); } /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol16(header->c_filesize, sizeof(header->c_filesize)); archive_entry_set_size(entry, cpio->entry_bytes_remaining); /* Pad file contents to a multiple of 4. */ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; return (r); } /* * Skip forward to the next cpio odc header by searching for the * 070707 string. This is a hand-optimized search that could * probably be easily generalized to handle all character-based * cpio variants. */ static int is_octal(const char *p, size_t len) { while (len-- > 0) { if (*p < '0' || *p > '7') return (0); ++p; } return (1); } static int find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("070707", p, 6) == 0 && is_octal(p, sizeof(struct cpio_odc_header))) return (ARCHIVE_OK); /* * Scan ahead until we find something that looks * like an odc header. */ while (p + sizeof(struct cpio_odc_header) < q) { switch (p[5]) { case '7': if (memcmp("070707", p, 6) == 0 && is_octal(p, sizeof(struct cpio_odc_header))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_odc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; int r; const struct cpio_odc_header *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; /* Find the start of the next header. */ r = find_odc_header(a); if (r < ARCHIVE_WARN) return (r); /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL); if (h == NULL) return (ARCHIVE_FATAL); __archive_read_consume(a, sizeof(struct cpio_odc_header)); /* Parse out octal fields. */ header = (const struct cpio_odc_header *)h; archive_entry_set_dev(entry, atol8(header->c_dev, sizeof(header->c_dev))); archive_entry_set_ino(entry, atol8(header->c_ino, sizeof(header->c_ino))); archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode))); archive_entry_set_uid(entry, atol8(header->c_uid, sizeof(header->c_uid))); archive_entry_set_gid(entry, atol8(header->c_gid, sizeof(header->c_gid))); archive_entry_set_nlink(entry, atol8(header->c_nlink, sizeof(header->c_nlink))); archive_entry_set_rdev(entry, atol8(header->c_rdev, sizeof(header->c_rdev))); archive_entry_set_mtime(entry, atol8(header->c_mtime, sizeof(header->c_mtime)), 0); *namelength = atol8(header->c_namesize, sizeof(header->c_namesize)); *name_pad = 0; /* No padding of filename. */ /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol8(header->c_filesize, sizeof(header->c_filesize)); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; return (r); } static int header_bin_le(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const struct cpio_bin_header *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL); if (h == NULL) return (ARCHIVE_FATAL); __archive_read_consume(a, sizeof(struct cpio_bin_header)); /* Parse out binary fields. */ header = (const struct cpio_bin_header *)h; archive_entry_set_dev(entry, header->c_dev[0] + header->c_dev[1] * 256); archive_entry_set_ino(entry, header->c_ino[0] + header->c_ino[1] * 256); archive_entry_set_mode(entry, header->c_mode[0] + header->c_mode[1] * 256); archive_entry_set_uid(entry, header->c_uid[0] + header->c_uid[1] * 256); archive_entry_set_gid(entry, header->c_gid[0] + header->c_gid[1] * 256); archive_entry_set_nlink(entry, header->c_nlink[0] + header->c_nlink[1] * 256); archive_entry_set_rdev(entry, header->c_rdev[0] + header->c_rdev[1] * 256); archive_entry_set_mtime(entry, le4(header->c_mtime), 0); *namelength = header->c_namesize[0] + header->c_namesize[1] * 256; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = le4(header->c_filesize); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ return (ARCHIVE_OK); } static int header_bin_be(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const struct cpio_bin_header *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL); if (h == NULL) return (ARCHIVE_FATAL); __archive_read_consume(a, sizeof(struct cpio_bin_header)); /* Parse out binary fields. */ header = (const struct cpio_bin_header *)h; archive_entry_set_dev(entry, header->c_dev[0] * 256 + header->c_dev[1]); archive_entry_set_ino(entry, header->c_ino[0] * 256 + header->c_ino[1]); archive_entry_set_mode(entry, header->c_mode[0] * 256 + header->c_mode[1]); archive_entry_set_uid(entry, header->c_uid[0] * 256 + header->c_uid[1]); archive_entry_set_gid(entry, header->c_gid[0] * 256 + header->c_gid[1]); archive_entry_set_nlink(entry, header->c_nlink[0] * 256 + header->c_nlink[1]); archive_entry_set_rdev(entry, header->c_rdev[0] * 256 + header->c_rdev[1]); archive_entry_set_mtime(entry, be4(header->c_mtime), 0); *namelength = header->c_namesize[0] * 256 + header->c_namesize[1]; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = be4(header->c_filesize); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ return (ARCHIVE_OK); } static int archive_read_format_cpio_cleanup(struct archive_read *a) { struct cpio *cpio; cpio = (struct cpio *)(a->format->data); /* Free inode->name map */ while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; if (cpio->links_head->name) free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } archive_string_free(&cpio->entry_name); free(cpio); (a->format->data) = NULL; return (ARCHIVE_OK); } static int le4(const unsigned char *p) { return ((p[0]<<16) + (p[1]<<24) + (p[2]<<0) + (p[3]<<8)); } static int be4(const unsigned char *p) { return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24)); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t atol8(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else return (l); p++; l <<= 3; l |= digit; } return (l); } static int64_t atol16(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= 'a' && *p <= 'f') digit = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') digit = *p - 'A' + 10; else if (*p >= '0' && *p <= '9') digit = *p - '0'; else return (l); p++; l <<= 4; l |= digit; } return (l); } static void record_hardlink(struct cpio *cpio, struct archive_entry *entry) { struct links_entry *le; dev_t dev; ino_t ino; dev = archive_entry_dev(entry); ino = archive_entry_ino(entry); /* * First look in the list of multiply-linked files. If we've * already dumped it, convert this entry to a hard link entry. */ for (le = cpio->links_head; le; le = le->next) { if (le->dev == dev && le->ino == ino) { archive_entry_copy_hardlink(entry, le->name); if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (cpio->links_head == le) cpio->links_head = le->next; free(le->name); free(le); } return; } } le = (struct links_entry *)malloc(sizeof(struct links_entry)); if (le == NULL) __archive_errx(1, "Out of memory adding file to list"); if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; le->name = strdup(archive_entry_pathname(entry)); if (le->name == NULL) __archive_errx(1, "Out of memory adding file to list"); } tarsnap-1.0.41/libarchive/archive_read_support_format_empty.c000066400000000000000000000056231476705112600245360ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_format_empty.c,v 1.4 2008/12/06 06:45:15 kientzle Exp $"); #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" static int archive_read_format_empty_bid(struct archive_read *); static int archive_read_format_empty_read_data(struct archive_read *, const void **, size_t *, off_t *); static int archive_read_format_empty_read_header(struct archive_read *, struct archive_entry *); int archive_read_support_format_empty(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r; r = __archive_read_register_format(a, NULL, NULL, archive_read_format_empty_bid, NULL, archive_read_format_empty_read_header, archive_read_format_empty_read_data, NULL, NULL, NULL, NULL); return (r); } static int archive_read_format_empty_bid(struct archive_read *a) { ssize_t avail; (void)__archive_read_ahead(a, 1, &avail); if (avail != 0) return (-1); return (1); } static int archive_read_format_empty_read_header(struct archive_read *a, struct archive_entry *entry) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ a->archive.archive_format = ARCHIVE_FORMAT_EMPTY; a->archive.archive_format_name = "Empty file"; return (ARCHIVE_EOF); } static int archive_read_format_empty_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { (void)a; /* UNUSED */ (void)buff; /* UNUSED */ (void)size; /* UNUSED */ (void)offset; /* UNUSED */ return (ARCHIVE_EOF); } tarsnap-1.0.41/libarchive/archive_read_support_format_iso9660.c000066400000000000000000001305041476705112600245140ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2009 Andreas Henriksson * 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: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.30 2008/12/06 06:57:45 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" /* * An overview of ISO 9660 format: * * Each disk is laid out as follows: * * 32k reserved for private use * * Volume descriptor table. Each volume descriptor * is 2k and specifies basic format information. * The "Primary Volume Descriptor" (PVD) is defined by the * standard and should always be present; other volume * descriptors include various vendor-specific extensions. * * Files and directories. Each file/dir is specified by * an "extent" (starting sector and length in bytes). * Dirs are just files with directory records packed one * after another. The PVD contains a single dir entry * specifying the location of the root directory. Everything * else follows from there. * * This module works by first reading the volume descriptors, then * building a list of directory entries, sorted by starting * sector. At each step, I look for the earliest dir entry that * hasn't yet been read, seek forward to that location and read * that entry. If it's a dir, I slurp in the new dir entries and * add them to the heap; if it's a regular file, I return the * corresponding archive_entry and wait for the client to request * the file body. This strategy allows us to read most compliant * CDs with a single pass through the data, as required by libarchive. */ /* Structure of on-disk primary volume descriptor. */ #define PVD_type_offset 0 #define PVD_type_size 1 #define PVD_id_offset (PVD_type_offset + PVD_type_size) #define PVD_id_size 5 #define PVD_version_offset (PVD_id_offset + PVD_id_size) #define PVD_version_size 1 #define PVD_reserved1_offset (PVD_version_offset + PVD_version_size) #define PVD_reserved1_size 1 #define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size) #define PVD_system_id_size 32 #define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size) #define PVD_volume_id_size 32 #define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size) #define PVD_reserved2_size 8 #define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size) #define PVD_volume_space_size_size 8 #define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size) #define PVD_reserved3_size 32 #define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size) #define PVD_volume_set_size_size 4 #define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size) #define PVD_volume_sequence_number_size 4 #define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size) #define PVD_logical_block_size_size 4 #define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size) #define PVD_path_table_size_size 8 #define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size) #define PVD_type_1_path_table_size 4 #define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size) #define PVD_opt_type_1_path_table_size 4 #define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size) #define PVD_type_m_path_table_size 4 #define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size) #define PVD_opt_type_m_path_table_size 4 #define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size) #define PVD_root_directory_record_size 34 #define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size) #define PVD_volume_set_id_size 128 #define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size) #define PVD_publisher_id_size 128 #define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size) #define PVD_preparer_id_size 128 #define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size) #define PVD_application_id_size 128 #define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size) #define PVD_copyright_file_id_size 37 #define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size) #define PVD_abstract_file_id_size 37 #define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size) #define PVD_bibliographic_file_id_size 37 #define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size) #define PVD_creation_date_size 17 #define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size) #define PVD_modification_date_size 17 #define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size) #define PVD_expiration_date_size 17 #define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size) #define PVD_effective_date_size 17 #define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size) #define PVD_file_structure_version_size 1 #define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size) #define PVD_reserved4_size 1 #define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size) #define PVD_application_data_size 512 #define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size) #define PVD_reserved5_size (2048 - PVD_reserved5_offset) /* TODO: It would make future maintenance easier to just hardcode the * above values. In particular, ECMA119 states the offsets as part of * the standard. That would eliminate the need for the following check.*/ #if PVD_reserved5_offset != 1395 #error PVD offset and size definitions are wrong. #endif /* Structure of optional on-disk supplementary volume descriptor. */ #define SVD_type_offset 0 #define SVD_type_size 1 #define SVD_id_offset (SVD_type_offset + SVD_type_size) #define SVD_id_size 5 #define SVD_version_offset (SVD_id_offset + SVD_id_size) #define SVD_version_size 1 /* ... */ #define SVD_volume_space_size_offset 80 #define SVD_volume_space_size_size 8 #define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size) #define SVD_escape_sequences_size 32 /* ... */ #define SVD_logical_block_size_offset 128 #define SVD_logical_block_size_size 4 /* ... */ #define SVD_root_directory_record_offset 156 #define SVD_root_directory_record_size 34 /* ... */ /* FIXME: validate correctness of last SVD entry offset. */ /* Structure of an on-disk directory record. */ /* Note: ISO9660 stores each multi-byte integer twice, once in * each byte order. The sizes here are the size of just one * of the two integers. (This is why the offset of a field isn't * the same as the offset+size of the previous field.) */ #define DR_length_offset 0 #define DR_length_size 1 #define DR_ext_attr_length_offset 1 #define DR_ext_attr_length_size 1 #define DR_extent_offset 2 #define DR_extent_size 4 #define DR_size_offset 10 #define DR_size_size 4 #define DR_date_offset 18 #define DR_date_size 7 #define DR_flags_offset 25 #define DR_flags_size 1 #define DR_file_unit_size_offset 26 #define DR_file_unit_size_size 1 #define DR_interleave_offset 27 #define DR_interleave_size 1 #define DR_volume_sequence_number_offset 28 #define DR_volume_sequence_number_size 2 #define DR_name_len_offset 32 #define DR_name_len_size 1 #define DR_name_offset 33 /* In-memory storage for a directory record. */ struct file_info { struct file_info *parent; int refcount; uint64_t offset; /* Offset on disk. */ uint64_t size; /* File size in bytes. */ uint64_t ce_offset; /* Offset of CE */ uint64_t ce_size; /* Size of CE */ time_t birthtime; /* File created time. */ time_t mtime; /* File last modified time. */ time_t atime; /* File last accessed time. */ time_t ctime; /* File attribute change time. */ uint64_t rdev; /* Device number */ mode_t mode; uid_t uid; gid_t gid; ino_t inode; int nlinks; struct archive_string name; /* Pathname */ char name_continues; /* Non-zero if name continues */ struct archive_string symlink; char symlink_continues; /* Non-zero if link continues */ }; struct iso9660 { int magic; #define ISO9660_MAGIC 0x96609660 int option_ignore_joliet; struct archive_string pathname; char seenRockridge; /* Set true if RR extensions are used. */ unsigned char suspOffset; char seenJoliet; uint64_t previous_offset; uint64_t previous_size; struct archive_string previous_pathname; /* TODO: Make this a heap for fast inserts and deletions. */ struct file_info **pending_files; int pending_files_allocated; int pending_files_used; uint64_t current_position; ssize_t logical_block_size; uint64_t volume_size; /* Total size of volume in bytes. */ off_t entry_sparse_offset; int64_t entry_bytes_remaining; }; static void add_entry(struct iso9660 *iso9660, struct file_info *file); static int archive_read_format_iso9660_bid(struct archive_read *); static int archive_read_format_iso9660_options(struct archive_read *, const char *, const char *); static int archive_read_format_iso9660_cleanup(struct archive_read *); static int archive_read_format_iso9660_read_data(struct archive_read *, const void **, size_t *, off_t *); static int archive_read_format_iso9660_read_data_skip(struct archive_read *); static int archive_read_format_iso9660_read_header(struct archive_read *, struct archive_entry *); static const char *build_pathname(struct archive_string *, struct file_info *); #if DEBUG static void dump_isodirrec(FILE *, const unsigned char *isodirrec); #endif static time_t time_from_tm(struct tm *); static time_t isodate17(const unsigned char *); static time_t isodate7(const unsigned char *); static int isJolietSVD(struct iso9660 *, const unsigned char *); static int isPVD(struct iso9660 *, const unsigned char *); static struct file_info *next_entry(struct iso9660 *); static int next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, struct file_info **pfile); static struct file_info * parse_file_info(struct iso9660 *iso9660, struct file_info *parent, const unsigned char *isodirrec); static void parse_rockridge(struct iso9660 *iso9660, struct file_info *file, const unsigned char *start, const unsigned char *end); static void parse_rockridge_NM1(struct file_info *, const unsigned char *, int); static void parse_rockridge_SL1(struct file_info *, const unsigned char *, int); static void parse_rockridge_TF1(struct file_info *, const unsigned char *, int); static void release_file(struct iso9660 *, struct file_info *); static unsigned toi(const void *p, int n); int archive_read_support_format_iso9660(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct iso9660 *iso9660; int r; iso9660 = (struct iso9660 *)malloc(sizeof(*iso9660)); if (iso9660 == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } memset(iso9660, 0, sizeof(*iso9660)); iso9660->magic = ISO9660_MAGIC; r = __archive_read_register_format(a, iso9660, "iso9660", archive_read_format_iso9660_bid, archive_read_format_iso9660_options, archive_read_format_iso9660_read_header, archive_read_format_iso9660_read_data, NULL, NULL, archive_read_format_iso9660_read_data_skip, archive_read_format_iso9660_cleanup); if (r != ARCHIVE_OK) { free(iso9660); return (r); } return (ARCHIVE_OK); } static int archive_read_format_iso9660_bid(struct archive_read *a) { struct iso9660 *iso9660; ssize_t bytes_read, brsvd; const void *h; const unsigned char *p, *psvd; int bid; iso9660 = (struct iso9660 *)(a->format->data); /* * Skip the first 32k (reserved area) and get the first * 8 sectors of the volume descriptor table. Of course, * if the I/O layer gives us more, we'll take it. */ h = __archive_read_ahead(a, 32768 + 8*2048, &bytes_read); if (h == NULL) return (-1); p = (const unsigned char *)h; /* Skip the reserved area. */ bytes_read -= 32768; p += 32768; /* Check each volume descriptor to locate possible SVD with Joliet. */ for (brsvd = bytes_read, psvd = p; !iso9660->option_ignore_joliet && brsvd > 2048; brsvd -= 2048, psvd += 2048) { bid = isJolietSVD(iso9660, psvd); if (bid > 0) return (bid); if (*p == '\177') /* End-of-volume-descriptor marker. */ break; } /* Check each volume descriptor to locate the PVD. */ for (; bytes_read > 2048; bytes_read -= 2048, p += 2048) { bid = isPVD(iso9660, p); if (bid > 0) return (bid); if (*p == '\177') /* End-of-volume-descriptor marker. */ break; } /* We didn't find a valid PVD; return a bid of zero. */ return (0); } static int archive_read_format_iso9660_options(struct archive_read *a, const char *key, const char *val) { struct iso9660 *iso9660; iso9660 = (struct iso9660 *)(a->format->data); if (strcmp(key, "joliet") == 0) { if (val == NULL || strcmp(val, "off") == 0 || strcmp(val, "ignore") == 0 || strcmp(val, "disable") == 0 || strcmp(val, "0") == 0) iso9660->option_ignore_joliet = 1; else iso9660->option_ignore_joliet = 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); } static int isJolietSVD(struct iso9660 *iso9660, const unsigned char *h) { struct file_info *file; const unsigned char *p; /* Type 2 means it's a SVD. */ if (h[SVD_type_offset] != 2) return (0); /* ID must be "CD001" */ if (memcmp(h + SVD_id_offset, "CD001", 5) != 0) return (0); /* FIXME: do more validations according to joliet spec. */ /* check if this SVD contains joliet extension! */ p = h + SVD_escape_sequences_offset; /* N.B. Joliet spec says p[1] == '\\', but.... */ if (p[0] == '%' && p[1] == '/') { int level = 0; if (p[2] == '@') level = 1; else if (p[2] == 'C') level = 2; else if (p[2] == 'E') level = 3; else /* not joliet */ return (0); iso9660->seenJoliet = level; } else /* not joliet */ return (0); iso9660->logical_block_size = toi(h + SVD_logical_block_size_offset, 2); if (iso9660->logical_block_size <= 0) return (0); iso9660->volume_size = iso9660->logical_block_size * (uint64_t)toi(h + SVD_volume_space_size_offset, 4); /* Store the root directory in the pending list. */ file = parse_file_info(iso9660, NULL, h + SVD_root_directory_record_offset); add_entry(iso9660, file); return (48); } static int isPVD(struct iso9660 *iso9660, const unsigned char *h) { struct file_info *file; int i; /* Type of the Primary Volume Descriptor must be 1. */ if (h[PVD_type_offset] != 1) return (0); /* ID must be "CD001" */ if (memcmp(h + PVD_id_offset, "CD001", 5) != 0) return (0); /* PVD version must be 1. */ if (h[PVD_version_offset] != 1) return (0); /* Reserved field must be 0. */ if (h[PVD_reserved1_offset] != 0) return (0); /* Reserved field must be 0. */ for (i = 0; i < PVD_reserved2_size; ++i) if (h[PVD_reserved2_offset + i] != 0) return (0); /* Reserved field must be 0. */ for (i = 0; i < PVD_reserved3_size; ++i) if (h[PVD_reserved3_offset + i] != 0) return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger * restriction on this field. */ iso9660->logical_block_size = toi(h + PVD_logical_block_size_offset, 2); if (iso9660->logical_block_size <= 0) return (0); iso9660->volume_size = iso9660->logical_block_size * (uint64_t)toi(h + PVD_volume_space_size_offset, 4); /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[PVD_file_structure_version_offset] != 1) return (0); /* Reserved field must be 0. */ for (i = 0; i < PVD_reserved4_size; ++i) if (h[PVD_reserved4_offset + i] != 0) return (0); /* Reserved field must be 0. */ for (i = 0; i < PVD_reserved5_size; ++i) if (h[PVD_reserved5_offset + i] != 0) return (0); /* XXX TODO: Check other values for sanity; reject more * malformed PVDs. XXX */ /* Store the root directory in the pending list. */ file = parse_file_info(iso9660, NULL, h + PVD_root_directory_record_offset); add_entry(iso9660, file); return (48); } static int archive_read_format_iso9660_read_header(struct archive_read *a, struct archive_entry *entry) { struct iso9660 *iso9660; struct file_info *file; int r; iso9660 = (struct iso9660 *)(a->format->data); if (!a->archive.archive_format) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; } /* Get the next entry that appears after the current offset. */ r = next_entry_seek(a, iso9660, &file); if (r != ARCHIVE_OK) { release_file(iso9660, file); return (r); } iso9660->entry_bytes_remaining = file->size; iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */ if (file->offset + file->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File is beyond end-of-media: %s", file->name); iso9660->entry_bytes_remaining = 0; iso9660->entry_sparse_offset = 0; release_file(iso9660, file); return (ARCHIVE_WARN); } /* Set up the entry structure with information about this entry. */ archive_entry_set_mode(entry, file->mode); archive_entry_set_uid(entry, file->uid); archive_entry_set_gid(entry, file->gid); archive_entry_set_nlink(entry, file->nlinks); archive_entry_set_ino(entry, file->inode); archive_entry_set_birthtime(entry, file->birthtime, 0); archive_entry_set_mtime(entry, file->mtime, 0); archive_entry_set_ctime(entry, file->ctime, 0); archive_entry_set_atime(entry, file->atime, 0); /* N.B.: Rock Ridge supports 64-bit device numbers. */ archive_entry_set_rdev(entry, (dev_t)file->rdev); archive_entry_set_size(entry, iso9660->entry_bytes_remaining); archive_string_empty(&iso9660->pathname); archive_entry_set_pathname(entry, build_pathname(&iso9660->pathname, file)); if (file->symlink.s != NULL) archive_entry_copy_symlink(entry, file->symlink.s); /* If this entry points to the same data as the previous * entry, convert this into a hardlink to that entry. * But don't bother for zero-length files. */ if (file->offset == iso9660->previous_offset && file->size == iso9660->previous_size && file->size > 0) { archive_entry_set_hardlink(entry, iso9660->previous_pathname.s); iso9660->entry_bytes_remaining = 0; iso9660->entry_sparse_offset = 0; release_file(iso9660, file); return (ARCHIVE_OK); } /* If the offset is before our current position, we can't * seek backwards to extract it, so issue a warning. */ if (file->offset < iso9660->current_position) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order file @%x (%s) %jd < %jd", file, iso9660->pathname.s, file->offset, iso9660->current_position); iso9660->entry_bytes_remaining = 0; iso9660->entry_sparse_offset = 0; release_file(iso9660, file); return (ARCHIVE_WARN); } iso9660->previous_size = file->size; iso9660->previous_offset = file->offset; archive_strcpy(&iso9660->previous_pathname, iso9660->pathname.s); /* If this is a directory, read in all of the entries right now. */ if (archive_entry_filetype(entry) == AE_IFDIR) { while (iso9660->entry_bytes_remaining > 0) { const void *block; const unsigned char *p; ssize_t step = iso9660->logical_block_size; if (step > iso9660->entry_bytes_remaining) step = iso9660->entry_bytes_remaining; block = __archive_read_ahead(a, step, NULL); if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning ISO9660 directory list"); release_file(iso9660, file); return (ARCHIVE_FATAL); } __archive_read_consume(a, step); iso9660->current_position += step; iso9660->entry_bytes_remaining -= step; for (p = (const unsigned char *)block; *p != 0 && p < (const unsigned char *)block + step; p += *p) { struct file_info *child; /* N.B.: these special directory identifiers * are 8 bit "values" even on a * Joliet CD with UCS-2 (16bit) encoding. */ /* Skip '.' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\0') continue; /* Skip '..' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\001') continue; child = parse_file_info(iso9660, file, p); add_entry(iso9660, child); if (iso9660->seenRockridge) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE; a->archive.archive_format_name = "ISO9660 with Rockridge extensions"; } } } } release_file(iso9660, file); return (ARCHIVE_OK); } static int archive_read_format_iso9660_read_data_skip(struct archive_read *a) { /* Because read_next_header always does an explicit skip * to the next entry, we don't need to do anything here. */ (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int archive_read_format_iso9660_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { ssize_t bytes_read; struct iso9660 *iso9660; iso9660 = (struct iso9660 *)(a->format->data); if (iso9660->entry_bytes_remaining <= 0) { *buff = NULL; *size = 0; *offset = iso9660->entry_sparse_offset; return (ARCHIVE_EOF); } *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); if (*buff == NULL) return (ARCHIVE_FATAL); if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = iso9660->entry_bytes_remaining; *size = bytes_read; *offset = iso9660->entry_sparse_offset; iso9660->entry_sparse_offset += bytes_read; iso9660->entry_bytes_remaining -= bytes_read; iso9660->current_position += bytes_read; __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } static int archive_read_format_iso9660_cleanup(struct archive_read *a) { struct iso9660 *iso9660; struct file_info *file; iso9660 = (struct iso9660 *)(a->format->data); while ((file = next_entry(iso9660)) != NULL) release_file(iso9660, file); archive_string_free(&iso9660->pathname); archive_string_free(&iso9660->previous_pathname); if (iso9660->pending_files) free(iso9660->pending_files); free(iso9660); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * This routine parses a single ISO directory record, makes sense * of any extensions, and stores the result in memory. */ static struct file_info * parse_file_info(struct iso9660 *iso9660, struct file_info *parent, const unsigned char *isodirrec) { struct file_info *file; size_t name_len; const unsigned char *rr_start, *rr_end; const unsigned char *p; int flags; /* TODO: Sanity check that name_len doesn't exceed length, etc. */ /* Create a new file entry and copy data from the ISO dir record. */ file = (struct file_info *)malloc(sizeof(*file)); if (file == NULL) return (NULL); memset(file, 0, sizeof(*file)); file->parent = parent; if (parent != NULL) parent->refcount++; file->offset = (uint64_t)toi(isodirrec + DR_extent_offset, DR_extent_size) * iso9660->logical_block_size; file->size = toi(isodirrec + DR_size_offset, DR_size_size); file->mtime = isodate7(isodirrec + DR_date_offset); file->ctime = file->atime = file->mtime; name_len = (size_t)isodirrec[DR_name_len_offset]; p = isodirrec + DR_name_offset; /* Rockridge extensions (if any) follow name. Compute this * before fidgeting the name_len below. */ rr_start = p + name_len + (name_len & 1 ? 0 : 1) + iso9660->suspOffset; rr_end = isodirrec + isodirrec[DR_length_offset]; if (iso9660->seenJoliet) { /* Joliet names are max 64 chars (128 bytes) according to spec, * but genisoimage (and others?) will allow you to have more. */ wchar_t wbuff[64+1], *wp; const unsigned char *c; /* TODO: warn when name_len > 128 ? */ /* convert BE UTF-16 to wchar_t */ for (c = p, wp = wbuff; c < (p + name_len) && wp < (wbuff + sizeof(wbuff)/sizeof(*wbuff) - 1); c += 2) { *wp++ = (((255 & (int)c[0]) << 8) | (255 & (int)c[1])); } *wp = L'\0'; #if 0 /* untested code, is it at all useful on Joliet? */ /* trim trailing first version and dot from filename. * * Remember we were in UTF-16BE land! * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both * 16 bits big endian characters on Joliet. * * TODO: sanitize filename? * Joliet allows any UCS-2 char except: * *, /, :, ;, ? and \. */ /* Chop off trailing ';1' from files. */ if (*(wp-2) == ';' && *(wp-1) == '1') { wp-=2; *wp = L'\0'; } /* Chop off trailing '.' from filenames. */ if (*(wp-1) == '.') *(--wp) = L'\0'; #endif /* store the result in the file name field. */ archive_strappend_w_utf8(&file->name, wbuff); } else { /* Chop off trailing ';1' from files. */ if (name_len > 2 && p[name_len - 2] == ';' && p[name_len - 1] == '1') name_len -= 2; /* Chop off trailing '.' from filenames. */ if (name_len > 1 && p[name_len - 1] == '.') --name_len; archive_strncpy(&file->name, (const char *)p, name_len); } flags = isodirrec[DR_flags_offset]; if (flags & 0x02) file->mode = AE_IFDIR | 0700; else file->mode = AE_IFREG | 0400; /* Rockridge extensions overwrite information from above. */ parse_rockridge(iso9660, file, rr_start, rr_end); #if DEBUG /* DEBUGGING: Warn about attributes I don't yet fully support. */ if ((flags & ~0x02) != 0) { fprintf(stderr, "\n ** Unrecognized flag: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) { fprintf(stderr, "\n ** Unrecognized sequence number: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_file_unit_size_offset) != 0) { fprintf(stderr, "\n ** Unexpected file unit size: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_interleave_offset) != 0) { fprintf(stderr, "\n ** Unexpected interleave: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_ext_attr_length_offset) != 0) { fprintf(stderr, "\n ** Unexpected extended attribute length: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } #endif return (file); } static void add_entry(struct iso9660 *iso9660, struct file_info *file) { /* Expand our pending files list as necessary. */ if (iso9660->pending_files_used >= iso9660->pending_files_allocated) { struct file_info **new_pending_files; int new_size = iso9660->pending_files_allocated * 2; if (iso9660->pending_files_allocated < 1024) new_size = 1024; /* Overflow might keep us from growing the list. */ if (new_size <= iso9660->pending_files_allocated) __archive_errx(1, "Out of memory"); new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) __archive_errx(1, "Out of memory"); memcpy(new_pending_files, iso9660->pending_files, iso9660->pending_files_allocated * sizeof(new_pending_files[0])); if (iso9660->pending_files != NULL) free(iso9660->pending_files); iso9660->pending_files = new_pending_files; iso9660->pending_files_allocated = new_size; } iso9660->pending_files[iso9660->pending_files_used++] = file; } static void parse_rockridge(struct iso9660 *iso9660, struct file_info *file, const unsigned char *p, const unsigned char *end) { (void)iso9660; /* UNUSED */ file->name_continues = 0; file->symlink_continues = 0; while (p + 4 < end /* Enough space for another entry. */ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */ && p[2] >= 4 /* Sanity-check length. */ && p + p[2] <= end) { /* Sanity-check length. */ const unsigned char *data = p + 4; int data_length = p[2] - 4; int version = p[3]; /* * Yes, each 'if' here does test p[0] again. * Otherwise, the fall-through handling to catch * unsupported extensions doesn't work. */ switch(p[0]) { case 'C': if (p[0] == 'C' && p[1] == 'E') { if (version == 1 && data_length == 24) { /* * CE extension comprises: * 8 byte sector containing extension * 8 byte offset w/in above sector * 8 byte length of continuation */ file->ce_offset = (uint64_t)toi(data, 4) * iso9660->logical_block_size + toi(data + 8, 4); file->ce_size = toi(data + 16, 4); /* If the result is ridiculous, * ignore it. */ if (file->ce_offset + file->ce_size > iso9660->volume_size) { file->ce_offset = 0; file->ce_size = 0; } } break; } /* FALLTHROUGH */ case 'N': if (p[0] == 'N' && p[1] == 'M') { if (version == 1) parse_rockridge_NM1(file, data, data_length); break; } /* FALLTHROUGH */ case 'P': if (p[0] == 'P' && p[1] == 'D') { /* * PD extension is padding; * contents are always ignored. */ break; } if (p[0] == 'P' && p[1] == 'N') { if (version == 1 && data_length == 16) { file->rdev = toi(data,4); file->rdev <<= 32; file->rdev |= toi(data + 8, 4); } break; } if (p[0] == 'P' && p[1] == 'X') { /* * PX extension comprises: * 8 bytes for mode, * 8 bytes for nlinks, * 8 bytes for uid, * 8 bytes for gid, * 8 bytes for inode. */ if (version == 1) { if (data_length >= 8) file->mode = toi(data, 4); if (data_length >= 16) file->nlinks = toi(data + 8, 4); if (data_length >= 24) file->uid = toi(data + 16, 4); if (data_length >= 32) file->gid = toi(data + 24, 4); if (data_length >= 40) file->inode = toi(data + 32, 4); } break; } /* FALLTHROUGH */ case 'R': if (p[0] == 'R' && p[1] == 'R' && version == 1) { iso9660->seenRockridge = 1; /* * RR extension comprises: * one byte flag value */ /* TODO: Handle RR extension. */ break; } /* FALLTHROUGH */ case 'S': if (p[0] == 'S' && p[1] == 'L') { if (version == 1) parse_rockridge_SL1(file, data, data_length); break; } if (p[0] == 'S' && p[1] == 'P' && version == 1 && data_length == 3 && data[0] == (unsigned char)'\xbe' && data[1] == (unsigned char)'\xef') { /* * SP extension stores the suspOffset * (Number of bytes to skip between * filename and SUSP records.) * It is mandatory by the SUSP standard * (IEEE 1281). * * It allows SUSP to coexist with * non-SUSP uses of the System * Use Area by placing non-SUSP data * before SUSP data. * * TODO: Add a check for 'SP' in * first directory entry, disable all SUSP * processing if not found. */ iso9660->suspOffset = data[2]; break; } if (p[0] == 'S' && p[1] == 'T' && data_length == 0 && version == 1) { /* * ST extension marks end of this * block of SUSP entries. * * It allows SUSP to coexist with * non-SUSP uses of the System * Use Area by placing non-SUSP data * after SUSP data. */ return; } case 'T': if (p[0] == 'T' && p[1] == 'F') { if (version == 1) parse_rockridge_TF1(file, data, data_length); break; } /* FALLTHROUGH */ default: /* The FALLTHROUGHs above leave us here for * any unsupported extension. */ #if DEBUG { const unsigned char *t; fprintf(stderr, "\nUnsupported RRIP extension for %s\n", file->name.s); fprintf(stderr, " %c%c(%d):", p[0], p[1], data_length); for (t = data; t < data + data_length && t < data + 16; t++) fprintf(stderr, " %02x", *t); fprintf(stderr, "\n"); } #endif break; } p += p[2]; } } static void parse_rockridge_NM1(struct file_info *file, const unsigned char *data, int data_length) { if (!file->name_continues) archive_string_empty(&file->name); file->name_continues = 0; if (data_length < 1) return; /* * NM version 1 extension comprises: * 1 byte flag, value is one of: * = 0: remainder is name * = 1: remainder is name, next NM entry continues name * = 2: "." * = 4: ".." * = 32: Implementation specific * All other values are reserved. */ switch(data[0]) { case 0: if (data_length < 2) return; archive_strncat(&file->name, (const char *)data + 1, data_length - 1); break; case 1: if (data_length < 2) return; archive_strncat(&file->name, (const char *)data + 1, data_length - 1); file->name_continues = 1; break; case 2: archive_strcat(&file->name, "."); break; case 4: archive_strcat(&file->name, ".."); break; default: return; } } static void parse_rockridge_TF1(struct file_info *file, const unsigned char *data, int data_length) { char flag; /* * TF extension comprises: * one byte flag * create time (optional) * modify time (optional) * access time (optional) * attribute time (optional) * Time format and presence of fields * is controlled by flag bits. */ if (data_length < 1) return; flag = data[0]; ++data; --data_length; if (flag & 0x80) { /* Use 17-byte time format. */ if ((flag & 1) && data_length >= 17) { /* Create time. */ file->birthtime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 2) && data_length >= 17) { /* Modify time. */ file->mtime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 4) && data_length >= 17) { /* Access time. */ file->atime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 8) && data_length >= 17) { /* Attribute change time. */ file->ctime = isodate17(data); } } else { /* Use 7-byte time format. */ if ((flag & 1) && data_length >= 7) { /* Create time. */ file->birthtime = isodate17(data); data += 7; data_length -= 7; } if ((flag & 2) && data_length >= 7) { /* Modify time. */ file->mtime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 4) && data_length >= 7) { /* Access time. */ file->atime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 8) && data_length >= 7) { /* Attribute change time. */ file->ctime = isodate7(data); } } } static void parse_rockridge_SL1(struct file_info *file, const unsigned char *data, int data_length) { int component_continues = 1; if (!file->symlink_continues) archive_string_empty(&file->symlink); else archive_strcat(&file->symlink, "/"); file->symlink_continues = 0; /* * Defined flag values: * 0: This is the last SL record for this symbolic link * 1: this symbolic link field continues in next SL entry * All other values are reserved. */ if (data_length < 1) return; switch(*data) { case 0: break; case 1: file->symlink_continues = 1; break; default: return; } ++data; /* Skip flag byte. */ --data_length; /* * SL extension body stores "components". * Basically, this is a complicated way of storing * a POSIX path. It also interferes with using * symlinks for storing non-path data. * * Each component is 2 bytes (flag and length) * possibly followed by name data. */ while (data_length >= 2) { unsigned char flag = *data++; unsigned char nlen = *data++; data_length -= 2; if (!component_continues) archive_strcat(&file->symlink, "/"); component_continues = 0; switch(flag) { case 0: /* Usual case, this is text. */ if (data_length < nlen) return; archive_strncat(&file->symlink, (const char *)data, nlen); break; case 0x01: /* Text continues in next component. */ if (data_length < nlen) return; archive_strncat(&file->symlink, (const char *)data, nlen); component_continues = 1; break; case 0x02: /* Current dir. */ archive_strcat(&file->symlink, "."); break; case 0x04: /* Parent dir. */ archive_strcat(&file->symlink, ".."); break; case 0x08: /* Root of filesystem. */ archive_string_empty(&file->symlink); archive_strcat(&file->symlink, "/"); break; case 0x10: /* Undefined (historically "volume root" */ archive_string_empty(&file->symlink); archive_strcat(&file->symlink, "ROOT"); break; case 0x20: /* Undefined (historically "hostname") */ archive_strcat(&file->symlink, "hostname"); break; default: /* TODO: issue a warning ? */ return; } data += nlen; data_length -= nlen; } } static void release_file(struct iso9660 *iso9660, struct file_info *file) { struct file_info *parent; if (file == NULL) return; if (file->refcount == 0) { parent = file->parent; archive_string_free(&file->name); archive_string_free(&file->symlink); free(file); if (parent != NULL) { parent->refcount--; release_file(iso9660, parent); } } } static int next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, struct file_info **pfile) { struct file_info *file; uint64_t offset; *pfile = NULL; for (;;) { *pfile = file = next_entry(iso9660); if (file == NULL) return (ARCHIVE_EOF); /* CE area precedes actual file data? Ignore it. */ if (file->ce_offset > file->offset) { /* fprintf(stderr, " *** Discarding CE data.\n"); */ file->ce_offset = 0; file->ce_size = 0; } /* Don't waste time seeking for zero-length bodies. */ if (file->size == 0) { file->offset = iso9660->current_position; } /* If CE exists, find and read it now. */ if (file->ce_offset > 0) offset = file->ce_offset; else offset = file->offset; /* Seek forward to the start of the entry. */ if (iso9660->current_position < offset) { off_t step = offset - iso9660->current_position; off_t bytes_read; bytes_read = __archive_read_skip(a, step); if (bytes_read < 0) return (bytes_read); iso9660->current_position = offset; } /* We found body of file; handle it now. */ if (offset == file->offset) return (ARCHIVE_OK); /* Found CE? Process it and push the file back onto list. */ if (offset == file->ce_offset) { const void *p; ssize_t size = file->ce_size; const unsigned char *rr_start; file->ce_offset = 0; file->ce_size = 0; p = __archive_read_ahead(a, size, NULL); if (p == NULL) return (ARCHIVE_FATAL); rr_start = (const unsigned char *)p; parse_rockridge(iso9660, file, rr_start, rr_start + size); __archive_read_consume(a, size); iso9660->current_position += size; add_entry(iso9660, file); } } } static struct file_info * next_entry(struct iso9660 *iso9660) { int least_index; uint64_t least_end_offset; int i; struct file_info *r; if (iso9660->pending_files_used < 1) return (NULL); /* Assume the first file in the list is the earliest on disk. */ least_index = 0; least_end_offset = iso9660->pending_files[0]->offset + iso9660->pending_files[0]->size; /* Now, try to find an earlier one. */ for (i = 0; i < iso9660->pending_files_used; i++) { /* Use the position of the file *end* as our comparison. */ uint64_t end_offset = iso9660->pending_files[i]->offset + iso9660->pending_files[i]->size; if (iso9660->pending_files[i]->ce_offset > 0 && iso9660->pending_files[i]->ce_offset < iso9660->pending_files[i]->offset) end_offset = iso9660->pending_files[i]->ce_offset + iso9660->pending_files[i]->ce_size; if (least_end_offset > end_offset) { least_index = i; least_end_offset = end_offset; } } r = iso9660->pending_files[least_index]; iso9660->pending_files[least_index] = iso9660->pending_files[--iso9660->pending_files_used]; return (r); } static unsigned int toi(const void *p, int n) { const unsigned char *v = (const unsigned char *)p; if (n > 1) return v[0] + 256 * toi(v + 1, n - 1); if (n == 1) return v[0]; return (0); } static time_t isodate7(const unsigned char *v) { struct tm tm; int offset; memset(&tm, 0, sizeof(tm)); tm.tm_year = v[0]; tm.tm_mon = v[1] - 1; tm.tm_mday = v[2]; tm.tm_hour = v[3]; tm.tm_min = v[4]; tm.tm_sec = v[5]; /* v[6] is the signed timezone offset, in 1/4-hour increments. */ offset = ((const signed char *)v)[6]; if (offset > -48 && offset < 52) { tm.tm_hour -= offset / 4; tm.tm_min -= (offset % 4) * 15; } return (time_from_tm(&tm)); } static time_t isodate17(const unsigned char *v) { struct tm tm; int offset; memset(&tm, 0, sizeof(tm)); tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] - '0') - 1900; tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0'); tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0'); tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0'); tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0'); tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0'); /* v[16] is the signed timezone offset, in 1/4-hour increments. */ offset = ((const signed char *)v)[16]; if (offset > -48 && offset < 52) { tm.tm_hour -= offset / 4; tm.tm_min -= (offset % 4) * 15; } return (time_from_tm(&tm)); } static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ mktime(t); /* Then we can compute timegm() from first principles. */ return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000 + ((t->tm_year - 69) / 4) * 86400 - ((t->tm_year - 1) / 100) * 86400 + ((t->tm_year + 299) / 400) * 86400); #endif } static const char * build_pathname(struct archive_string *as, struct file_info *file) { if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) { build_pathname(as, file->parent); archive_strcat(as, "/"); } if (archive_strlen(&file->name) == 0) archive_strcat(as, "."); else archive_string_concat(as, &file->name); return (as->s); } #if DEBUG static void dump_isodirrec(FILE *out, const unsigned char *isodirrec) { fprintf(out, " l %d,", toi(isodirrec + DR_length_offset, DR_length_size)); fprintf(out, " a %d,", toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size)); fprintf(out, " ext 0x%x,", toi(isodirrec + DR_extent_offset, DR_extent_size)); fprintf(out, " s %d,", toi(isodirrec + DR_size_offset, DR_extent_size)); fprintf(out, " f 0x%02x,", toi(isodirrec + DR_flags_offset, DR_flags_size)); fprintf(out, " u %d,", toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size)); fprintf(out, " ilv %d,", toi(isodirrec + DR_interleave_offset, DR_interleave_size)); fprintf(out, " seq %d,", toi(isodirrec + DR_volume_sequence_number_offset, DR_volume_sequence_number_size)); fprintf(out, " nl %d:", toi(isodirrec + DR_name_len_offset, DR_name_len_size)); fprintf(out, " `%.*s'", toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset); } #endif tarsnap-1.0.41/libarchive/archive_read_support_format_mtree.c000066400000000000000000000767441476705112600245300ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_mtree.c,v 1.11 2008/12/06 06:45:15 kientzle Exp $"); #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" #ifndef O_BINARY #define O_BINARY 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 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; off_t offset; int fd; int filetype; 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; off_t cur_size; }; static int cleanup(struct archive_read *); static int mtree_bid(struct archive_read *); 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, off_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 **); 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; 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, NULL, read_header, read_data, NULL, NULL, skip, cleanup); 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 int mtree_bid(struct archive_read *a) { const char *signature = "#mtree"; const char *p; /* 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 (strncmp(p, signature, strlen(signature)) == 0) return (8 * strlen(signature)); 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 *option; if ((option = malloc(sizeof(*option))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } if ((option->value = malloc(len + 1)) == NULL) { free(option); archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(option->value, value, len); option->value[len] = '\0'; option->next = *global; *global = option; 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, struct mtree_entry **last_entry) { struct mtree_entry *entry; struct mtree_option *iter; const char *next, *eq; size_t len; int r; 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; len = strcspn(line, " \t\r\n"); if ((entry->name = malloc(len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(entry->name, line, len); entry->name[len] = '\0'; parse_escapes(entry->name, entry); line += len; 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); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (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; mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; global = NULL; last_entry = NULL; for (counter = 1; ; ++counter) { len = readline(a, mtree, &p, 256); if (len == 0) { mtree->this_entry = mtree->entries; free_options(global); return (ARCHIVE_OK); } if (len < 0) { free_options(global); return (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, &last_entry); } 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, mismatched_type; mentry->used = 1; /* Initialize reasonable defaults. */ mtree->filetype = AE_IFREG; archive_entry_set_size(entry, 0); /* 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; } /* * 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); 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; } /* * If there is a contents file on disk, use that size; * otherwise leave it as-is (it might have been set from * the mtree size= keyword). */ if (st != NULL) { mismatched_type = 0; if ((st->st_mode & S_IFMT) == S_IFREG && archive_entry_filetype(entry) != AE_IFREG) mismatched_type = 1; if ((st->st_mode & S_IFMT) == S_IFLNK && archive_entry_filetype(entry) != AE_IFLNK) mismatched_type = 1; if ((st->st_mode & S_IFSOCK) == S_IFSOCK && archive_entry_filetype(entry) != AE_IFSOCK) mismatched_type = 1; if ((st->st_mode & S_IFMT) == S_IFCHR && archive_entry_filetype(entry) != AE_IFCHR) mismatched_type = 1; if ((st->st_mode & S_IFMT) == S_IFBLK && archive_entry_filetype(entry) != AE_IFBLK) mismatched_type = 1; if ((st->st_mode & S_IFMT) == S_IFDIR && archive_entry_filetype(entry) != AE_IFDIR) mismatched_type = 1; if ((st->st_mode & S_IFMT) == S_IFIFO && archive_entry_filetype(entry) != AE_IFIFO) mismatched_type = 1; if (mismatched_type) { if ((parsed_kws & MTREE_HAS_OPTIONAL) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "mtree specification has different type for %s", archive_entry_pathname(entry)); r = ARCHIVE_WARN; } else { *use_next = 1; } /* Don't hold a non-regular file open. */ if (mtree->fd >= 0) close(mtree->fd); mtree->fd = -1; st = NULL; return r; } } if (st != NULL) { if ((parsed_kws & MTREE_HAS_DEVICE) == 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) archive_entry_set_gid(entry, st->st_gid); if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0) archive_entry_set_uid(entry, st->st_uid); if ((parsed_kws & MTREE_HAS_MTIME) == 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) archive_entry_set_nlink(entry, st->st_nlink); if ((parsed_kws & MTREE_HAS_PERM) == 0) archive_entry_set_perm(entry, st->st_mode); if ((parsed_kws & MTREE_HAS_SIZE) == 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 ((*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] * * Just use major and minor, no translation etc is done * between formats. */ static int parse_device(struct archive *a, struct archive_entry *entry, char *val) { char *comma1, *comma2; comma1 = strchr(val, ','); if (comma1 == NULL) { archive_entry_set_dev(entry, mtree_atol10(&val)); return (ARCHIVE_OK); } ++comma1; comma2 = strchr(comma1, ','); if (comma2 == NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed device attribute"); return (ARCHIVE_WARN); } ++comma2; archive_entry_set_rdevmajor(entry, mtree_atol(&comma1)); archive_entry_set_rdevminor(entry, mtree_atol(&comma2)); return (ARCHIVE_OK); } /* * 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 *option, int *parsed_kws) { char *val, *key; key = option->value; if (*key == '\0') 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) { *parsed_kws |= MTREE_HAS_DEVICE; return parse_device(&a->archive, entry, val); } 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 '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, 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, mtree_atol10(&val)); break; } case '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) { time_t m; long ns; *parsed_kws |= MTREE_HAS_MTIME; m = (time_t)mtree_atol10(&val); if (*val == '.') { ++val; ns = (long)mtree_atol10(&val); } else ns = 0; archive_entry_set_mtime(entry, m, ns); break; } if (strcmp(key, "type") == 0) { *parsed_kws |= MTREE_HAS_TYPE; switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { mtree->filetype = AE_IFBLK; break; } case 'c': if (strcmp(val, "char") == 0) { mtree->filetype = AE_IFCHR; break; } case 'd': if (strcmp(val, "dir") == 0) { mtree->filetype = AE_IFDIR; break; } case 'f': if (strcmp(val, "fifo") == 0) { mtree->filetype = AE_IFIFO; break; } if (strcmp(val, "file") == 0) { mtree->filetype = AE_IFREG; break; } case 'l': if (strcmp(val, "link") == 0) { mtree->filetype = AE_IFLNK; break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized file type \"%s\"", val); return (ARCHIVE_WARN); } archive_entry_set_filetype(entry, mtree->filetype); 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, off_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 ((off_t)mtree->buffsize > mtree->cur_size - mtree->offset) bytes_to_read = 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; } } *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; const char *s; void *p; 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); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; } 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); } memcpy(mtree->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; /* Null terminate. */ mtree->line.s[total_size] = '\0'; /* If we found an unescaped '\n', clean up and return. */ for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { *start = mtree->line.s; return total_size; } if (u[0] == '#') { if (p == NULL) break; *start = mtree->line.s; return total_size; } if (u[0] != '\\') continue; if (u[1] == '\\') { ++u; continue; } if (u[1] == '\n') { memmove(u, u + 1, total_size - (u - mtree->line.s) + 1); --total_size; ++u; break; } if (u[1] == '\0') break; } find_off = u - mtree->line.s; } } tarsnap-1.0.41/libarchive/archive_read_support_format_tar.c000066400000000000000000002054411476705112600241660ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_read_support_format_tar.c,v 1.72 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #include /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif /* Obtain suitable wide-character manipulation functions. */ #ifdef HAVE_WCHAR_H #include #else /* Good enough for equality testing, which is all we need. */ static int wcscmp(const wchar_t *s1, const wchar_t *s2) { int diff = *s1 - *s2; while (*s1 && diff == 0) diff = (int)*++s1 - (int)*++s2; return diff; } /* Good enough for equality testing, which is all we need. */ static int wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t n) { int diff = *s1 - *s2; while (*s1 && diff == 0 && n-- > 0) diff = (int)*++s1 - (int)*++s2; return diff; } static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p) p++; return p - s; } #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #define tar_min(a,b) ((a) < (b) ? (a) : (b)) /* * Layout of POSIX 'ustar' tar header. */ struct archive_entry_header_ustar { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char checksum[8]; char typeflag[1]; char linkname[100]; /* "old format" header ends here */ char magic[6]; /* For POSIX: "ustar\0" */ char version[2]; /* For POSIX: "00" */ char uname[32]; char gname[32]; char rdevmajor[8]; char rdevminor[8]; char prefix[155]; }; /* * Structure of GNU tar header */ struct gnu_sparse { char offset[12]; char numbytes[12]; }; struct archive_entry_header_gnutar { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char checksum[8]; char typeflag[1]; char linkname[100]; char magic[8]; /* "ustar \0" (note blank/blank/null at end) */ char uname[32]; char gname[32]; char rdevmajor[8]; char rdevminor[8]; char atime[12]; char ctime[12]; char offset[12]; char longnames[4]; char unused[1]; struct gnu_sparse sparse[4]; char isextended[1]; char realsize[12]; /* * Old GNU format doesn't use POSIX 'prefix' field; they use * the 'L' (longname) entry instead. */ }; /* * Data specific to this format. */ struct sparse_block { struct sparse_block *next; off_t offset; off_t remaining; }; struct tar { struct archive_string acl_text; struct archive_string entry_pathname; /* For "GNU.sparse.name" and other similar path extensions. */ struct archive_string entry_pathname_override; struct archive_string entry_linkpath; struct archive_string entry_uname; struct archive_string entry_gname; struct archive_string longlink; struct archive_string longname; struct archive_string pax_header; struct archive_string pax_global; struct archive_string line; int pax_hdrcharset_binary; wchar_t *pax_entry; size_t pax_entry_length; int header_recursion_depth; off_t entry_bytes_remaining; off_t entry_offset; off_t entry_padding; off_t realsize; struct sparse_block *sparse_list; struct sparse_block *sparse_last; int64_t sparse_offset; int64_t sparse_numbytes; int sparse_gnu_major; int sparse_gnu_minor; char sparse_gnu_pending; }; static ssize_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n); static int archive_block_is_null(const unsigned char *p); static char *base64_decode(const char *, size_t, size_t *); static void gnu_add_sparse_entry(struct tar *, off_t offset, off_t remaining); static void gnu_clear_sparse_list(struct tar *); static int gnu_sparse_old_read(struct archive_read *, struct tar *, const struct archive_entry_header_gnutar *header); static void gnu_sparse_old_parse(struct tar *, const struct gnu_sparse *sparse, int length); static int gnu_sparse_01_parse(struct tar *, const char *); static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *); static int header_Solaris_ACL(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_common(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_old_tar(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_pax_extensions(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_pax_global(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_longlink(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_longname(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_volume(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_ustar(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_gnutar(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int archive_read_format_tar_bid(struct archive_read *); static int archive_read_format_tar_cleanup(struct archive_read *); static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset); static off_t archive_read_format_tar_get_entryleft(struct archive_read *a); static int archive_read_format_tar_read_advance(struct archive_read *a, off_t offset); static int archive_read_format_tar_skip(struct archive_read *a); static int archive_read_format_tar_read_header(struct archive_read *, struct archive_entry *); static int checksum(struct archive_read *, const void *); static int pax_attribute(struct tar *, struct archive_entry *, char *key, char *value); static int pax_header(struct archive_read *, struct tar *, struct archive_entry *, char *attr); static void pax_time(const char *, int64_t *sec, long *nanos); static ssize_t readline(struct archive_read *, struct tar *, const char **, ssize_t limit); static int read_body_to_string(struct archive_read *, struct tar *, struct archive_string *, const void *h); static int64_t tar_atol(const char *, unsigned); static int64_t tar_atol10(const char *, unsigned); static int64_t tar_atol256(const char *, unsigned); static int64_t tar_atol8(const char *, unsigned); static int tar_read_header(struct archive_read *, struct tar *, struct archive_entry *); static int tohex(int c); static char *url_decode(const char *); static wchar_t *utf8_decode(struct tar *, const char *, size_t length); int archive_read_support_format_gnutar(struct archive *a) { return (archive_read_support_format_tar(a)); } int archive_read_support_format_tar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct tar *tar; int r; tar = (struct tar *)malloc(sizeof(*tar)); if (tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } memset(tar, 0, sizeof(*tar)); r = __archive_read_register_format(a, tar, "tar", archive_read_format_tar_bid, NULL, archive_read_format_tar_read_header, archive_read_format_tar_read_data, archive_read_format_tar_get_entryleft, archive_read_format_tar_read_advance, archive_read_format_tar_skip, archive_read_format_tar_cleanup); if (r != ARCHIVE_OK) free(tar); return (ARCHIVE_OK); } static int archive_read_format_tar_cleanup(struct archive_read *a) { struct tar *tar; tar = (struct tar *)(a->format->data); gnu_clear_sparse_list(tar); archive_string_free(&tar->acl_text); archive_string_free(&tar->entry_pathname); archive_string_free(&tar->entry_pathname_override); archive_string_free(&tar->entry_linkpath); archive_string_free(&tar->entry_uname); archive_string_free(&tar->entry_gname); archive_string_free(&tar->line); archive_string_free(&tar->pax_global); archive_string_free(&tar->pax_header); archive_string_free(&tar->longname); archive_string_free(&tar->longlink); free(tar->pax_entry); free(tar); (a->format->data) = NULL; return (ARCHIVE_OK); } static int archive_read_format_tar_bid(struct archive_read *a) { int bid; const void *h; const struct archive_entry_header_ustar *header; bid = 0; /* Now let's look at the actual header and see if it matches. */ h = __archive_read_ahead(a, 512, NULL); if (h == NULL) return (-1); /* If it's an end-of-archive mark, we can handle it. */ if ((*(const char *)h) == 0 && archive_block_is_null((const unsigned char *)h)) { /* * Usually, I bid the number of bits verified, but * in this case, 4096 seems excessive so I picked 10 as * an arbitrary but reasonable-seeming value. */ return (10); } /* If it's not an end-of-archive mark, it must have a valid checksum.*/ if (!checksum(a, h)) return (0); bid += 48; /* Checksum is usually 6 octal digits. */ header = (const struct archive_entry_header_ustar *)h; /* Recognize POSIX formats. */ if ((memcmp(header->magic, "ustar\0", 6) == 0) &&(memcmp(header->version, "00", 2)==0)) bid += 56; /* Recognize GNU tar format. */ if ((memcmp(header->magic, "ustar ", 6) == 0) &&(memcmp(header->version, " \0", 2)==0)) bid += 56; /* Type flag must be null, digit or A-Z, a-z. */ if (header->typeflag[0] != 0 && !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') && !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') && !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') ) return (0); bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ /* Sanity check: Look at first byte of mode field. */ switch (255 & (unsigned)header->mode[0]) { case 0: case 255: /* Base-256 value: No further verification possible! */ break; case ' ': /* Not recommended, but not illegal, either. */ break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* Octal Value. */ /* TODO: Check format of remainder of this field. */ break; default: /* Not a valid mode; bail out here. */ return (0); } /* TODO: Sanity test uid/gid/size/mtime/rdevmajor/rdevminor fields. */ return (bid); } /* * The function invoked by archive_read_next_header(). This * just sets up a few things and then calls the internal * tar_read_header() function below. */ static int archive_read_format_tar_read_header(struct archive_read *a, struct archive_entry *entry) { /* * When converting tar archives to cpio archives, it is * essential that each distinct file have a distinct inode * number. To simplify this, we keep a static count here to * assign fake dev/inode numbers to each tar entry. Note that * pax format archives may overwrite this with something more * useful. * * Ideally, we would track every file read from the archive so * that we could assign the same dev/ino pair to hardlinks, * but the memory required to store a complete lookup table is * probably not worthwhile just to support the relatively * obscure tar->cpio conversion case. */ static int default_inode; static int default_dev; struct tar *tar; struct sparse_block *sp; const char *p; int r; size_t l; /* Assign default device/inode values. */ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */ /* Limit generated st_ino number to 16 bits. */ if (default_inode >= 0xffff) { ++default_dev; default_inode = 0; } tar = (struct tar *)(a->format->data); tar->entry_offset = 0; while (tar->sparse_list != NULL) { sp = tar->sparse_list; tar->sparse_list = sp->next; free(sp); } tar->sparse_last = NULL; tar->realsize = -1; /* Mark this as "unset" */ r = tar_read_header(a, tar, entry); /* * "non-sparse" files are really just sparse files with * a single block. */ if (tar->sparse_list == NULL) gnu_add_sparse_entry(tar, 0, tar->entry_bytes_remaining); if (r == ARCHIVE_OK) { /* * "Regular" entry with trailing '/' is really * directory: This is needed for certain old tar * variants and even for some broken newer ones. */ p = archive_entry_pathname(entry); l = strlen(p); if (archive_entry_filetype(entry) == AE_IFREG && p[l-1] == '/') archive_entry_set_filetype(entry, AE_IFDIR); } return (r); } static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { ssize_t bytes_read; struct tar *tar; struct sparse_block *p; tar = (struct tar *)(a->format->data); if (tar->sparse_gnu_pending) { if (tar->sparse_gnu_major == 1 && tar->sparse_gnu_minor == 0) { tar->sparse_gnu_pending = 0; /* Read initial sparse map. */ bytes_read = gnu_sparse_10_read(a, tar); tar->entry_bytes_remaining -= bytes_read; if (bytes_read < 0) return (bytes_read); } else { *size = 0; *offset = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unrecognized GNU sparse file format"); return (ARCHIVE_WARN); } tar->sparse_gnu_pending = 0; } /* Remove exhausted entries from sparse list. */ while (tar->sparse_list != NULL && tar->sparse_list->remaining == 0) { p = tar->sparse_list; tar->sparse_list = p->next; free(p); } /* If we're at end of file, return EOF. */ if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) { if (__archive_read_skip(a, tar->entry_padding) < 0) return (ARCHIVE_FATAL); tar->entry_padding = 0; *buff = NULL; *size = 0; *offset = tar->realsize; return (ARCHIVE_EOF); } *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated tar archive"); return (ARCHIVE_FATAL); } if (bytes_read > tar->entry_bytes_remaining) bytes_read = tar->entry_bytes_remaining; /* Don't read more than is available in the * current sparse block. */ if (tar->sparse_list->remaining < bytes_read) bytes_read = tar->sparse_list->remaining; *size = bytes_read; *offset = tar->sparse_list->offset; tar->sparse_list->remaining -= bytes_read; tar->sparse_list->offset += bytes_read; tar->entry_bytes_remaining -= bytes_read; __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } static off_t archive_read_format_tar_get_entryleft(struct archive_read *a) { struct tar* tar; tar = (struct tar *)(a->format->data); return (tar->entry_bytes_remaining + tar->entry_padding); } static int archive_read_format_tar_read_advance(struct archive_read *a, off_t offset) { struct tar* tar; tar = (struct tar *)(a->format->data); if (tar->entry_bytes_remaining >= offset) { tar->entry_bytes_remaining -= offset; offset = 0; } else { offset -= tar->entry_bytes_remaining; tar->entry_bytes_remaining = 0; } if (tar->entry_padding >= offset) { tar->entry_padding -= offset; offset = 0; (void)offset; /* not used beyond this point. */ } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "read_advance beyond end of entry"); return (-1); } return (0); } static int archive_read_format_tar_skip(struct archive_read *a) { off_t bytes_skipped; struct tar* tar; tar = (struct tar *)(a->format->data); /* * Compression layer skip functions are required to either skip the * length requested or fail, so we can rely upon the entire entry * plus padding being skipped. */ bytes_skipped = __archive_read_skip(a, tar->entry_bytes_remaining + tar->entry_padding); if (bytes_skipped < 0) return (ARCHIVE_FATAL); tar->entry_bytes_remaining = 0; tar->entry_padding = 0; /* Free the sparse list. */ gnu_clear_sparse_list(tar); return (ARCHIVE_OK); } /* * This function recursively interprets all of the headers associated * with a single entry. */ static int tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry) { ssize_t bytes; int err; const void *h; const struct archive_entry_header_ustar *header; /* Read 512-byte header record */ h = __archive_read_ahead(a, 512, &bytes); if (bytes < 0) return (bytes); if (bytes < 512) { /* Short read or EOF. */ /* Try requesting just one byte and see what happens. */ (void)__archive_read_ahead(a, 1, &bytes); if (bytes == 0) { /* * The archive ends at a 512-byte boundary but * without a proper end-of-archive marker. * Yes, there are tar writers that do this; * hold our nose and accept it. */ return (ARCHIVE_EOF); } /* Archive ends with a partial block; this is bad. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 512); /* Check for end-of-archive mark. */ if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) { /* Try to consume a second all-null record, as well. */ h = __archive_read_ahead(a, 512, NULL); if (h != NULL) __archive_read_consume(a, 512); archive_set_error(&a->archive, 0, NULL); if (a->archive.archive_format_name == NULL) { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar"; } return (ARCHIVE_EOF); } /* * Note: If the checksum fails and we return ARCHIVE_RETRY, * then the client is likely to just retry. This is a very * crude way to search for the next valid header! * * TODO: Improve this by implementing a real header scan. */ if (!checksum(a, h)) { archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); return (ARCHIVE_RETRY); /* Retryable: Invalid header */ } if (++tar->header_recursion_depth > 32) { archive_set_error(&a->archive, EINVAL, "Too many special headers"); return (ARCHIVE_WARN); } /* Determine the format variant. */ header = (const struct archive_entry_header_ustar *)h; switch(header->typeflag[0]) { case 'A': /* Solaris tar ACL */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "Solaris tar"; err = header_Solaris_ACL(a, tar, entry, h); break; case 'g': /* POSIX-standard 'g' header. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_global(a, tar, entry, h); break; case 'K': /* Long link name (GNU tar, others) */ err = header_longlink(a, tar, entry, h); break; case 'L': /* Long filename (GNU tar, others) */ err = header_longname(a, tar, entry, h); break; case 'V': /* GNU volume header */ err = header_volume(a, tar, entry, h); break; case 'X': /* Used by SUN tar; same as 'x'. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format (Sun variant)"; err = header_pax_extensions(a, tar, entry, h); break; case 'x': /* POSIX-standard 'x' header. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_extensions(a, tar, entry, h); break; default: if (memcmp(header->magic, "ustar \0", 8) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; a->archive.archive_format_name = "GNU tar format"; err = header_gnutar(a, tar, entry, h); } else if (memcmp(header->magic, "ustar", 5) == 0) { if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; a->archive.archive_format_name = "POSIX ustar format"; } err = header_ustar(a, tar, entry, h); } else { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar (non-POSIX)"; err = header_old_tar(a, tar, entry, h); } } --tar->header_recursion_depth; /* We return warnings or success as-is. Anything else is fatal. */ if (err == ARCHIVE_WARN || err == ARCHIVE_OK) return (err); if (err == ARCHIVE_EOF) /* EOF when recursively reading a header is bad. */ archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); return (ARCHIVE_FATAL); } /* * Return true if block checksum is correct. */ static int checksum(struct archive_read *a, const void *h) { const unsigned char *bytes; const struct archive_entry_header_ustar *header; int check, i, sum; (void)a; /* UNUSED */ bytes = (const unsigned char *)h; header = (const struct archive_entry_header_ustar *)h; /* * Test the checksum. Note that POSIX specifies _unsigned_ * bytes for this calculation. */ sum = tar_atol(header->checksum, sizeof(header->checksum)); check = 0; for (i = 0; i < 148; i++) check += (unsigned char)bytes[i]; for (; i < 156; i++) check += 32; for (; i < 512; i++) check += (unsigned char)bytes[i]; if (sum == check) return (1); /* * Repeat test with _signed_ bytes, just in case this archive * was created by an old BSD, Solaris, or HP-UX tar with a * broken checksum calculation. */ check = 0; for (i = 0; i < 148; i++) check += (signed char)bytes[i]; for (; i < 156; i++) check += 32; for (; i < 512; i++) check += (signed char)bytes[i]; if (sum == check) return (1); return (0); } /* * Return true if this block contains only nulls. */ static int archive_block_is_null(const unsigned char *p) { unsigned i; for (i = 0; i < 512; i++) if (*p++) return (0); return (1); } /* * Interpret 'A' Solaris ACL header */ static int header_Solaris_ACL(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; size_t size; int err; char *acl, *p; wchar_t *wp; /* * read_body_to_string adds a NUL terminator, but we need a little * more to make sure that we don't overrun acl_text later. */ header = (const struct archive_entry_header_ustar *)h; size = tar_atol(header->size, sizeof(header->size)); err = read_body_to_string(a, tar, &(tar->acl_text), h); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* Skip leading octal number. */ /* XXX TODO: Parse the octal number and sanity-check it. */ p = acl = tar->acl_text.s; while (*p != '\0' && p < acl + size) p++; p++; if (p >= acl + size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute"); return(ARCHIVE_WARN); } /* Skip leading octal number. */ size -= (p - acl); acl = p; while (*p != '\0' && p < acl + size) p++; wp = utf8_decode(tar, acl, p - acl); err = __archive_entry_acl_parse_w(entry, wp, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); return (err); } /* * Interpret 'K' long linkname header. */ static int header_longlink(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { int err; err = read_body_to_string(a, tar, &(tar->longlink), h); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* Set symlink if symlink already set, else hardlink. */ archive_entry_copy_link(entry, tar->longlink.s); return (ARCHIVE_OK); } /* * Interpret 'L' long filename header. */ static int header_longname(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { int err; err = read_body_to_string(a, tar, &(tar->longname), h); if (err != ARCHIVE_OK) return (err); /* Read and parse "real" header, then override name. */ err = tar_read_header(a, tar, entry); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); archive_entry_copy_pathname(entry, tar->longname.s); return (ARCHIVE_OK); } /* * Interpret 'V' GNU tar volume header. */ static int header_volume(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { (void)h; /* Just skip this and read the next header. */ return (tar_read_header(a, tar, entry)); } /* * Read body of an archive entry into an archive_string object. */ static int read_body_to_string(struct archive_read *a, struct tar *tar, struct archive_string *as, const void *h) { off_t size, padded_size; const struct archive_entry_header_ustar *header; const void *src; (void)tar; /* UNUSED */ header = (const struct archive_entry_header_ustar *)h; size = tar_atol(header->size, sizeof(header->size)); if ((size > 1048576) || (size < 0)) { archive_set_error(&a->archive, EINVAL, "Special header too large"); return (ARCHIVE_FATAL); } /* Fail if we can't make our buffer big enough. */ if (archive_string_ensure(as, size+1) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } /* Read the body into the string. */ padded_size = (size + 511) & ~ 511; src = __archive_read_ahead(a, padded_size, NULL); if (src == NULL) return (ARCHIVE_FATAL); memcpy(as->s, src, size); __archive_read_consume(a, padded_size); as->s[size] = '\0'; return (ARCHIVE_OK); } /* * Parse out common header elements. * * This would be the same as header_old_tar, except that the * filename is handled slightly differently for old and POSIX * entries (POSIX entries support a 'prefix'). This factoring * allows header_old_tar and header_ustar * to handle filenames differently, while still putting most of the * common parsing into one place. */ static int header_common(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; char tartype; (void)a; /* UNUSED */ header = (const struct archive_entry_header_ustar *)h; if (header->linkname[0]) archive_strncpy(&(tar->entry_linkpath), header->linkname, sizeof(header->linkname)); else archive_string_empty(&(tar->entry_linkpath)); /* Parse out the numeric fields (all are octal) */ archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode))); archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid))); archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size)); tar->realsize = tar->entry_bytes_remaining; archive_entry_set_size(entry, tar->entry_bytes_remaining); archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); /* Handle the tar type flag appropriately. */ tartype = header->typeflag[0]; switch (tartype) { case '1': /* Hard link */ archive_entry_copy_hardlink(entry, tar->entry_linkpath.s); /* * The following may seem odd, but: Technically, tar * does not store the file type for a "hard link" * entry, only the fact that it is a hard link. So, I * leave the type zero normally. But, pax interchange * format allows hard links to have data, which * implies that the underlying entry is a regular * file. */ if (archive_entry_size(entry) > 0) archive_entry_set_filetype(entry, AE_IFREG); /* * A tricky point: Traditionally, tar readers have * ignored the size field when reading hardlink * entries, and some writers put non-zero sizes even * though the body is empty. POSIX blessed this * convention in the 1988 standard, but broke with * this tradition in 2001 by permitting hardlink * entries to store valid bodies in pax interchange * format, but not in ustar format. Since there is no * hard and fast way to distinguish pax interchange * from earlier archives (the 'x' and 'g' entries are * optional, after all), we need a heuristic. */ if (archive_entry_size(entry) == 0) { /* If the size is already zero, we're done. */ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { /* Definitely pax extended; must obey hardlink size. */ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR) { /* Old-style or GNU tar: we must ignore the size. */ archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; } else if (archive_read_format_tar_bid(a) > 50) { /* * We don't know if it's pax: If the bid * function sees a valid ustar header * immediately following, then let's ignore * the hardlink size. */ archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; } /* * TODO: There are still two cases I'd like to handle: * = a ustar non-pax archive with a hardlink entry at * end-of-archive. (Look for block of nulls following?) * = a pax archive that has not seen any pax headers * and has an entry which is a hardlink entry storing * a body containing an uncompressed tar archive. * The first is worth addressing; I don't see any reliable * way to deal with the second possibility. */ break; case '2': /* Symlink */ archive_entry_set_filetype(entry, AE_IFLNK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; archive_entry_copy_symlink(entry, tar->entry_linkpath.s); break; case '3': /* Character device */ archive_entry_set_filetype(entry, AE_IFCHR); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '4': /* Block device */ archive_entry_set_filetype(entry, AE_IFBLK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '5': /* Dir */ archive_entry_set_filetype(entry, AE_IFDIR); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '6': /* FIFO device */ archive_entry_set_filetype(entry, AE_IFIFO); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case 'D': /* GNU incremental directory type */ /* * No special handling is actually required here. * It might be nice someday to preprocess the file list and * provide it to the client, though. */ archive_entry_set_filetype(entry, AE_IFDIR); break; case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/ /* * As far as I can tell, this is just like a regular file * entry, except that the contents should be _appended_ to * the indicated file at the indicated offset. This may * require some API work to fully support. */ break; case 'N': /* Old GNU "long filename" entry. */ /* The body of this entry is a script for renaming * previously-extracted entries. Ugh. It will never * be supported by libarchive. */ archive_entry_set_filetype(entry, AE_IFREG); break; case 'S': /* GNU sparse files */ /* * Sparse files are really just regular files with * sparse information in the extended area. */ /* FALLTHROUGH */ default: /* Regular file and non-standard types */ /* * Per POSIX: non-recognized types should always be * treated as regular files. */ archive_entry_set_filetype(entry, AE_IFREG); break; } return (0); } /* * Parse out header elements for "old-style" tar archives. */ static int header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_ustar *)h; archive_strncpy(&(tar->entry_pathname), header->name, sizeof(header->name)); archive_entry_copy_pathname(entry, tar->entry_pathname.s); /* Grab rest of common fields */ header_common(a, tar, entry, h); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (0); } /* * Parse a file header for a pax extended archive entry. */ static int header_pax_global(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { int err; err = read_body_to_string(a, tar, &(tar->pax_global), h); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry); return (err); } static int header_pax_extensions(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { int err, err2; err = read_body_to_string(a, tar, &(tar->pax_header), h); if (err != ARCHIVE_OK) return (err); /* Parse the next header. */ err = tar_read_header(a, tar, entry); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* * TODO: Parse global/default options into 'entry' struct here * before handling file-specific options. * * This design (parse standard header, then overwrite with pax * extended attribute data) usually works well, but isn't ideal; * it would be better to parse the pax extended attributes first * and then skip any fields in the standard header that were * defined in the pax header. */ err2 = pax_header(a, tar, entry, tar->pax_header.s); err = err_combine(err, err2); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Parse a file header for a Posix "ustar" archive entry. This also * handles "pax" or "extended ustar" entries. */ static int header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; struct archive_string *as; header = (const struct archive_entry_header_ustar *)h; /* Copy name into an internal buffer to ensure null-termination. */ as = &(tar->entry_pathname); if (header->prefix[0]) { archive_strncpy(as, header->prefix, sizeof(header->prefix)); if (as->s[archive_strlen(as) - 1] != '/') archive_strappend_char(as, '/'); archive_strncat(as, header->name, sizeof(header->name)); } else archive_strncpy(as, header->name, sizeof(header->name)); archive_entry_copy_pathname(entry, as->s); /* Handle rest of common fields. */ header_common(a, tar, entry, h); /* Handle POSIX ustar fields. */ archive_strncpy(&(tar->entry_uname), header->uname, sizeof(header->uname)); archive_entry_copy_uname(entry, tar->entry_uname.s); archive_strncpy(&(tar->entry_gname), header->gname, sizeof(header->gname)); archive_entry_copy_gname(entry, tar->entry_gname.s); /* Parse out device numbers only for char and block specials. */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { archive_entry_set_rdevmajor(entry, tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); archive_entry_set_rdevminor(entry, tar_atol(header->rdevminor, sizeof(header->rdevminor))); } tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (0); } /* * Parse the pax extended attributes record. * * Returns non-zero if there's an error in the data. */ static int pax_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, char *attr) { size_t attr_length, l, line_length; char *p; char *key, *value; int err, err2; attr_length = strlen(attr); tar->pax_hdrcharset_binary = 0; archive_string_empty(&(tar->entry_gname)); archive_string_empty(&(tar->entry_linkpath)); archive_string_empty(&(tar->entry_pathname)); archive_string_empty(&(tar->entry_pathname_override)); archive_string_empty(&(tar->entry_uname)); err = ARCHIVE_OK; while (attr_length > 0) { /* Parse decimal length field at start of line. */ line_length = 0; l = attr_length; p = attr; /* Record start of line. */ while (l>0) { if (*p == ' ') { p++; l--; break; } if (*p < '0' || *p > '9') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring malformed pax extended attributes"); return (ARCHIVE_WARN); } line_length *= 10; line_length += *p - '0'; if (line_length > 999999) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Rejecting pax extended attribute > 1MB"); return (ARCHIVE_WARN); } p++; l--; } /* * Parsed length must be no bigger than available data, * at least 1, and the last character of the line must * be '\n'. */ if (line_length > attr_length || line_length < 1 || attr[line_length - 1] != '\n') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring malformed pax extended attribute"); return (ARCHIVE_WARN); } /* Null-terminate the line. */ attr[line_length - 1] = '\0'; /* Find end of key and null terminate it. */ key = p; if (key[0] == '=') return (-1); while (*p && *p != '=') ++p; if (*p == '\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid pax extended attributes"); return (ARCHIVE_WARN); } *p = '\0'; /* Identify null-terminated 'value' portion. */ value = p + 1; /* Identify this attribute and set it in the entry. */ err2 = pax_attribute(tar, entry, key, value); err = err_combine(err, err2); /* Skip to next line */ attr += line_length; attr_length -= line_length; } if (archive_strlen(&(tar->entry_gname)) > 0) { value = tar->entry_gname.s; if (tar->pax_hdrcharset_binary) archive_entry_copy_gname(entry, value); else { if (!archive_entry_update_gname_utf8(entry, value)) { err = ARCHIVE_WARN; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Gname in pax header can't " "be converted to current locale."); } } } if (archive_strlen(&(tar->entry_linkpath)) > 0) { value = tar->entry_linkpath.s; if (tar->pax_hdrcharset_binary) archive_entry_copy_link(entry, value); else { if (!archive_entry_update_link_utf8(entry, value)) { err = ARCHIVE_WARN; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname in pax header can't " "be converted to current locale."); } } } /* * Some extensions (such as the GNU sparse file extensions) * deliberately store a synthetic name under the regular 'path' * attribute and the real file name under a different attribute. * Since we're supposed to not care about the order, we * have no choice but to store all of the various filenames * we find and figure it all out afterwards. This is the * figuring out part. */ value = NULL; if (archive_strlen(&(tar->entry_pathname_override)) > 0) value = tar->entry_pathname_override.s; else if (archive_strlen(&(tar->entry_pathname)) > 0) value = tar->entry_pathname.s; if (value != NULL) { if (tar->pax_hdrcharset_binary) archive_entry_copy_pathname(entry, value); else { if (!archive_entry_update_pathname_utf8(entry, value)) { err = ARCHIVE_WARN; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname in pax header can't be " "converted to current locale."); } } } if (archive_strlen(&(tar->entry_uname)) > 0) { value = tar->entry_uname.s; if (tar->pax_hdrcharset_binary) archive_entry_copy_uname(entry, value); else { if (!archive_entry_update_uname_utf8(entry, value)) { err = ARCHIVE_WARN; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Uname in pax header can't " "be converted to current locale."); } } } return (err); } static int pax_attribute_xattr(struct archive_entry *entry, char *name, char *value) { char *name_decoded; void *value_decoded; size_t value_len; if (strlen(name) < 18 || (strncmp(name, "LIBARCHIVE.xattr.", 17)) != 0) return 3; name += 17; /* URL-decode name */ name_decoded = url_decode(name); if (name_decoded == NULL) return 2; /* Base-64 decode value */ value_decoded = base64_decode(value, strlen(value), &value_len); if (value_decoded == NULL) { free(name_decoded); return 1; } archive_entry_xattr_add_entry(entry, name_decoded, value_decoded, value_len); free(name_decoded); free(value_decoded); return 0; } /* * Parse a single key=value attribute. key/value pointers are * assumed to point into reasonably long-lived storage. * * Note that POSIX reserves all-lowercase keywords. Vendor-specific * extensions should always have keywords of the form "VENDOR.attribute" * In particular, it's quite feasible to support many different * vendor extensions here. I'm using "LIBARCHIVE" for extensions * unique to this library. * * Investigate other vendor-specific extensions and see if * any of them look useful. */ static int pax_attribute(struct tar *tar, struct archive_entry *entry, char *key, char *value) { int64_t s; long n; wchar_t *wp; switch (key[0]) { case 'G': /* GNU "0.0" sparse pax format. */ if (strcmp(key, "GNU.sparse.numblocks") == 0) { tar->sparse_offset = -1; tar->sparse_numbytes = -1; tar->sparse_gnu_major = 0; tar->sparse_gnu_minor = 0; } if (strcmp(key, "GNU.sparse.offset") == 0) { tar->sparse_offset = tar_atol10(value, strlen(value)); if (tar->sparse_numbytes != -1) { gnu_add_sparse_entry(tar, tar->sparse_offset, tar->sparse_numbytes); tar->sparse_offset = -1; tar->sparse_numbytes = -1; } } if (strcmp(key, "GNU.sparse.numbytes") == 0) { tar->sparse_numbytes = tar_atol10(value, strlen(value)); if (tar->sparse_numbytes != -1) { gnu_add_sparse_entry(tar, tar->sparse_offset, tar->sparse_numbytes); tar->sparse_offset = -1; tar->sparse_numbytes = -1; } } if (strcmp(key, "GNU.sparse.size") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); } /* GNU "0.1" sparse pax format. */ if (strcmp(key, "GNU.sparse.map") == 0) { tar->sparse_gnu_major = 0; tar->sparse_gnu_minor = 1; if (gnu_sparse_01_parse(tar, value) != ARCHIVE_OK) return (ARCHIVE_WARN); } /* GNU "1.0" sparse pax format */ if (strcmp(key, "GNU.sparse.major") == 0) { tar->sparse_gnu_major = tar_atol10(value, strlen(value)); tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.minor") == 0) { tar->sparse_gnu_minor = tar_atol10(value, strlen(value)); tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.name") == 0) { /* * The real filename; when storing sparse * files, GNU tar puts a synthesized name into * the regular 'path' attribute in an attempt * to limit confusion. ;-) */ archive_strcpy(&(tar->entry_pathname_override), value); } if (strcmp(key, "GNU.sparse.realsize") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); } break; case 'L': /* Our extensions */ /* TODO: Handle arbitrary extended attributes... */ /* if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0) archive_entry_set_xxxxxx(entry, value); */ if (strcmp(key, "LIBARCHIVE.creationtime")==0) { pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } if (strncmp(key, "LIBARCHIVE.xattr.", 17)==0) pax_attribute_xattr(entry, key, value); break; case 'S': /* We support some keys used by the "star" archiver */ if (strcmp(key, "SCHILY.acl.access")==0) { wp = utf8_decode(tar, value, strlen(value)); /* TODO: if (wp == NULL) */ __archive_entry_acl_parse_w(entry, wp, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); } else if (strcmp(key, "SCHILY.acl.default")==0) { wp = utf8_decode(tar, value, strlen(value)); /* TODO: if (wp == NULL) */ __archive_entry_acl_parse_w(entry, wp, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); } else if (strcmp(key, "SCHILY.devmajor")==0) { archive_entry_set_rdevmajor(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.devminor")==0) { archive_entry_set_rdevminor(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.fflags")==0) { archive_entry_copy_fflags_text(entry, value); } else if (strcmp(key, "SCHILY.dev")==0) { archive_entry_set_dev(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.ino")==0) { archive_entry_set_ino(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.nlink")==0) { archive_entry_set_nlink(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.realsize")==0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); } break; case 'a': if (strcmp(key, "atime")==0) { pax_time(value, &s, &n); archive_entry_set_atime(entry, s, n); } break; case 'c': if (strcmp(key, "ctime")==0) { pax_time(value, &s, &n); archive_entry_set_ctime(entry, s, n); } else if (strcmp(key, "charset")==0) { /* TODO: Publish charset information in entry. */ } else if (strcmp(key, "comment")==0) { /* TODO: Publish comment in entry. */ } break; case 'g': if (strcmp(key, "gid")==0) { archive_entry_set_gid(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "gname")==0) { archive_strcpy(&(tar->entry_gname), value); } break; case 'h': if (strcmp(key, "hdrcharset") == 0) { if (strcmp(value, "BINARY") == 0) tar->pax_hdrcharset_binary = 1; else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0) tar->pax_hdrcharset_binary = 0; else { /* TODO: Warn about unsupported hdrcharset */ } } break; case 'l': /* pax interchange doesn't distinguish hardlink vs. symlink. */ if (strcmp(key, "linkpath")==0) { archive_strcpy(&(tar->entry_linkpath), value); } break; case 'm': if (strcmp(key, "mtime")==0) { pax_time(value, &s, &n); archive_entry_set_mtime(entry, s, n); } break; case 'p': if (strcmp(key, "path")==0) { archive_strcpy(&(tar->entry_pathname), value); } break; case 'r': /* POSIX has reserved 'realtime.*' */ break; case 's': /* POSIX has reserved 'security.*' */ /* Someday: if (strcmp(key, "security.acl")==0) { ... } */ if (strcmp(key, "size")==0) { /* "size" is the size of the data in the entry. */ tar->entry_bytes_remaining = tar_atol10(value, strlen(value)); /* * But, "size" is not necessarily the size of * the file on disk; if this is a sparse file, * the disk size may have already been set from * GNU.sparse.realsize or GNU.sparse.size or * an old GNU header field or SCHILY.realsize * or .... */ if (tar->realsize < 0) { archive_entry_set_size(entry, tar->entry_bytes_remaining); tar->realsize = tar->entry_bytes_remaining; } } break; case 'u': if (strcmp(key, "uid")==0) { archive_entry_set_uid(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "uname")==0) { archive_strcpy(&(tar->entry_uname), value); } break; } return (0); } /* * parse a decimal time value, which may include a fractional portion */ static void pax_time(const char *p, int64_t *ps, long *pn) { char digit; int64_t s; unsigned long l; int sign; int64_t limit, last_digit_limit; limit = INT64_MAX / 10; last_digit_limit = INT64_MAX % 10; s = 0; sign = 1; if (*p == '-') { sign = -1; p++; } while (*p >= '0' && *p <= '9') { digit = *p - '0'; if (s > limit || (s == limit && digit > last_digit_limit)) { s = UINT64_MAX; break; } s = (s * 10) + digit; ++p; } *ps = s * sign; /* Calculate nanoseconds. */ *pn = 0; if (*p != '.') return; l = 100000000UL; do { ++p; if (*p >= '0' && *p <= '9') *pn += (*p - '0') * l; else break; } while (l /= 10); } /* * Parse GNU tar header */ static int header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_gnutar *header; (void)a; /* * GNU header is like POSIX ustar, except 'prefix' is * replaced with some other fields. This also means the * filename is stored as in old-style archives. */ /* Grab fields common to all tar variants. */ header_common(a, tar, entry, h); /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_gnutar *)h; archive_strncpy(&(tar->entry_pathname), header->name, sizeof(header->name)); archive_entry_copy_pathname(entry, tar->entry_pathname.s); /* Fields common to ustar and GNU */ /* XXX Can the following be factored out since it's common * to ustar and gnu tar? Is it okay to move it down into * header_common, perhaps? */ archive_strncpy(&(tar->entry_uname), header->uname, sizeof(header->uname)); archive_entry_copy_uname(entry, tar->entry_uname.s); archive_strncpy(&(tar->entry_gname), header->gname, sizeof(header->gname)); archive_entry_copy_gname(entry, tar->entry_gname.s); /* Parse out device numbers only for char and block specials */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { archive_entry_set_rdevmajor(entry, tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); archive_entry_set_rdevminor(entry, tar_atol(header->rdevminor, sizeof(header->rdevminor))); } else archive_entry_set_rdev(entry, 0); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); /* Grab GNU-specific fields. */ archive_entry_set_atime(entry, tar_atol(header->atime, sizeof(header->atime)), 0); archive_entry_set_ctime(entry, tar_atol(header->ctime, sizeof(header->ctime)), 0); if (header->realsize[0] != 0) { tar->realsize = tar_atol(header->realsize, sizeof(header->realsize)); archive_entry_set_size(entry, tar->realsize); } if (header->sparse[0].offset[0] != 0) { gnu_sparse_old_read(a, tar, header); } else { if (header->isextended[0] != 0) { /* XXX WTF? XXX */ } } return (0); } static void gnu_add_sparse_entry(struct tar *tar, off_t offset, off_t remaining) { struct sparse_block *p; p = (struct sparse_block *)malloc(sizeof(*p)); if (p == NULL) __archive_errx(1, "Out of memory"); memset(p, 0, sizeof(*p)); if (tar->sparse_last != NULL) tar->sparse_last->next = p; else tar->sparse_list = p; tar->sparse_last = p; p->offset = offset; p->remaining = remaining; } static void gnu_clear_sparse_list(struct tar *tar) { struct sparse_block *p; while (tar->sparse_list != NULL) { p = tar->sparse_list; tar->sparse_list = p->next; free(p); } tar->sparse_last = NULL; } /* * GNU tar old-format sparse data. * * GNU old-format sparse data is stored in a fixed-field * format. Offset/size values are 11-byte octal fields (same * format as 'size' field in ustart header). These are * stored in the header, allocating subsequent header blocks * as needed. Extending the header in this way is a pretty * severe POSIX violation; this design has earned GNU tar a * lot of criticism. */ static int gnu_sparse_old_read(struct archive_read *a, struct tar *tar, const struct archive_entry_header_gnutar *header) { ssize_t bytes_read; const void *data; struct extended { struct gnu_sparse sparse[21]; char isextended[1]; char padding[7]; }; const struct extended *ext; gnu_sparse_old_parse(tar, header->sparse, 4); if (header->isextended[0] == 0) return (ARCHIVE_OK); do { data = __archive_read_ahead(a, 512, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read < 512) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " "detected while reading sparse file data"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 512); ext = (const struct extended *)data; gnu_sparse_old_parse(tar, ext->sparse, 21); } while (ext->isextended[0] != 0); if (tar->sparse_list != NULL) tar->entry_offset = tar->sparse_list->offset; return (ARCHIVE_OK); } static void gnu_sparse_old_parse(struct tar *tar, const struct gnu_sparse *sparse, int length) { while (length > 0 && sparse->offset[0] != 0) { gnu_add_sparse_entry(tar, tar_atol(sparse->offset, sizeof(sparse->offset)), tar_atol(sparse->numbytes, sizeof(sparse->numbytes))); sparse++; length--; } } /* * GNU tar sparse format 0.0 * * Beginning with GNU tar 1.15, sparse files are stored using * information in the pax extended header. The GNU tar maintainers * have gone through a number of variations in the process of working * out this scheme; fortunately, they're all numbered. * * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to * store offset/size for each block. The repeated instances of these * latter fields violate the pax specification (which frowns on * duplicate keys), so this format was quickly replaced. */ /* * GNU tar sparse format 0.1 * * This version replaced the offset/numbytes attributes with * a single "map" attribute that stored a list of integers. This * format had two problems: First, the "map" attribute could be very * long, which caused problems for some implementations. More * importantly, the sparse data was lost when extracted by archivers * that didn't recognize this extension. */ static int gnu_sparse_01_parse(struct tar *tar, const char *p) { const char *e; off_t offset = -1, size = -1; for (;;) { e = p; while (*e != '\0' && *e != ',') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; } if (offset < 0) { offset = tar_atol10(p, e - p); if (offset < 0) return (ARCHIVE_WARN); } else { size = tar_atol10(p, e - p); if (size < 0) return (ARCHIVE_WARN); gnu_add_sparse_entry(tar, offset, size); offset = -1; } if (*e == '\0') return (ARCHIVE_OK); p = e + 1; } } /* * GNU tar sparse format 1.0 * * The idea: The offset/size data is stored as a series of base-10 * ASCII numbers prepended to the file data, so that dearchivers that * don't support this format will extract the block map along with the * data and a separate post-process can restore the sparseness. * * Unfortunately, GNU tar 1.16 had a bug that added unnecessary * padding to the body of the file when using this format. GNU tar * 1.17 corrected this bug without bumping the version number, so * it's not possible to support both variants. This code supports * the later variant at the expense of not supporting the former. * * This variant also replaced GNU.sparse.size with GNU.sparse.realsize * and introduced the GNU.sparse.major/GNU.sparse.minor attributes. */ /* * Read the next line from the input, and parse it as a decimal * integer followed by '\n'. Returns positive integer value or * negative on error. */ static int64_t gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, ssize_t *remaining) { int64_t l, limit, last_digit_limit; const char *p; ssize_t bytes_read; int base, digit; base = 10; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; /* * Skip any lines starting with '#'; GNU tar specs * don't require this, but they should. */ do { bytes_read = readline(a, tar, &p, tar_min(*remaining, 100)); if (bytes_read <= 0) return (ARCHIVE_FATAL); *remaining -= bytes_read; } while (p[0] == '#'); l = 0; while (bytes_read > 0) { if (*p == '\n') return (l); if (*p < '0' || *p >= '0' + base) return (ARCHIVE_WARN); digit = *p - '0'; if (l > limit || (l == limit && digit > last_digit_limit)) l = UINT64_MAX; /* Truncate on overflow. */ else l = (l * base) + digit; p++; bytes_read--; } /* TODO: Error message. */ return (ARCHIVE_WARN); } /* * Returns length (in bytes) of the sparse data description * that was read. */ static ssize_t gnu_sparse_10_read(struct archive_read *a, struct tar *tar) { ssize_t remaining, bytes_read; int entries; off_t offset, size, to_skip; /* Clear out the existing sparse list. */ gnu_clear_sparse_list(tar); remaining = tar->entry_bytes_remaining; /* Parse entries. */ entries = gnu_sparse_10_atol(a, tar, &remaining); if (entries < 0) return (ARCHIVE_FATAL); /* Parse the individual entries. */ while (entries-- > 0) { /* Parse offset/size */ offset = gnu_sparse_10_atol(a, tar, &remaining); if (offset < 0) return (ARCHIVE_FATAL); size = gnu_sparse_10_atol(a, tar, &remaining); if (size < 0) return (ARCHIVE_FATAL); /* Add a new sparse entry. */ gnu_add_sparse_entry(tar, offset, size); } /* Skip rest of block... */ bytes_read = tar->entry_bytes_remaining - remaining; to_skip = 0x1ff & -bytes_read; if (to_skip != __archive_read_skip(a, to_skip)) return (ARCHIVE_FATAL); return (bytes_read + to_skip); } /*- * Convert text->integer. * * Traditional tar formats (including POSIX) specify base-8 for * all of the standard numeric fields. This is a significant limitation * in practice: * = file size is limited to 8GB * = rdevmajor and rdevminor are limited to 21 bits * = uid/gid are limited to 21 bits * * There are two workarounds for this: * = pax extended headers, which use variable-length string fields * = GNU tar and STAR both allow either base-8 or base-256 in * most fields. The high bit is set to indicate base-256. * * On read, this implementation supports both extensions. */ static int64_t tar_atol(const char *p, unsigned char_cnt) { /* * Technically, GNU tar considers a field to be in base-256 * only if the first byte is 0xff or 0x80. */ if (*p & 0x80) return (tar_atol256(p, char_cnt)); return (tar_atol8(p, char_cnt)); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t tar_atol8(const char *p, unsigned char_cnt) { int64_t l, limit, last_digit_limit; int digit, sign, base; base = 8; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; while (*p == ' ' || *p == '\t') p++; if (*p == '-') { sign = -1; p++; } else sign = 1; l = 0; digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt-- > 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (sign < 0) ? -l : 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 tar_atol10(const char *p, unsigned char_cnt) { int64_t l, limit, last_digit_limit; int base, digit, sign; base = 10; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; while (*p == ' ' || *p == '\t') p++; if (*p == '-') { sign = -1; p++; } else sign = 1; l = 0; digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt-- > 0) { if (l > limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (sign < 0) ? -l : l; } /* * Parse a base-256 integer. This is just a straight signed binary * value in big-endian order, except that the high-order bit is * ignored. */ static int64_t tar_atol256(const char *_p, unsigned char_cnt) { int64_t l, upper_limit, lower_limit; const unsigned char *p = (const unsigned char *)_p; upper_limit = INT64_MAX / 256; lower_limit = INT64_MIN / 256; /* Sign-extend the 7-bit value to 64 bits. */ if ((0x40 & *p) == 0x40) l = ~((int64_t)0x3f) | *p++; else l = 0x3f & *p++; while (--char_cnt > 0) { if (l > upper_limit) return (INT64_MAX); /* Truncate on overflow */ else if (l < lower_limit) return (INT64_MIN); l = (l << 8) | (0xff & (int64_t)*p++); } return (l); } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. This avoids copying * when possible. */ static ssize_t readline(struct archive_read *a, struct tar *tar, const char **start, ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; const void *t; const char *s; void *p; t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n' in the read buffer, return pointer to that. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; if (bytes_read > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } __archive_read_consume(a, bytes_read); *start = s; return (bytes_read); } /* Otherwise, we need to accumulate in a line buffer. */ for (;;) { if (total_size + bytes_read > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } memcpy(tar->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; /* If we found '\n', clean up and return. */ if (p != NULL) { *start = tar->line.s; return (total_size); } /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; } } } static wchar_t * utf8_decode(struct tar *tar, const char *src, size_t length) { wchar_t *dest; ssize_t n; /* Ensure pax_entry buffer is big enough. */ if (tar->pax_entry_length <= length) { wchar_t *old_entry; if (tar->pax_entry_length <= 0) tar->pax_entry_length = 1024; while (tar->pax_entry_length <= length + 1) tar->pax_entry_length *= 2; old_entry = tar->pax_entry; tar->pax_entry = (wchar_t *)realloc(tar->pax_entry, tar->pax_entry_length * sizeof(wchar_t)); if (tar->pax_entry == NULL) { free(old_entry); /* TODO: Handle this error. */ return (NULL); } } dest = tar->pax_entry; while (length > 0) { n = UTF8_mbrtowc(dest, src, length); if (n < 0) return (NULL); if (n == 0) break; dest++; src += n; length -= n; } *dest++ = L'\0'; return (tar->pax_entry); } /* * Copied and simplified from FreeBSD libc/locale. */ static ssize_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n) { int ch, i, len, mask; unsigned long wch; if (s == NULL || n == 0 || pwc == NULL) return (0); /* * Determine the number of octets that make up this character from * the first octet, and a mask that extracts the interesting bits of * the first octet. */ ch = (unsigned char)*s; if ((ch & 0x80) == 0) { mask = 0x7f; len = 1; } else if ((ch & 0xe0) == 0xc0) { mask = 0x1f; len = 2; } else if ((ch & 0xf0) == 0xe0) { mask = 0x0f; len = 3; } else if ((ch & 0xf8) == 0xf0) { mask = 0x07; len = 4; } else { /* Invalid first byte. */ return (-1); } if (n < (size_t)len) { /* Valid first byte but truncated. */ return (-2); } /* * Decode the octet sequence representing the character in chunks * of 6 bits, most significant first. */ wch = (unsigned char)*s++ & mask; i = len; while (--i != 0) { if ((*s & 0xc0) != 0x80) { /* Invalid intermediate byte; consume one byte and * emit '?' */ *pwc = '?'; return (1); } wch <<= 6; wch |= *s++ & 0x3f; } /* Assign the value to the output; out-of-range values * just get truncated. */ *pwc = (wchar_t)wch; #ifdef WCHAR_MAX /* * If platform has WCHAR_MAX, we can do something * more sensible with out-of-range values. */ if (wch >= WCHAR_MAX) *pwc = '?'; #endif /* Return number of bytes input consumed: 0 for end-of-string. */ return (wch == L'\0' ? 0 : len); } /* * base64_decode - Base64 decode * * This accepts most variations of base-64 encoding, including: * * with or without line breaks * * with or without the final group padded with '=' or '_' characters * (The most economical Base-64 variant does not pad the last group and * omits line breaks; RFC1341 used for MIME requires both.) */ static char * base64_decode(const char *s, size_t len, size_t *out_len) { static const unsigned char digits[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' }; static unsigned char decode_table[128]; char *out, *d; const unsigned char *src = (const unsigned char *)s; /* If the decode table is not yet initialized, prepare it. */ if (decode_table[digits[1]] != 1) { size_t i; memset(decode_table, 0xff, sizeof(decode_table)); for (i = 0; i < sizeof(digits); i++) decode_table[digits[i]] = i; } /* Allocate enough space to hold the entire output. */ /* Note that we may not use all of this... */ out = (char *)malloc(len - len / 4 + 1); if (out == NULL) { *out_len = 0; return (NULL); } d = out; while (len > 0) { /* Collect the next group of (up to) four characters. */ int v = 0; int group_size = 0; while (group_size < 4 && len > 0) { /* '=' or '_' padding indicates final group. */ if (*src == '=' || *src == '_') { len = 0; break; } /* Skip illegal characters (including line breaks) */ if (*src > 127 || *src < 32 || decode_table[*src] == 0xff) { len--; src++; continue; } v <<= 6; v |= decode_table[*src++]; len --; group_size++; } /* Align a short group properly. */ v <<= 6 * (4 - group_size); /* Unpack the group we just collected. */ switch (group_size) { case 4: d[2] = v & 0xff; /* FALLTHROUGH */ case 3: d[1] = (v >> 8) & 0xff; /* FALLTHROUGH */ case 2: d[0] = (v >> 16) & 0xff; break; case 1: /* this is invalid! */ break; } d += group_size * 3 / 4; } *out_len = d - out; return (out); } static char * url_decode(const char *in) { char *out, *d; const char *s; out = (char *)malloc(strlen(in) + 1); if (out == NULL) return (NULL); for (s = in, d = out; *s != '\0'; ) { if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') { /* Try to convert % escape */ int digit1 = tohex(s[1]); int digit2 = tohex(s[2]); if (digit1 >= 0 && digit2 >= 0) { /* Looks good, consume three chars */ s += 3; /* Convert output */ *d++ = ((digit1 << 4) | digit2); continue; } /* Else fall through and treat '%' as normal char */ } *d++ = *s++; } *d = '\0'; return (out); } static int tohex(int c) { if (c >= '0' && c <= '9') return (c - '0'); else if (c >= 'A' && c <= 'F') return (c - 'A' + 10); else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); else return (-1); } tarsnap-1.0.41/libarchive/archive_read_support_format_zip.c000066400000000000000000000616031476705112600242020ustar00rootroot00000000000000/*- * Copyright (c) 2004 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: src/lib/libarchive/archive_read_support_format_zip.c,v 1.28 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef HAVE_ZLIB_H #include #else /* Hmmm... This is necessary, but means that we can't correctly extract * even uncompressed entries on platforms that lack zlib. */ #define crc32(crc, buf, len) (unsigned long)0 #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" struct zip { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_bytes_remaining; int64_t entry_offset; /* 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 version; unsigned system; unsigned flags; unsigned compression; const char * compression_name; time_t mtime; time_t ctime; time_t atime; mode_t mode; uid_t uid; gid_t gid; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; unsigned long crc32; ssize_t filename_length; ssize_t extra_length; int64_t uncompressed_size; int64_t compressed_size; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; #ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif struct archive_string pathname; struct archive_string extra; char format_name[64]; }; #define ZIP_LENGTH_AT_END 8 struct zip_file_header { char signature[4]; char version[2]; char flags[2]; char compression[2]; char timedate[4]; char crc32[4]; char compressed_size[4]; char uncompressed_size[4]; char filename_length[2]; char extra_length[2]; }; static const char *compression_names[] = { "uncompressed", "shrinking", "reduced-1", "reduced-2", "reduced-3", "reduced-4", "imploded", "reserved", "deflation" }; static int archive_read_format_zip_bid(struct archive_read *); static int archive_read_format_zip_cleanup(struct archive_read *); static int archive_read_format_zip_read_data(struct archive_read *, const void **, size_t *, off_t *); static int archive_read_format_zip_read_data_skip(struct archive_read *a); static int archive_read_format_zip_read_header(struct archive_read *, struct archive_entry *); static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, off_t *offset); static int zip_read_data_none(struct archive_read *a, const void **buff, size_t *size, off_t *offset); static int zip_read_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip); static time_t zip_time(const char *); static void process_extra(const void* extra, struct zip* zip); int archive_read_support_format_zip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; zip = (struct zip *)malloc(sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } memset(zip, 0, sizeof(*zip)); r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_bid, NULL, archive_read_format_zip_read_header, archive_read_format_zip_read_data, NULL, NULL, archive_read_format_zip_read_data_skip, archive_read_format_zip_cleanup); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } static int archive_read_format_zip_bid(struct archive_read *a) { const char *p; const void *buff; ssize_t bytes_avail, offset; if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* * Bid of 30 here is: 16 bits for "PK", * next 16-bit field has four options (-2 bits). * 16 + 16-2 = 30. */ 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] == '\007' && p[3] == '\010') || (p[2] == '0' && p[3] == '0')) return (30); } /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward * up to 128k for a 'PK\003\004' marker. */ if (p[0] == 'M' && p[1] == 'Z') { /* * TODO: Optimize by initializing 'offset' to an * estimate of the likely start of the archive data * based on values in the PE header. Note that we * don't need to be exact, but we mustn't skip too * far. The search below will compensate if we * undershoot. */ offset = 0; while (offset < 124000) { /* Get 4k of data beyond where we stopped. */ buff = __archive_read_ahead(a, offset + 4096, &bytes_avail); if (bytes_avail < offset + 1) break; p = (const char *)buff + offset; while (p + 9 < (const char *)buff + bytes_avail) { if (p[0] == 'P' && p[1] == 'K' /* signature */ && p[2] == 3 && p[3] == 4 /* File entry */ && p[8] == 8 /* compression == deflate */ && p[9] == 0 /* High byte of compression */ ) { return (30); } ++p; } offset = p - (const char *)buff; } } return (0); } /* * Search forward for a "PK\003\004" file header. This handles the * case of self-extracting archives, where there is an executable * prepended to the ZIP archive. */ static int skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; size_t skip; ssize_t bytes; /* * TODO: We should be able to skip forward by a bunch * by lifting some values from the PE header. We don't * need to be exact (we're still going to search forward * to find the header), but it will speed things up and * reduce the chance of a false positive. */ for (;;) { h = __archive_read_ahead(a, 4, &bytes); if (bytes < 4) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the zip header. */ while (p + 4 < q) { switch (p[3]) { case '\004': /* TODO: Additional verification here. */ if (memcmp("PK\003\004", p, 4) == 0) { skip = p - (const char *)h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += 4; break; case '\003': p += 1; break; case 'K': p += 2; break; case 'P': p += 3; break; default: p += 4; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); } } static int archive_read_format_zip_read_header(struct archive_read *a, struct archive_entry *entry) { const void *h; const char *signature; struct zip *zip; int r = ARCHIVE_OK, r1; 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); zip->decompress_init = 0; zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; zip->entry_crc32 = crc32(0, NULL, 0); if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; if (signature[0] == 'M' && signature[1] == 'Z') { /* This is an executable? Must be self-extracting... */ r = skip_sfx(a); if (r < ARCHIVE_WARN) return (r); if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; } if (signature[0] != 'P' || signature[1] != 'K') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad ZIP file"); return (ARCHIVE_FATAL); } /* * "PK00" signature is used for "split" archives that * only have a single segment. This means we can just * skip the PK00; the first real file header should follow. */ if (signature[2] == '0' && signature[3] == '0') { __archive_read_consume(a, 4); if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; if (signature[0] != 'P' || signature[1] != 'K') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad ZIP file"); return (ARCHIVE_FATAL); } } if (signature[2] == '\001' && signature[3] == '\002') { /* Beginning of central directory. */ return (ARCHIVE_EOF); } if (signature[2] == '\003' && signature[3] == '\004') { /* Regular file entry. */ r1 = zip_read_file_header(a, entry, zip); if (r1 != ARCHIVE_OK) return (r1); return (r); } if (signature[2] == '\005' && signature[3] == '\006') { /* End-of-archive record. */ return (ARCHIVE_EOF); } if (signature[2] == '\007' && signature[3] == '\010') { /* * We should never encounter this record here; * see ZIP_LENGTH_AT_END handling below for details. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad ZIP file: Unexpected end-of-entry record"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Damaged ZIP file or unsupported format variant (%d,%d)", signature[2], signature[3]); return (ARCHIVE_FATAL); } static int zip_read_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const struct zip_file_header *p; const void *h; if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } zip->version = p->version[0]; zip->system = p->version[1]; zip->flags = archive_le16dec(p->flags); zip->compression = archive_le16dec(p->compression); if (zip->compression < sizeof(compression_names)/sizeof(compression_names[0])) zip->compression_name = compression_names[zip->compression]; else zip->compression_name = "??"; zip->mtime = zip_time(p->timedate); zip->ctime = 0; zip->atime = 0; zip->mode = 0; zip->uid = 0; zip->gid = 0; zip->crc32 = archive_le32dec(p->crc32); zip->filename_length = archive_le16dec(p->filename_length); zip->extra_length = archive_le16dec(p->extra_length); zip->uncompressed_size = archive_le32dec(p->uncompressed_size); zip->compressed_size = archive_le32dec(p->compressed_size); __archive_read_consume(a, sizeof(struct zip_file_header)); /* Read the filename. */ if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL) __archive_errx(1, "Out of memory"); archive_strncpy(&zip->pathname, h, zip->filename_length); __archive_read_consume(a, zip->filename_length); archive_entry_set_pathname(entry, zip->pathname.s); if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/') zip->mode = AE_IFDIR | 0777; else zip->mode = AE_IFREG | 0777; /* Read the extra data. */ if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } process_extra(h, zip); __archive_read_consume(a, zip->extra_length); /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip->mode); archive_entry_set_uid(entry, zip->uid); archive_entry_set_gid(entry, zip->gid); archive_entry_set_mtime(entry, zip->mtime, 0); archive_entry_set_ctime(entry, zip->ctime, 0); archive_entry_set_atime(entry, zip->atime, 0); /* Set the size only if it's meaningful. */ if (0 == (zip->flags & ZIP_LENGTH_AT_END)) archive_entry_set_size(entry, zip->uncompressed_size); zip->entry_bytes_remaining = zip->compressed_size; zip->entry_offset = 0; /* If there's no body, force read_data() to return EOF immediately. */ if (0 == (zip->flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ sprintf(zip->format_name, "ZIP %d.%d (%s)", zip->version / 10, zip->version % 10, zip->compression_name); a->archive.archive_format_name = zip->format_name; return (ARCHIVE_OK); } /* 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); } static int archive_read_format_zip_read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { int r; struct zip *zip; zip = (struct zip *)(a->format->data); /* * If we hit end-of-entry last time, clean up and return * ARCHIVE_EOF this time. */ if (zip->end_of_entry) { *offset = zip->entry_uncompressed_bytes_read; *size = 0; *buff = NULL; return (ARCHIVE_EOF); } switch(zip->compression) { case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); break; case 8: /* Deflate compression. */ r = zip_read_data_deflate(a, buff, size, offset); break; default: /* Unsupported compression. */ *buff = NULL; *size = 0; *offset = 0; /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported ZIP compression method (%s)", zip->compression_name); if (zip->flags & ZIP_LENGTH_AT_END) { /* * ZIP_LENGTH_AT_END requires us to * decompress the entry in order to * skip it, but we don't know this * compression method, so we give up. */ r = ARCHIVE_FATAL; } else { /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ r = ARCHIVE_WARN; } break; } if (r != ARCHIVE_OK) return (r); /* Update checksum */ if (*size) zip->entry_crc32 = crc32(zip->entry_crc32, *buff, *size); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { if (zip->flags & ZIP_LENGTH_AT_END) { const char *p; if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP end-of-file record"); return (ARCHIVE_FATAL); } zip->crc32 = archive_le32dec(p + 4); zip->compressed_size = archive_le32dec(p + 8); zip->uncompressed_size = archive_le32dec(p + 12); __archive_read_consume(a, 16); } /* Check file size, CRC against these values. */ if (zip->compressed_size != zip->entry_compressed_bytes_read) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP compressed data is wrong size"); return (ARCHIVE_WARN); } /* Size field only stores the lower 32 bits of the actual size. */ if ((zip->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"); return (ARCHIVE_WARN); } /* Check computed CRC against header */ if (zip->crc32 != zip->entry_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad CRC: 0x%lx should be 0x%lx", zip->entry_crc32, zip->crc32); return (ARCHIVE_WARN); } } /* Return EOF immediately if this is a non-regular file. */ if (AE_IFREG != (zip->mode & AE_IFMT)) return (ARCHIVE_EOF); return (ARCHIVE_OK); } /* * Read "uncompressed" data. According to the current specification, * if ZIP_LENGTH_AT_END is specified, then the size fields in the * initial file header are supposed to be set to zero. This would, of * course, make it impossible for us to read the archive, since we * couldn't determine the end of the file data. Info-ZIP seems to * include the real size fields both before and after the data in this * case (the CRC only appears afterwards), so this works as you would * expect. * * 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, off_t *offset) { struct zip *zip; ssize_t bytes_avail; zip = (struct zip *)(a->format->data); if (zip->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = zip->entry_offset; zip->end_of_entry = 1; return (ARCHIVE_OK); } /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ *buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = zip->entry_bytes_remaining; __archive_read_consume(a, bytes_avail); *size = bytes_avail; *offset = zip->entry_offset; zip->entry_offset += *size; zip->entry_bytes_remaining -= *size; zip->entry_uncompressed_bytes_read += *size; zip->entry_compressed_bytes_read += *size; return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { struct zip *zip; ssize_t bytes_avail; const void *compressed_buff; int r; 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 = 32 * 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); } } /* 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; } /* * 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 = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); return (ARCHIVE_FATAL); } /* * 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 = bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = zip->uncompressed_buffer; zip->stream.avail_out = 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; __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; *offset = zip->entry_offset; *size = zip->stream.total_out; zip->entry_uncompressed_bytes_read += *size; *buff = zip->uncompressed_buffer; zip->entry_offset += *size; return (ARCHIVE_OK); } #else static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, off_t *offset) { *buff = NULL; *size = 0; *offset = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "libarchive compiled without deflate support (no libz)"); return (ARCHIVE_FATAL); } #endif static int archive_read_format_zip_read_data_skip(struct archive_read *a) { struct zip *zip; const void *buff = NULL; off_t bytes_skipped; zip = (struct zip *)(a->format->data); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* * If the length is at the end, we have no choice but * to decompress all the data to find the end marker. */ if (zip->flags & ZIP_LENGTH_AT_END) { size_t size; off_t offset; int r; do { r = archive_read_format_zip_read_data(a, &buff, &size, &offset); } while (r == ARCHIVE_OK); return (r); } /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = __archive_read_skip(a, zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* This entry is finished and done. */ zip->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_zip_cleanup(struct archive_read *a) { struct zip *zip; zip = (struct zip *)(a->format->data); #ifdef HAVE_ZLIB_H if (zip->stream_valid) inflateEnd(&zip->stream); #endif free(zip->uncompressed_buffer); archive_string_free(&(zip->pathname)); archive_string_free(&(zip->extra)); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * 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 void* extra, struct zip* zip) { int offset = 0; const char *p = (const char *)extra; while (offset < zip->extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > zip->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. */ if (datasize >= 8) zip->uncompressed_size = archive_le64dec(p + offset); if (datasize >= 16) zip->compressed_size = archive_le64dec(p + offset + 8); break; 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->mtime, archive_le32dec(p + offset)); #endif if (datasize < 4) break; zip->mtime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x02) { if (datasize < 4) break; zip->atime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x04) { if (datasize < 4) break; zip->ctime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } 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->uid = archive_le16dec(p + offset); if (datasize >= 4) zip->gid = archive_le16dec(p + offset + 2); break; default: break; } offset += datasize; } #ifdef DEBUG if (offset != zip->extra_length) { fprintf(stderr, "Extra data field contents do not match reported size!\n"); } #endif } tarsnap-1.0.41/libarchive/archive_string.c000066400000000000000000000305501476705112600205440ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_string.c,v 1.17 2008/12/06 05:56:43 kientzle Exp $"); /* * Basic resizable string support, to simplify manipulating arbitrary-sized * strings while minimizing heap activity. */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) #include #endif #include "archive_private.h" #include "archive_string.h" struct archive_string * __archive_string_append(struct archive_string *as, const char *p, size_t s) { if (__archive_string_ensure(as, as->length + s + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(as->s + as->length, p, s); as->s[as->length + s] = 0; as->length += s; return (as); } void __archive_string_copy(struct archive_string *dest, struct archive_string *src) { if (src->length == 0) dest->length = 0; else { if (__archive_string_ensure(dest, src->length + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(dest->s, src->s, src->length); dest->length = src->length; dest->s[dest->length] = 0; } } void __archive_string_concat(struct archive_string *dest, struct archive_string *src) { if (src->length > 0) { if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(dest->s + dest->length, src->s, src->length); dest->length += src->length; dest->s[dest->length] = 0; } } void __archive_string_free(struct archive_string *as) { as->length = 0; as->buffer_length = 0; if (as->s != NULL) { free(as->s); as->s = NULL; } } /* Returns NULL on any allocation failure. */ struct archive_string * __archive_string_ensure(struct archive_string *as, size_t s) { char *p; size_t new_length; /* If buffer is already big enough, don't reallocate. */ if (as->s && (s <= as->buffer_length)) return (as); /* * Growing the buffer at least exponentially ensures that * append operations are always linear in the number of * characters appended. Using a smaller growth rate for * larger buffers reduces memory waste somewhat at the cost of * a larger constant factor. */ if (as->buffer_length < 32) /* Start with a minimum 32-character buffer. */ new_length = 32; else if (as->buffer_length < 8192) /* Buffers under 8k are doubled for speed. */ new_length = as->buffer_length + as->buffer_length; else { /* Buffers 8k and over grow by at least 25% each time. */ new_length = as->buffer_length + as->buffer_length / 4; /* Be safe: If size wraps, fail. */ if (new_length < as->buffer_length) { /* On failure, wipe the string and return NULL. */ __archive_string_free(as); return (NULL); } } /* * The computation above is a lower limit to how much we'll * grow the buffer. In any case, we have to grow it enough to * hold the request. */ if (new_length < s) new_length = s; /* Now we can reallocate the buffer. */ p = (char *)realloc(as->s, new_length); if (p == NULL) { /* On failure, wipe the string and return NULL. */ __archive_string_free(as); return (NULL); } as->s = p; as->buffer_length = new_length; return (as); } struct archive_string * __archive_strncat(struct archive_string *as, const void *_p, size_t n) { size_t s; const char *p, *pp; p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (*pp && s < n) { pp++; s++; } return (__archive_string_append(as, p, s)); } struct archive_string * __archive_strappend_char(struct archive_string *as, char c) { return (__archive_string_append(as, &c, 1)); } /* * Translates a wide character string into UTF-8 and appends * to the archive_string. Note: returns NULL if conversion fails, * but still leaves a best-effort conversion in the argument as. */ struct archive_string * __archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) { char *p; unsigned wc; char buff[256]; struct archive_string *return_val = as; /* * Convert one wide char at a time into 'buff', whenever that * fills, append it to the string. */ p = buff; while (*w != L'\0') { /* Flush the buffer when we have <=16 bytes free. */ /* (No encoding has a single character >16 bytes.) */ if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) { *p = '\0'; archive_strcat(as, buff); p = buff; } wc = *w++; /* If this is a surrogate pair, assemble the full code point.*/ /* Note: wc must not be wchar_t here, because the full code * point can be more than 16 bits! */ if (wc >= 0xD800 && wc <= 0xDBff && *w >= 0xDC00 && *w <= 0xDFFF) { wc -= 0xD800; wc *= 0x400; wc += (*w - 0xDC00); wc += 0x10000; ++w; } /* Translate code point to UTF8 */ if (wc <= 0x7f) { *p++ = (char)wc; } else if (wc <= 0x7ff) { *p++ = 0xc0 | ((wc >> 6) & 0x1f); *p++ = 0x80 | (wc & 0x3f); } else if (wc <= 0xffff) { *p++ = 0xe0 | ((wc >> 12) & 0x0f); *p++ = 0x80 | ((wc >> 6) & 0x3f); *p++ = 0x80 | (wc & 0x3f); } else if (wc <= 0x1fffff) { *p++ = 0xf0 | ((wc >> 18) & 0x07); *p++ = 0x80 | ((wc >> 12) & 0x3f); *p++ = 0x80 | ((wc >> 6) & 0x3f); *p++ = 0x80 | (wc & 0x3f); } else { /* Unicode has no codes larger than 0x1fffff. */ /* TODO: use \uXXXX escape here instead of ? */ *p++ = '?'; return_val = NULL; } } *p = '\0'; archive_strcat(as, buff); return (return_val); } static int utf8_to_unicode(int *pwc, const char *s, size_t n) { int ch; /* * 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. */ } if ((ch & 0x80) == 0) { *pwc = ch & 0x7f; return (1); } if ((ch & 0xe0) == 0xc0) { if (n < 2) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (2); } if ((ch & 0xf0) == 0xe0) { if (n < 3) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); return (3); } if ((ch & 0xf8) == 0xf0) { 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); *pwc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); return (4); } /* Invalid first byte. */ return (-1); } /* * Return a wide-character Unicode string by converting this archive_string * from UTF-8. We assume that systems with 16-bit wchar_t always use * UTF16 and systems with 32-bit wchar_t can accept UCS4. */ wchar_t * __archive_string_utf8_w(struct archive_string *as) { wchar_t *ws, *dest; int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */ const char *src, *end; int n; ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t)); if (ws == NULL) __archive_errx(1, "Out of memory"); dest = ws; src = as->s; end = as->s + as->buffer_length; while (*src != '\0') { n = utf8_to_unicode(&wc, src, end - src); if (n == 0) break; if (n < 0) { free(ws); return (NULL); } src += n; if (wc >= 0xD800 && wc <= 0xDBFF) { /* This is a leading surrogate; some idiot * has translated UTF16 to UTF8 without combining * surrogates; rebuild the full code point before * continuing. */ n = utf8_to_unicode(&wc2, src, end - src); if (n < 0) { free(ws); return (NULL); } if (n == 0) /* Ignore the leading surrogate */ break; if (wc2 < 0xDC00 || wc2 > 0xDFFF) { /* If the second character isn't a * trailing surrogate, then someone * has really screwed up and this is * invalid. */ free(ws); return (NULL); } else { src += n; wc -= 0xD800; wc *= 0x400; wc += wc2 - 0xDC00; wc += 0x10000; } } if ((sizeof(wchar_t) < 4) && (wc > 0xffff)) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ wc -= 0x10000; *dest++ = ((wc >> 10) & 0x3ff) + 0xD800; *dest++ = (wc & 0x3ff) + 0xDC00; } else *dest++ = wc; } *dest++ = L'\0'; return (ws); } #if defined(_WIN32) && !defined(__CYGWIN__) /* * Translates a wide character string into current locale character set * and appends to the archive_string. Note: returns NULL if conversion * fails. * * Win32 builds use WideCharToMultiByte from the Windows API. * (Maybe Cygwin should too? WideCharToMultiByte will know a * lot more about local character encodings than the wcrtomb() * wrapper is going to know.) */ struct archive_string * __archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) { char *p; int l, wl; BOOL useDefaultChar = FALSE; wl = (int)wcslen(w); l = wl * 4 + 4; p = malloc(l); if (p == NULL) __archive_errx(1, "Out of memory"); /* To check a useDefaultChar is to simulate error handling of * the my_wcstombs() which is running on non Windows system with * wctomb(). * And to set NULL for last argument is necessary when a codepage * is not CP_ACP(current locale). */ l = WideCharToMultiByte(CP_ACP, 0, w, wl, p, l, NULL, &useDefaultChar); if (l == 0) { free(p); return (NULL); } __archive_string_append(as, p, l); free(p); return (as); } #else /* * Translates a wide character string into current locale character set * and appends to the archive_string. Note: returns NULL if conversion * fails. * * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion * one character at a time. If a non-Windows platform doesn't have * either of these, fall back to the built-in UTF8 conversion. */ struct archive_string * __archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) { #if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB) /* If there's no built-in locale support, fall back to UTF8 always. */ return __archive_strappend_w_utf8(as, w); #else /* We cannot use the standard wcstombs() here because it * cannot tell us how big the output buffer should be. So * I've built a loop around wcrtomb() or wctomb() that * converts a character at a time and resizes the string as * needed. We prefer wcrtomb() when it's available because * it's thread-safe. */ int n; char *p; char buff[256]; #if HAVE_WCRTOMB mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ wctomb(NULL, L'\0'); #endif /* * Convert one wide char at a time into 'buff', whenever that * fills, append it to the string. */ p = buff; while (*w != L'\0') { /* Flush the buffer when we have <=16 bytes free. */ /* (No encoding has a single character >16 bytes.) */ if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - MB_CUR_MAX)) { *p = '\0'; archive_strcat(as, buff); p = buff; } #if HAVE_WCRTOMB n = wcrtomb(p, *w++, &shift_state); #else n = wctomb(p, *w++); #endif if (n == -1) return (NULL); p += n; } *p = '\0'; archive_strcat(as, buff); return (as); #endif } #endif /* _WIN32 && ! __CYGWIN__ */ tarsnap-1.0.41/libarchive/archive_string.h000066400000000000000000000127231476705112600205530ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.13 2008/12/06 05:56:43 kientzle Exp $ * */ #ifndef ARCHIVE_STRING_H_INCLUDED #define ARCHIVE_STRING_H_INCLUDED #include #ifdef HAVE_STDLIB_H #include /* required for wchar_t on some systems */ #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif /* * Basic resizable/reusable string support a la Java's "StringBuffer." * * Unlike sbuf(9), the buffers here are fully reusable and track the * length throughout. * * Note that all visible symbols here begin with "__archive" as they * are internal symbols not intended for anyone outside of this library * to see or use. */ struct archive_string { char *s; /* Pointer to the storage */ size_t length; /* Length of 's' */ size_t buffer_length; /* Length of malloc-ed storage */ }; /* Initialize an archive_string object on the stack or elsewhere. */ #define archive_string_init(a) \ do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0) /* Append a C char to an archive_string, resizing as necessary. */ struct archive_string * __archive_strappend_char(struct archive_string *, char); #define archive_strappend_char __archive_strappend_char /* Convert a wide-char string to UTF-8 and append the result. */ struct archive_string * __archive_strappend_w_utf8(struct archive_string *, const wchar_t *); #define archive_strappend_w_utf8 __archive_strappend_w_utf8 /* Convert a wide-char string to current locale and append the result. */ /* Returns NULL if conversion fails. */ struct archive_string * __archive_strappend_w_mbs(struct archive_string *, const wchar_t *); #define archive_strappend_w_mbs __archive_strappend_w_mbs /* Basic append operation. */ struct archive_string * __archive_string_append(struct archive_string *as, const char *p, size_t s); /* Copy one archive_string to another */ void __archive_string_copy(struct archive_string *dest, struct archive_string *src); #define archive_string_copy(dest, src) \ __archive_string_copy(dest, src) /* Concatenate one archive_string to another */ void __archive_string_concat(struct archive_string *dest, struct archive_string *src); #define archive_string_concat(dest, src) \ __archive_string_concat(dest, src) /* Ensure that the underlying buffer is at least as large as the request. */ struct archive_string * __archive_string_ensure(struct archive_string *, size_t); #define archive_string_ensure __archive_string_ensure /* Append C string, which may lack trailing \0. */ /* The source is declared void * here because this gets used with * "signed char *", "unsigned char *" and "char *" arguments. * Declaring it "char *" as with some of the other functions just * leads to a lot of extra casts. */ struct archive_string * __archive_strncat(struct archive_string *, const void *, size_t); #define archive_strncat __archive_strncat /* Append a C string to an archive_string, resizing as necessary. */ #define archive_strcat(as,p) __archive_string_append((as),(p),strlen(p)) /* Copy a C string to an archive_string, resizing as necessary. */ #define archive_strcpy(as,p) \ ((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p))) /* Copy a C string to an archive_string with limit, resizing as necessary. */ #define archive_strncpy(as,p,l) \ ((as)->length=0, archive_strncat((as), (p), (l))) /* Return length of string. */ #define archive_strlen(a) ((a)->length) /* Set string length to zero. */ #define archive_string_empty(a) ((a)->length = 0) /* Release any allocated storage resources. */ void __archive_string_free(struct archive_string *); #define archive_string_free __archive_string_free /* Like 'vsprintf', but resizes the underlying string as necessary. */ void __archive_string_vsprintf(struct archive_string *, const char *, va_list); #define archive_string_vsprintf __archive_string_vsprintf void __archive_string_sprintf(struct archive_string *, const char *, ...); #define archive_string_sprintf __archive_string_sprintf /* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it. * Returns NULL if conversion failed in any way. */ wchar_t *__archive_string_utf8_w(struct archive_string *as); #endif tarsnap-1.0.41/libarchive/archive_string_sprintf.c000066400000000000000000000110761476705112600223130ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_string_sprintf.c,v 1.10 2008/03/14 22:00:09 kientzle Exp $"); /* * The use of printf()-family functions can be troublesome * for space-constrained applications. In addition, correctly * implementing this function in terms of vsnprintf() requires * two calls (one to determine the size, another to format the * result), which in turn requires duplicating the argument list * using va_copy, which isn't yet universally available. * * So, I've implemented a bare minimum of printf()-like capability * here. This is only used to format error messages, so doesn't * require any floating-point support or field-width handling. */ #include #include "archive_string.h" #include "archive_private.h" /* * Utility functions to format signed/unsigned integers and append * them to an archive_string. */ static void append_uint(struct archive_string *as, uintmax_t d, unsigned base) { static const char *digits = "0123456789abcdef"; if (d >= base) append_uint(as, d/base, base); archive_strappend_char(as, digits[d % base]); } static void append_int(struct archive_string *as, intmax_t d, unsigned base) { uintmax_t ud; if (d < 0) { archive_strappend_char(as, '-'); ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d); } else ud = d; append_uint(as, ud, base); } void __archive_string_sprintf(struct archive_string *as, const char *fmt, ...) { va_list ap; va_start(ap, fmt); archive_string_vsprintf(as, fmt, ap); va_end(ap); } /* * Like 'vsprintf', but ensures the target is big enough, resizing if * necessary. */ void __archive_string_vsprintf(struct archive_string *as, const char *fmt, va_list ap) { char long_flag; intmax_t s; /* Signed integer temp. */ uintmax_t u; /* Unsigned integer temp. */ const char *p, *p2; if (__archive_string_ensure(as, 64) == NULL) __archive_errx(1, "Out of memory"); if (fmt == NULL) { as->s[0] = 0; return; } for (p = fmt; *p != '\0'; p++) { const char *saved_p = p; if (*p != '%') { archive_strappend_char(as, *p); continue; } p++; long_flag = '\0'; switch(*p) { case 'j': long_flag = 'j'; p++; break; case 'l': long_flag = 'l'; p++; break; } switch (*p) { case '%': __archive_strappend_char(as, '%'); break; case 'c': s = va_arg(ap, int); __archive_strappend_char(as, s); break; case 'd': switch(long_flag) { case 'j': s = va_arg(ap, intmax_t); break; case 'l': s = va_arg(ap, long); break; default: s = va_arg(ap, int); break; } append_int(as, s, 10); break; case 's': p2 = va_arg(ap, char *); archive_strcat(as, p2); break; case 'o': case 'u': case 'x': case 'X': /* Common handling for unsigned integer formats. */ switch(long_flag) { case 'j': u = va_arg(ap, uintmax_t); break; case 'l': u = va_arg(ap, unsigned long); break; default: u = va_arg(ap, unsigned int); break; } /* Format it in the correct base. */ switch (*p) { case 'o': append_uint(as, u, 8); break; case 'u': append_uint(as, u, 10); break; default: append_uint(as, u, 16); break; } break; default: /* Rewind and print the initial '%' literally. */ p = saved_p; archive_strappend_char(as, *p); } } } tarsnap-1.0.41/libarchive/archive_util.c000066400000000000000000000213021476705112600202060ustar00rootroot00000000000000/*- * Copyright (c) 2009 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.19 2008/10/21 12:10:30 des Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_string.h" static void errmsg(const char *m) { size_t s = strlen(m); ssize_t written; while (s > 0) { written = write(2, m, strlen(m)); if (written <= 0) return; m += written; s -= written; } } #if ARCHIVE_VERSION_NUMBER < 3000000 /* These disappear in libarchive 3.0 */ /* Deprecated. */ int archive_api_feature(void) { return (ARCHIVE_API_FEATURE); } /* Deprecated. */ int archive_api_version(void) { return (ARCHIVE_API_VERSION); } /* Deprecated synonym for archive_version_number() */ int archive_version_stamp(void) { return (archive_version_number()); } /* Deprecated synonym for archive_version_string() */ const char * archive_version(void) { return (archive_version_string()); } #endif int archive_version_number(void) { return (ARCHIVE_VERSION_NUMBER); } const char * archive_version_string(void) { return (ARCHIVE_VERSION_STRING); } int archive_errno(struct archive *a) { return (a->archive_error_number); } const char * archive_error_string(struct archive *a) { if (a->error != NULL && *a->error != '\0') return (a->error); else return ("(Empty error message)"); } int archive_file_count(struct archive *a) { return (a->file_count); } int archive_format(struct archive *a) { return (a->archive_format); } const char * archive_format_name(struct archive *a) { return (a->archive_format_name); } int archive_compression(struct archive *a) { return (a->compression_code); } const char * archive_compression_name(struct archive *a) { return (a->compression_name); } /* * Return a count of the number of compressed bytes processed. */ int64_t archive_position_compressed(struct archive *a) { return (a->raw_position); } /* * Return a count of the number of uncompressed bytes processed. */ int64_t archive_position_uncompressed(struct archive *a) { return (a->file_position); } void archive_clear_error(struct archive *a) { archive_string_empty(&a->error_string); a->error = NULL; a->archive_error_number = 0; } void archive_set_error(struct archive *a, int error_number, const char *fmt, ...) { va_list ap; a->archive_error_number = error_number; if (fmt == NULL) { a->error = NULL; return; } va_start(ap, fmt); archive_string_vsprintf(&(a->error_string), fmt, ap); va_end(ap); a->error = a->error_string.s; } void archive_copy_error(struct archive *dest, struct archive *src) { dest->archive_error_number = src->archive_error_number; archive_string_copy(&dest->error_string, &src->error_string); dest->error = dest->error_string.s; } void __archive_errx(int retvalue, const char *msg) { static const char *msg1 = "Fatal Internal Error in libarchive: "; errmsg(msg1); errmsg(msg); errmsg("\n"); exit(retvalue); } /* * Parse option strings * Detail of option format. * - The option can accept: * "opt-name", "!opt-name", "opt-name=value". * * - The option entries are separated by comma. * e.g "compression=9,opt=XXX,opt-b=ZZZ" * * - The name of option string consist of '-' and alphabet * but character '-' cannot be used for the first character. * (Regular expression is [a-z][-a-z]+) * * - For a specific format/filter, using the format name with ':'. * e.g "zip:compression=9" * (This "compression=9" option entry is for "zip" format only) * * If another entries follow it, those are not for * the specfic format/filter. * e.g handle "zip:compression=9,opt=XXX,opt-b=ZZZ" * "zip" format/filter handler will get "compression=9" * all format/filter handler will get "opt=XXX" * all format/filter handler will get "opt-b=ZZZ" * * - Whitespace and tab are bypassed. * */ int __archive_parse_options(const char *p, const char *fn, int keysize, char *key, int valsize, char *val) { const char *p_org; int apply; int kidx, vidx; int negative; enum { /* Requested for initialization. */ INIT, /* Finding format/filter-name and option-name. */ F_BOTH, /* Finding option-name only. * (already detected format/filter-name) */ F_NAME, /* Getting option-value. */ G_VALUE, } state; p_org = p; state = INIT; kidx = vidx = negative = 0; apply = 1; while (*p) { switch (state) { case INIT: kidx = vidx = 0; negative = 0; apply = 1; state = F_BOTH; break; case F_BOTH: case F_NAME: if ((*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '-') { if (kidx == 0 && !(*p >= 'a' && *p <= 'z')) /* Illegal sequence. */ return (-1); if (kidx >= keysize -1) /* Too many characters. */ return (-1); key[kidx++] = *p++; } else if (*p == '!') { if (kidx != 0) /* Illegal sequence. */ return (-1); negative = 1; ++p; } else if (*p == ',') { if (kidx == 0) /* Illegal sequence. */ return (-1); if (!negative) val[vidx++] = '1'; /* We have got boolean option data. */ ++p; if (apply) goto complete; else /* This option does not apply to the * format which the fn variable * indicate. */ state = INIT; } else if (*p == ':') { /* obuf data is format name */ if (state == F_NAME) /* We already found it. */ return (-1); if (kidx == 0) /* Illegal sequence. */ return (-1); if (negative) /* We cannot accept "!format-name:". */ return (-1); key[kidx] = '\0'; if (strcmp(fn, key) != 0) /* This option does not apply to the * format which the fn variable * indicate. */ apply = 0; kidx = 0; ++p; state = F_NAME; } else if (*p == '=') { if (kidx == 0) /* Illegal sequence. */ return (-1); if (negative) /* We cannot accept "!opt-name=value". */ return (-1); ++p; state = G_VALUE; } else if (*p == ' ') { /* Pass the space character */ ++p; } else { /* Illegal character. */ return (-1); } break; case G_VALUE: if (*p == ',') { if (vidx == 0) /* Illegal sequence. */ return (-1); /* We have got option data. */ ++p; if (apply) goto complete; else /* This option does not apply to the * format which the fn variable * indicate. */ state = INIT; } else if (*p == ' ') { /* Pass the space character */ ++p; } else { if (vidx >= valsize -1) /* Too many characters. */ return (-1); val[vidx++] = *p++; } break; } } switch (state) { case F_BOTH: case F_NAME: if (kidx != 0) { if (!negative) val[vidx++] = '1'; /* We have got boolean option. */ if (apply) /* This option apply to the format which the * fn variable indicate. */ goto complete; } break; case G_VALUE: if (vidx == 0) /* Illegal sequence. */ return (-1); /* We have got option value. */ if (apply) /* This option apply to the format which the fn * variable indicate. */ goto complete; break; case INIT:/* nothing */ break; } /* End of Option string. */ return (0); complete: key[kidx] = '\0'; val[vidx] = '\0'; /* Return a size which we've consumed for detecting option */ return ((int)(p - p_org)); } tarsnap-1.0.41/libarchive/archive_virtual.c000066400000000000000000000055011476705112600207220ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_virtual.c,v 1.1 2007/03/03 07:37:36 kientzle Exp $"); #include "archive.h" #include "archive_entry.h" #include "archive_private.h" int archive_write_close(struct archive *a) { return ((a->vtable->archive_close)(a)); } int archive_read_close(struct archive *a) { return ((a->vtable->archive_close)(a)); } #if ARCHIVE_API_VERSION > 1 int archive_write_finish(struct archive *a) { if (a == NULL) return (ARCHIVE_OK); return ((a->vtable->archive_finish)(a)); } #else /* Temporarily allow library to compile with either 1.x or 2.0 API. */ void archive_write_finish(struct archive *a) { if (a == NULL) return (ARCHIVE_OK); (void)(a->vtable->archive_finish)(a); } #endif int archive_read_finish(struct archive *a) { if (a == NULL) return (ARCHIVE_OK); return ((a->vtable->archive_finish)(a)); } int archive_write_header(struct archive *a, struct archive_entry *entry) { ++a->file_count; return ((a->vtable->archive_write_header)(a, entry)); } int archive_write_finish_entry(struct archive *a) { return ((a->vtable->archive_write_finish_entry)(a)); } #if ARCHIVE_API_VERSION > 1 ssize_t #else /* Temporarily allow library to compile with either 1.x or 2.0 API. */ int #endif archive_write_data(struct archive *a, const void *buff, size_t s) { return ((a->vtable->archive_write_data)(a, buff, s)); } ssize_t archive_write_data_block(struct archive *a, const void *buff, size_t s, off_t o) { return ((a->vtable->archive_write_data_block)(a, buff, s, o)); } tarsnap-1.0.41/libarchive/archive_windows.c000066400000000000000000000636141476705112600207370ustar00rootroot00000000000000/*- * Copyright (c) 2009 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Kees Zeelenberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * A set of compatibility glue for building libarchive on Windows platforms. * * Originally created as "libarchive-nonposix.c" by Kees Zeelenberg * for the GnuWin32 project, trimmed significantly by Tim Kientzle. * * Much of the original file was unnecessary for libarchive, because * many of the features it emulated were not strictly necessary for * libarchive. I hope for this to shrink further as libarchive * internals are gradually reworked to sit more naturally on both * POSIX and Windows. Any ideas for this are greatly appreciated. * * The biggest remaining issue is the dev/ino emulation; libarchive * has a couple of public APIs that rely on dev/ino uniquely * identifying a file. This doesn't match well with Windows. I'm * considering alternative APIs. */ #if defined(_WIN32) && !defined(__CYGWIN__) #define _WIN32_WINNT 0x0500 #define WINVER 0x0500 #include "archive_platform.h" #include #include #include #include #include #include #include #include #define EPOC_TIME (116444736000000000ULL) struct ustat { int64_t st_atime; uint32_t st_atime_nsec; int64_t st_ctime; uint32_t st_ctime_nsec; int64_t st_mtime; uint32_t st_mtime_nsec; gid_t st_gid; /* 64bits ino */ int64_t st_ino; mode_t st_mode; uint32_t st_nlink; uint64_t st_size; uid_t st_uid; dev_t st_dev; dev_t st_rdev; }; /* Transform 64-bits ino into 32-bits by hashing. * You do not forget that really unique number size is 64-bits. */ #define INOSIZE (8*sizeof(ino_t)) /* 32 */ static __inline ino_t getino(struct ustat *ub) { ULARGE_INTEGER ino64; ino64.QuadPart = ub->st_ino; /* I don't know this hashing is correct way */ return (ino64.LowPart ^ (ino64.LowPart >> INOSIZE)); } /* * Prepend "\\?\" to the path name and convert it to unicode to permit * an extended-length path for a maximum total path length of 32767 * characters. * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static wchar_t * permissive_name(const char *name) { wchar_t *wn, *wnp; wchar_t *ws, *wsp; size_t l, len, slen; int unc; len = strlen(name); wn = malloc((len + 1) * sizeof(wchar_t)); if (wn == NULL) return (NULL); l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len); if (l == 0) { free(wn); return (NULL); } wn[l] = L'\0'; /* Get a full path names */ l = GetFullPathNameW(wn, 0, NULL, NULL); if (l == 0) { free(wn); return (NULL); } wnp = malloc(l * sizeof(wchar_t)); if (wnp == NULL) { free(wn); return (NULL); } len = GetFullPathNameW(wn, l, wnp, NULL); free(wn); wn = wnp; if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] == L'?' && wnp[3] == L'\\') /* We have already permissive names. */ return (wn); if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] == L'.' && wnp[3] == L'\\') { /* Device names */ 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 names. */ return (wn); } unc = 0; if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') { wchar_t *p = &wnp[2]; /* Skip server-name letters. */ while (*p != L'\\' && *p != L'\0') ++p; if (*p == L'\\') { 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\" */ wnp += 2; len -= 2; unc = 1; } } } slen = 4 + (unc * 4) + len + 1; ws = wsp = malloc(slen * sizeof(wchar_t)); if (ws == NULL) { free(wn); return (NULL); } /* prepend "\\?\" */ wcsncpy(wsp, L"\\\\?\\", 4); wsp += 4; slen -= 4; if (unc) { /* append "UNC\" ---> "\\?\UNC\" */ wcsncpy(wsp, L"UNC\\", 4); wsp += 4; slen -= 4; } wcsncpy_s(wsp, slen, wnp, _TRUNCATE); free(wn); return (ws); } static HANDLE la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { wchar_t *wpath; HANDLE handle; handle = CreateFileA(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); if (handle != INVALID_HANDLE_VALUE) return (handle); if (GetLastError() != ERROR_PATH_NOT_FOUND) return (handle); wpath = permissive_name(path); if (wpath == NULL) return (handle); handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); free(wpath); return (handle); } static size_t wequallen(const wchar_t *s1, const wchar_t *s2) { size_t i = 0; while (*s1 != L'\0' && *s2 != L'\0' && *s1 == *s2) { ++s1; ++s2; ++i; } return (i); } /* Check that path1 and path2 can be hard-linked by each other. * Both arguments must be made by permissive_name function. */ static int canHardLinkW(const wchar_t *path1, const wchar_t *path2) { wchar_t root[MAX_PATH]; wchar_t fs[32]; const wchar_t *s; int r; r = wequallen(path1, path2); /* Is volume-name the same? */ if (r < 7) return (0); if (wcsncmp(path1, L"\\\\?\\UNC\\", 8) == 0) { int len; s = path1 + 8; if (*s == L'\\') return (0); /* 012345678 * Name : "\\?\UNC\Server\Share\" * ^ search */ s = wcschr(++s, L'\\'); if (s == NULL) return (0); if (*++s == L'\\') return (0); /* 012345678 * Name : "\\?\UNC\Server\Share\" * ^ search */ s = wcschr(++s, L'\\'); if (s == NULL) return (0); s++; /* 012345678 * Name : "\\?\UNC\Server\Share\xxxx" * ^--- len ----^ */ len = (int)(s - path1 - 8); /* Is volume-name the same? */ if (r < len + 8) return (0); /* Is volume-name too long? */ if (sizeof(root) -3 < len) return (0); root[0] = root[1] = L'\\'; wcsncpy(root + 2, path1 + 8, len); /* root : "\\Server\Share\" */ root[2 + len] = L'\0'; } else if (wcsncmp(path1, L"\\\\?\\", 4) == 0) { s = path1 + 4; if ((!iswalpha(*s)) || s[1] != L':' || s[2] != L'\\') return (0); wcsncpy(root, path1 + 4, 3); root[3] = L'\0'; } else return (0); if (!GetVolumeInformationW(root, NULL, 0, NULL, NULL, NULL, fs, sizeof(fs))) return (0); if (wcscmp(fs, L"NTFS") == 0) return (1); else return (0); } /* Make a link to src called dst. */ static int __link(const char *src, const char *dst, int sym) { wchar_t *wsrc, *wdst; int res, retval; DWORD attr; if (src == NULL || dst == NULL) { set_errno (EINVAL); return -1; } wsrc = permissive_name(src); wdst = permissive_name(dst); if (wsrc == NULL || wdst == NULL) { if (wsrc != NULL) free(wsrc); if (wdst != NULL) free(wdst); set_errno (EINVAL); return -1; } if ((attr = GetFileAttributesW(wsrc)) != -1) { if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { errno = EPERM; retval = -1; goto exit; } if (!sym && canHardLinkW(wsrc, wdst)) res = CreateHardLinkW(wdst, wsrc, NULL); else res = CopyFileW(wsrc, wdst, FALSE); } else { /* wsrc does not exist; try src prepend it with the dirname of wdst */ wchar_t *wnewsrc, *slash; int i, n, slen, wlen; if (strlen(src) >= 3 && isalpha((unsigned char)src[0]) && src[1] == ':' && src[2] == '\\') { /* Original src name is already full-path */ retval = -1; goto exit; } if (src[0] == '\\') { /* Original src name is almost full-path * (maybe src name is without drive) */ retval = -1; goto exit; } wnewsrc = malloc ((wcslen(wsrc) + wcslen(wdst) + 1) * sizeof(wchar_t)); if (wnewsrc == NULL) { errno = ENOMEM; retval = -1; goto exit; } /* Copying a dirname of wdst */ wcscpy(wnewsrc, wdst); slash = wcsrchr(wnewsrc, L'\\'); if (slash != NULL) *++slash = L'\0'; else wcscat(wnewsrc, L"\\"); /* Converting multi-byte src to wide-char src */ wlen = wcslen(wsrc); slen = strlen(src); n = MultiByteToWideChar(CP_ACP, 0, src, slen, wsrc, slen); if (n == 0) { free (wnewsrc); retval = -1; goto exit; } for (i = 0; i < n; i++) if (wsrc[i] == L'/') wsrc[i] = L'\\'; wcsncat(wnewsrc, wsrc, n); /* Check again */ attr = GetFileAttributesW(wnewsrc); if (attr == -1 || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { if (attr == -1) _dosmaperr(GetLastError()); else errno = EPERM; free (wnewsrc); retval = -1; goto exit; } if (!sym && canHardLinkW(wnewsrc, wdst)) res = CreateHardLinkW(wdst, wnewsrc, NULL); else res = CopyFileW(wnewsrc, wdst, FALSE); free (wnewsrc); } if (res == 0) { _dosmaperr(GetLastError()); retval = -1; } else retval = 0; exit: free(wsrc); free(wdst); return (retval); } /* Make a hard link to src called dst. */ int link(const char *src, const char *dst) { return __link (src, dst, 0); } /* Make a symbolic link to FROM called TO. */ int symlink (from, to) const char *from; const char *to; { return __link (from, to, 1); } int ftruncate(int fd, off_t length) { LARGE_INTEGER distance; HANDLE handle; if (fd < 0) { errno = EBADF; return (-1); } handle = (HANDLE)_get_osfhandle(fd); if (GetFileType(handle) != FILE_TYPE_DISK) { errno = EBADF; return (-1); } distance.QuadPart = length; if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN)) { _dosmaperr(GetLastError()); return (-1); } if (!SetEndOfFile(handle)) { _dosmaperr(GetLastError()); return (-1); } return (0); } #define WINTIME(sec, usec) ((Int32x32To64(sec, 10000000) + EPOC_TIME) + (usec * 10)) static int __hutimes(HANDLE handle, const struct __timeval *times) { ULARGE_INTEGER wintm; FILETIME fatime, fmtime; wintm.QuadPart = WINTIME(times[0].tv_sec, times[0].tv_usec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; wintm.QuadPart = WINTIME(times[1].tv_sec, times[1].tv_usec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; if (SetFileTime(handle, NULL, &fatime, &fmtime) == 0) { errno = EINVAL; return (-1); } return (0); } int futimes(int fd, const struct __timeval *times) { return (__hutimes((HANDLE)_get_osfhandle(fd), times)); } int utimes(const char *name, const struct __timeval *times) { int ret; HANDLE handle; handle = la_CreateFile(name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { _dosmaperr(GetLastError()); return (-1); } ret = __hutimes(handle, times); CloseHandle(handle); return (ret); } int la_chdir(const char *path) { wchar_t *ws; int r; r = SetCurrentDirectoryA(path); if (r == 0) { if (GetLastError() != ERROR_FILE_NOT_FOUND) { _dosmaperr(GetLastError()); return (-1); } } else return (0); ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } r = SetCurrentDirectoryW(ws); free(ws); if (r == 0) { _dosmaperr(GetLastError()); return (-1); } return (0); } int la_chmod(const char *path, mode_t mode) { wchar_t *ws; int r; r = _chmod(path, mode); if (r >= 0 || errno != ENOENT) return (r); ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } r = _wchmod(ws, mode); free(ws); return (r); } /* * This fcntl is limited implemention. */ int la_fcntl(int fd, int cmd, int val) { HANDLE handle; handle = (HANDLE)_get_osfhandle(fd); if (GetFileType(handle) == FILE_TYPE_PIPE) { if (cmd == F_SETFL && val == 0) { DWORD mode = PIPE_WAIT; if (SetNamedPipeHandleState( handle, &mode, NULL, NULL) != 0) return (0); } } errno = EINVAL; return (-1); } __int64 la_lseek(int fd, __int64 offset, int whence) { LARGE_INTEGER distance; LARGE_INTEGER newpointer; HANDLE handle; if (fd < 0) { errno = EBADF; return (-1); } handle = (HANDLE)_get_osfhandle(fd); if (GetFileType(handle) != FILE_TYPE_DISK) { errno = EBADF; return (-1); } distance.QuadPart = offset; if (!SetFilePointerEx(handle, distance, &newpointer, whence)) { DWORD lasterr; lasterr = GetLastError(); if (lasterr == ERROR_BROKEN_PIPE) return (0); if (lasterr == ERROR_ACCESS_DENIED) errno = EBADF; else _dosmaperr(lasterr); return (-1); } return (newpointer.QuadPart); } int la_mkdir(const char *path, mode_t mode) { wchar_t *ws; int r; (void)mode;/* UNUSED */ r = CreateDirectoryA(path, NULL); if (r == 0) { DWORD lasterr = GetLastError(); if (lasterr != ERROR_FILENAME_EXCED_RANGE && lasterr != ERROR_PATH_NOT_FOUND) { _dosmaperr(GetLastError()); return (-1); } } else return (0); ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } r = CreateDirectoryW(ws, NULL); free(ws); if (r == 0) { _dosmaperr(GetLastError()); return (-1); } return (0); } /* Windows' mbstowcs is differrent error handling from other unix mbstowcs. * That one is using MultiByteToWideChar function with MB_PRECOMPOSED and * MB_ERR_INVALID_CHARS flags. * This implements for only to pass libarchive_test. */ size_t la_mbstowcs(wchar_t *wcstr, const char *mbstr, size_t nwchars) { return (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, mbstr, (int)strlen(mbstr), wcstr, (int)nwchars)); } int la_open(const char *path, int flags, ...) { va_list ap; wchar_t *ws; int r, pmode; DWORD attr; va_start(ap, flags); pmode = va_arg(ap, int); va_end(ap); ws = NULL; if ((flags & ~O_BINARY) == O_RDONLY) { /* * When we open a directory, _open function returns * "Permission denied" error. */ attr = GetFileAttributesA(path); if (attr == -1 && GetLastError() == ERROR_PATH_NOT_FOUND) { ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } attr = GetFileAttributesW(ws); } if (attr == -1) { _dosmaperr(GetLastError()); free(ws); return (-1); } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; if (ws != NULL) handle = CreateFileW(ws, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL); else handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL); free(ws); if (handle == INVALID_HANDLE_VALUE) { _dosmaperr(GetLastError()); return (-1); } r = _open_osfhandle((intptr_t)handle, _O_RDONLY); return (r); } } if (ws == NULL) { r = _open(path, flags, pmode); if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) { /* simular other POSIX system action to pass a test */ attr = GetFileAttributesA(path); if (attr == -1) _dosmaperr(GetLastError()); else if (attr & FILE_ATTRIBUTE_DIRECTORY) errno = EISDIR; else errno = EACCES; return (-1); } if (r >= 0 || errno != ENOENT) return (r); ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } } r = _wopen(ws, flags, pmode); if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) { /* simular other POSIX system action to pass a test */ attr = GetFileAttributesW(ws); if (attr == -1) _dosmaperr(GetLastError()); else if (attr & FILE_ATTRIBUTE_DIRECTORY) errno = EISDIR; else errno = EACCES; } free(ws); return (r); } ssize_t la_read(int fd, void *buf, size_t nbytes) { HANDLE handle; DWORD bytes_read, lasterr; int r; #ifdef _WIN64 if (nbytes > UINT32_MAX) nbytes = UINT32_MAX; #endif if (fd < 0) { errno = EBADF; return (-1); } handle = (HANDLE)_get_osfhandle(fd); if (GetFileType(handle) == FILE_TYPE_PIPE) { DWORD sta; if (GetNamedPipeHandleState( handle, &sta, NULL, NULL, NULL, NULL, 0) != 0 && (sta & PIPE_NOWAIT) == 0) { DWORD avail = -1; int cnt = 3; while (PeekNamedPipe( handle, NULL, 0, NULL, &avail, NULL) != 0 && avail == 0 && --cnt) Sleep(100); if (avail == 0) return (0); } } r = ReadFile(handle, buf, (uint32_t)nbytes, &bytes_read, NULL); if (r == 0) { lasterr = GetLastError(); if (lasterr == ERROR_NO_DATA) { errno = EAGAIN; return (-1); } if (lasterr == ERROR_BROKEN_PIPE) return (0); if (lasterr == ERROR_ACCESS_DENIED) errno = EBADF; else _dosmaperr(lasterr); return (-1); } return ((ssize_t)bytes_read); } /* Remove directory */ int la_rmdir(const char *path) { wchar_t *ws; int r; r = _rmdir(path); if (r >= 0 || errno != ENOENT) return (r); ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } r = _wrmdir(ws); free(ws); return (r); } /* Convert Windows FILETIME to UTC */ __inline static void fileTimeToUTC(const FILETIME *filetime, time_t *time, long *ns) { ULARGE_INTEGER utc; utc.HighPart = filetime->dwHighDateTime; utc.LowPart = filetime->dwLowDateTime; if (utc.QuadPart >= EPOC_TIME) { utc.QuadPart -= EPOC_TIME; *time = (time_t)(utc.QuadPart / 10000000); /* milli seconds base */ *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */ } else { *time = 0; *ns = 0; } } /* Stat by handle * Windows' stat() does not accept path which is added "\\?\" especially "?" * character. * It means we cannot access a long name path(which is longer than MAX_PATH). * So I've implemented simular Windows' stat() to access the long name path. * And I've added some feature. * 1. set st_ino by nFileIndexHigh and nFileIndexLow of * BY_HANDLE_FILE_INFORMATION. * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION. * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION. */ static int __hstat(HANDLE handle, struct ustat *st) { BY_HANDLE_FILE_INFORMATION info; ULARGE_INTEGER ino64; DWORD ftype; mode_t mode; time_t time; long ns; switch (ftype = GetFileType(handle)) { case FILE_TYPE_UNKNOWN: errno = EBADF; return (-1); case FILE_TYPE_CHAR: case FILE_TYPE_PIPE: if (ftype == FILE_TYPE_CHAR) { st->st_mode = S_IFCHR; st->st_size = 0; } else { DWORD avail; st->st_mode = S_IFIFO; if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL)) st->st_size = avail; else st->st_size = 0; } st->st_atime = 0; st->st_atime_nsec = 0; st->st_mtime = 0; st->st_mtime_nsec = 0; st->st_ctime = 0; st->st_ctime_nsec = 0; st->st_ino = 0; st->st_nlink = 1; st->st_uid = 0; st->st_gid = 0; st->st_rdev = 0; st->st_dev = 0; return (0); case FILE_TYPE_DISK: break; default: /* This ftype is undocumented type. */ _dosmaperr(GetLastError()); return (-1); } ZeroMemory(&info, sizeof(info)); if (!GetFileInformationByHandle (handle, &info)) { _dosmaperr(GetLastError()); return (-1); } mode = S_IRUSR | S_IRGRP | S_IROTH; if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) mode |= S_IWUSR | S_IWGRP | S_IWOTH; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else mode |= S_IFREG; st->st_mode = mode; fileTimeToUTC(&info.ftLastAccessTime, &time, &ns); st->st_atime = time; st->st_atime_nsec = ns; fileTimeToUTC(&info.ftLastWriteTime, &time, &ns); st->st_mtime = time; st->st_mtime_nsec = ns; fileTimeToUTC(&info.ftCreationTime, &time, &ns); st->st_ctime = time; st->st_ctime_nsec = ns; st->st_size = ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1)) + (int64_t)(info.nFileSizeLow); #ifdef SIMULATE_WIN_STAT st->st_ino = 0; st->st_nlink = 1; st->st_dev = 0; #else /* Getting FileIndex as i-node. We have to remove a sequence which * is high-16-bits of nFileIndexHigh. */ ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL; ino64.LowPart = info.nFileIndexLow; st->st_ino = ino64.QuadPart; st->st_nlink = info.nNumberOfLinks; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ++st->st_nlink;/* Add parent directory. */ st->st_dev = info.dwVolumeSerialNumber; #endif st->st_uid = 0; st->st_gid = 0; st->st_rdev = 0; return (0); } static void copy_stat(struct stat *st, struct ustat *us) { st->st_atime = us->st_atime; st->st_ctime = us->st_ctime; st->st_mtime = us->st_mtime; st->st_gid = us->st_gid; st->st_ino = getino(us); st->st_mode = us->st_mode; st->st_nlink = us->st_nlink; st->st_size = us->st_size; st->st_uid = us->st_uid; st->st_dev = us->st_dev; st->st_rdev = us->st_rdev; } int la_fstat(int fd, struct stat *st) { struct ustat u; int ret; if (fd < 0) { errno = EBADF; return (-1); } ret = __hstat((HANDLE)_get_osfhandle(fd), &u); if (ret >= 0) { copy_stat(st, &u); if (u.st_mode & (S_IFCHR | S_IFIFO)) { st->st_dev = fd; st->st_rdev = fd; } } return (ret); } int la_stat(const char *path, struct stat *st) { HANDLE handle; struct ustat u; int ret; handle = la_CreateFile(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL); if (handle == INVALID_HANDLE_VALUE) { _dosmaperr(GetLastError()); return (-1); } ret = __hstat(handle, &u); CloseHandle(handle); if (ret >= 0) { char *p; copy_stat(st, &u); p = strrchr(path, '.'); if (p != NULL && strlen(p) == 4) { char exttype[4]; ++ p; exttype[0] = toupper(*p++); exttype[1] = toupper(*p++); exttype[2] = toupper(*p++); exttype[3] = '\0'; if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") || !strcmp(exttype, "BAT") || !strcmp(exttype, "COM")) st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; } } return (ret); } int la_unlink(const char *path) { wchar_t *ws; int r; r = _unlink(path); if (r >= 0 || errno != ENOENT) return (r); ws = permissive_name(path); if (ws == NULL) { errno = EINVAL; return (-1); } r = _wunlink(ws); free(ws); return (r); } /* * This waitpid is limited implemention. */ pid_t la_waitpid(pid_t wpid, int *status, int option) { HANDLE child; DWORD cs, ret; (void)option;/* UNUSED */ child = OpenProcess(PROCESS_ALL_ACCESS, FALSE, wpid); if (child == NULL) { _dosmaperr(GetLastError()); return (-1); } ret = WaitForSingleObject(child, INFINITE); if (ret == WAIT_FAILED) { CloseHandle(child); _dosmaperr(GetLastError()); return (-1); } if (GetExitCodeProcess(child, &cs) == 0) { CloseHandle(child); _dosmaperr(GetLastError()); return (-1); } if (cs == STILL_ACTIVE) *status = 0x100; else *status = (int)(cs & 0xff); CloseHandle(child); return (wpid); } ssize_t la_write(int fd, const void *buf, size_t nbytes) { uint32_t bytes_written; #ifdef _WIN64 if (nbytes > UINT32_MAX) nbytes = UINT32_MAX; #endif if (fd < 0) { errno = EBADF; return (-1); } if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes, &bytes_written, NULL)) { DWORD lasterr; lasterr = GetLastError(); if (lasterr == ERROR_ACCESS_DENIED) errno = EBADF; else _dosmaperr(lasterr); return (-1); } return (bytes_written); } #if !defined(HAVE_OPENSSL_MD5_H) && !defined(HAVE_OPENSSL_SHA_H) /* * Message digest functions. */ static void Digest_Init(Digest_CTX *ctx, ALG_ID algId) { ctx->valid = 0; if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, PROV_RSA_FULL, 0)) { if (GetLastError() != NTE_BAD_KEYSET) return; if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) return; } if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) { CryptReleaseContext(ctx->cryptProv, 0); return; } ctx->valid = 1; } static void Digest_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) { if (!ctx->valid) return; CryptHashData(ctx->hash, buf, (DWORD)len, 0); } static void Digest_Final(unsigned char *buf, int bufsize, Digest_CTX *ctx) { DWORD siglen = bufsize; if (!ctx->valid) return; CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0); CryptDestroyHash(ctx->hash); CryptReleaseContext(ctx->cryptProv, 0); ctx->valid = 0; } #define DIGEST_INIT(name, algid) \ void name ## _Init(Digest_CTX *ctx)\ {\ Digest_Init(ctx, algid);\ } #define DIGEST_UPDATE(name) \ void name ## _Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)\ {\ Digest_Update(ctx, buf, len);\ } #define DIGEST_FINAL(name, size) \ void name ## _Final(unsigned char buf[size], Digest_CTX *ctx)\ {\ Digest_Final(buf, size, ctx);\ } DIGEST_INIT(MD5, CALG_MD5) DIGEST_UPDATE(MD5) DIGEST_FINAL(MD5, MD5_DIGEST_LENGTH) DIGEST_INIT(SHA1, CALG_SHA1) DIGEST_UPDATE(SHA1) DIGEST_FINAL(SHA1, SHA1_DIGEST_LENGTH) /* * SHA256 nor SHA384 nor SHA512 are not supported on Windows XP and Windows 2000. */ #ifdef CALG_SHA256 DIGEST_INIT(SHA256, CALG_SHA256) DIGEST_UPDATE(SHA256) DIGEST_FINAL(SHA256, SHA256_DIGEST_LENGTH) #endif #ifdef CALG_SHA384 DIGEST_INIT(SHA384, CALG_SHA384) DIGEST_UPDATE(SHA384) DIGEST_FINAL(SHA384, SHA384_DIGEST_LENGTH) #endif #ifdef CALG_SHA512 DIGEST_INIT(SHA512, CALG_SHA512) DIGEST_UPDATE(SHA512) DIGEST_FINAL(SHA512, SHA384_DIGEST_LENGTH) #endif #endif /* !HAVE_OPENSSL_MD5_H && !HAVE_OPENSSL_SHA_H */ #endif /* _WIN32 && !__CYGWIN__ */ tarsnap-1.0.41/libarchive/archive_windows.h000066400000000000000000000304701476705112600207360ustar00rootroot00000000000000/*- * Copyright (c) 2009 Michihiro NAKAJIMA * Copyright (c) 2003-2006 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef LIBARCHIVE_NONPOSIX_H_INCLUDED #define LIBARCHIVE_NONPOSIX_H_INCLUDED /* Start of configuration for native Win32 */ #include #define set_errno(val) ((errno)=val) #include #include //brings in NULL #include #include #include #include #include #include //#define EFTYPE 7 #if !defined(STDIN_FILENO) #define STDIN_FILENO 0 #endif #if !defined(STDOUT_FILENO) #define STDOUT_FILENO 1 #endif #if !defined(STDERR_FILENO) #define STDERR_FILENO 2 #endif #if defined(_MSC_VER) /* TODO: Fix the code, don't suppress the warnings. */ #pragma warning(disable:4244) /* 'conversion' conversion from 'type1' to 'type2', possible loss of data */ #pragma warning(disable:4146) /* unary minus operator applied to unsigned type, result still unsigned */ //#pragma warning(disable:4267) /* Conversion, possible loss of data */ #pragma warning(default: 4061) /* The enumerate has no associated handler in a switch statement */ #pragma warning(default: 4296) /* An unsigned variable was used in a comparison operation with zero */ #pragma warning(default: 4365) /* 'action':conversion from 'type_1' to 'type_2', signed/unsigned mismatch */ #endif #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8))|(0xffff00ff & (min))) /* Alias the Windows _function to the POSIX equivalent. */ #define access _access #define chdir la_chdir #define chmod la_chmod #define close _close #define fcntl la_fcntl #define fileno _fileno #define fstat la_fstat #define getcwd _getcwd #define lseek la_lseek #define lstat la_stat #define open la_open #define stat _stat #define mbstowcs la_mbstowcs #define mkdir(d,m) la_mkdir(d, m) #define mktemp _mktemp #define read la_read #define rmdir la_rmdir #define strdup _strdup #define tzset _tzset #define umask _umask #define unlink la_unlink #define waitpid la_waitpid #define write la_write #define O_RDONLY _O_RDONLY #define O_WRONLY _O_WRONLY #define O_TRUNC _O_TRUNC #define O_CREAT _O_CREAT #define O_EXCL _O_EXCL #ifndef _S_IFIFO #define _S_IFIFO 0010000 /* pipe */ #endif #ifndef _S_IFCHR #define _S_IFCHR 0020000 /* character special */ #endif #ifndef _S_IFDIR #define _S_IFDIR 0040000 /* directory */ #endif #ifndef _S_IFBLK #define _S_IFBLK 0060000 /* block special */ #endif #ifndef _S_IFLNK #define _S_IFLNK 0120000 /* symbolic link */ #endif #ifndef _S_IFSOCK #define _S_IFSOCK 0140000 /* socket */ #endif #ifndef _S_IFREG #define _S_IFREG 0100000 /* regular */ #endif #ifndef _S_IFMT #define _S_IFMT 0170000 /* file type mask */ #endif #define S_IFIFO _S_IFIFO //#define S_IFCHR _S_IFCHR //#define S_IFDIR _S_IFDIR #define S_IFBLK _S_IFBLK #define S_IFLNK _S_IFLNK #define S_IFSOCK _S_IFSOCK //#define S_IFREG _S_IFREG //#define S_IFMT _S_IFMT #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* block special */ #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */ #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* char special */ #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* directory */ #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* regular file */ #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */ #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */ #define _S_ISUID 0004000 /* set user id on execution */ #define _S_ISGID 0002000 /* set group id on execution */ #define _S_ISVTX 0001000 /* save swapped text even after use */ #define S_ISUID _S_ISUID #define S_ISGID _S_ISGID #define S_ISVTX _S_ISVTX #define _S_IRWXU (_S_IREAD | _S_IWRITE | _S_IEXEC) #define _S_IXUSR _S_IEXEC /* read permission, user */ #define _S_IWUSR _S_IWRITE /* write permission, user */ #define _S_IRUSR _S_IREAD /* execute/search permission, user */ #define _S_IRWXG (_S_IRWXU >> 3) #define _S_IXGRP (_S_IXUSR >> 3) /* read permission, group */ #define _S_IWGRP (_S_IWUSR >> 3) /* write permission, group */ #define _S_IRGRP (_S_IRUSR >> 3) /* execute/search permission, group */ #define _S_IRWXO (_S_IRWXG >> 3) #define _S_IXOTH (_S_IXGRP >> 3) /* read permission, other */ #define _S_IWOTH (_S_IWGRP >> 3) /* write permission, other */ #define _S_IROTH (_S_IRGRP >> 3) /* execute/search permission, other */ #define S_IRWXU _S_IRWXU #define S_IXUSR _S_IXUSR #define S_IWUSR _S_IWUSR #define S_IRUSR _S_IRUSR #define S_IRWXG _S_IRWXG #define S_IXGRP _S_IXGRP #define S_IWGRP _S_IWGRP #define S_IRGRP _S_IRGRP #define S_IRWXO _S_IRWXO #define S_IXOTH _S_IXOTH #define S_IWOTH _S_IWOTH #define S_IROTH _S_IROTH #define F_DUPFD 0 /* Duplicate file descriptor. */ #define F_GETFD 1 /* Get file descriptor flags. */ #define F_SETFD 2 /* Set file descriptor flags. */ #define F_GETFL 3 /* Get file status flags. */ #define F_SETFL 4 /* Set file status flags. */ #define F_GETOWN 5 /* Get owner (receiver of SIGIO). */ #define F_SETOWN 6 /* Set owner (receiver of SIGIO). */ #define F_GETLK 7 /* Get record locking info. */ #define F_SETLK 8 /* Set record locking info (non-blocking). */ #define F_SETLKW 9 /* Set record locking info (blocking). */ /* XXX missing */ #define F_GETLK64 7 /* Get record locking info. */ #define F_SETLK64 8 /* Set record locking info (non-blocking). */ #define F_SETLKW64 9 /* Set record locking info (blocking). */ /* File descriptor flags used with F_GETFD and F_SETFD. */ #define FD_CLOEXEC 1 /* Close on exec. */ //NOT SURE IF O_NONBLOCK is OK here but at least the 0x0004 flag is not used by anything else... #define O_NONBLOCK 0x0004 /* Non-blocking I/O. */ //#define O_NDELAY O_NONBLOCK /* Symbolic constants for the access() function */ #if !defined(F_OK) #define R_OK 4 /* Test for read permission */ #define W_OK 2 /* Test for write permission */ #define X_OK 1 /* Test for execute permission */ #define F_OK 0 /* Test for existence of file */ #endif #ifdef _LARGEFILE_SOURCE # define __USE_LARGEFILE 1 /* declare fseeko and ftello */ #endif #if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64 # define __USE_FILE_OFFSET64 1 /* replace 32-bit functions by 64-bit ones */ #endif #if __USE_LARGEFILE && __USE_FILE_OFFSET64 /* replace stat and seek by their large-file equivalents */ #undef stat #define stat _stati64 #undef lseek #define lseek _lseeki64 #define lseek64 _lseeki64 #define tell _telli64 #define tell64 _telli64 #ifdef __MINGW32__ # define fseek fseeko64 # define fseeko fseeko64 # define ftell ftello64 # define ftello ftello64 # define ftell64 ftello64 #endif /* __MINGW32__ */ #endif /* LARGE_FILES */ #ifdef USE_WINSOCK_TIMEVAL /* Winsock timeval has long size tv_sec. */ #define __timeval timeval #else struct _timeval64i32 { time_t tv_sec; long tv_usec; }; #define __timeval _timeval64i32 #endif typedef int pid_t; /* Message digest define */ #if !defined(HAVE_OPENSSL_MD5_H) && !defined(HAVE_OPENSSL_SHA_H) typedef struct { int valid; HCRYPTPROV cryptProv; HCRYPTHASH hash; } Digest_CTX; #endif #if !defined(HAVE_OPENSSL_MD5_H) && defined(CALG_MD5) #define MD5_DIGEST_LENGTH 16 #define HAVE_MD5 1 #define MD5_CTX Digest_CTX #endif #ifndef HAVE_OPENSSL_SHA_H #ifdef CALG_SHA1 #define SHA1_DIGEST_LENGTH 20 #define HAVE_SHA1 1 #define SHA1_CTX Digest_CTX #endif #ifdef CALG_SHA256 #define SHA256_DIGEST_LENGTH 32 #define HAVE_SHA256 1 #define SHA256_CTX Digest_CTX #endif #ifdef CALG_SHA384 #define SHA384_DIGEST_LENGTH 48 #define HAVE_SHA384 1 #define SHA384_CTX Digest_CTX #endif #ifdef CALG_SHA512 #define SHA512_DIGEST_LENGTH 64 #define HAVE_SHA512 1 #define SHA512_CTX Digest_CTX #endif #endif /* !HAVE_OPENSSL_SHA_H */ /* End of Win32 definitions. */ #ifdef __cplusplus extern "C" { #endif /* Implementation POSIX function */ extern int link (const char *from, const char *to); extern int symlink (const char *from, const char *to); extern int ftruncate(int fd, off_t length); extern int futimes(int fd, const struct __timeval *times); extern int utimes(const char *name, const struct __timeval *times); /* Replacement POSIX function */ extern int la_chdir(const char *path); extern int la_chmod(const char *path, mode_t mode); extern int la_fcntl(int fd, int cmd, int val); extern int la_fstat(int fd, struct stat *st); extern __int64 la_lseek(int fd, __int64 offset, int whence); extern int la_mkdir(const char *path, mode_t mode); extern size_t la_mbstowcs(wchar_t *wcstr, const char *mbstr, size_t nwchars); extern int la_open(const char *path, int flags, ...); extern ssize_t la_read(int fd, void *buf, size_t nbytes); extern int la_rmdir(const char *path); extern int la_stat(const char *path, struct stat *st); extern int la_unlink(const char *path); extern pid_t la_waitpid(pid_t wpid, int *status, int option); extern ssize_t la_write(int fd, const void *buf, size_t nbytes); #define _stat64i32(path, st) la_stat(path, st) #define _stat64(path, st) la_stat(path, st) /* for status returned by la_waitpid */ #define WIFSIGNALED(sts) 0 #define WTERMSIG(sts) 0 #define WIFEXITED(sts) ((sts & 0x100) == 0) #define WEXITSTATUS(sts) (sts & 0x0FF) /* Convert a Win32 API error code */ extern void _dosmaperr(unsigned long); /* Message digest function */ #if !defined(HAVE_OPENSSL_MD5_H) && !defined(HAVE_OPENSSL_SHA_H) #ifdef MD5_DIGEST_LENGTH extern void MD5_Init(Digest_CTX *ctx); extern void MD5_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len); extern void MD5_Final(unsigned char buf[MD5_DIGEST_LENGTH], Digest_CTX *ctx); #endif #ifdef SHA1_DIGEST_LENGTH extern void SHA1_Init(Digest_CTX *ctx); extern void SHA1_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len); extern void SHA1_Final(unsigned char buf[SHA1_DIGEST_LENGTH], Digest_CTX *ctx); #endif #ifdef SHA256_DIGEST_LENGTH extern void SHA256_Init(Digest_CTX *ctx); extern void SHA256_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len); extern void SHA256_Final(unsigned char buf[SHA256_DIGEST_LENGTH], Digest_CTX *ctx); #endif #ifdef SHA384_DIGEST_LENGTH extern void SHA384_Init(Digest_CTX *ctx); extern void SHA384_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len); extern void SHA384_Final(unsigned char buf[SHA384_DIGEST_LENGTH], Digest_CTX *ctx); #endif #ifdef SHA512_DIGEST_LENGTH extern void SHA512_Init(Digest_CTX *ctx); extern void SHA512_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len); extern void SHA512_Final(unsigned char buf[SHA512_DIGEST_LENGTH], Digest_CTX *ctx); #endif #endif #ifdef __cplusplus } #endif #endif /* !LIBARCHIVE_NONPOSIX_H_INCLUDED */ tarsnap-1.0.41/libarchive/archive_write.c000066400000000000000000000326411476705112600203730ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write.c,v 1.27 2008/03/14 23:09:02 kientzle Exp $"); /* * This file contains the "essential" portions of the write API, that * is, stuff that will essentially always be used by any client that * actually needs to write an archive. Optional pieces have been, as * far as possible, separated out into separate files to reduce * needlessly bloating statically-linked clients. */ #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" static struct archive_vtable *archive_write_vtable(void); static int _archive_write_close(struct archive *); static int _archive_write_finish(struct archive *); static int _archive_write_header(struct archive *, struct archive_entry *); static int _archive_write_finish_entry(struct archive *); static ssize_t _archive_write_data(struct archive *, const void *, size_t); static struct archive_vtable * archive_write_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_close; av.archive_finish = _archive_write_finish; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; av.archive_write_data = _archive_write_data; inited = 1; } return (&av); } /* * Allocate, initialize and return an archive object. */ struct archive * archive_write_new(void) { struct archive_write *a; unsigned char *nulls; a = (struct archive_write *)malloc(sizeof(*a)); if (a == NULL) return (NULL); memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_WRITE_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_write_vtable(); /* * The value 10240 here matches the traditional tar default, * but is otherwise arbitrary. * TODO: Set the default block size from the format selected. */ a->bytes_per_block = 10240; a->bytes_in_last_block = -1; /* Default */ /* Initialize a block of nulls for padding purposes. */ a->null_length = 1024; nulls = (unsigned char *)malloc(a->null_length); if (nulls == NULL) { free(a); return (NULL); } memset(nulls, 0, a->null_length); a->nulls = nulls; /* * Set default compression, but don't set a default format. * Were we to set a default format here, we would force every * client to link in support for that format, even if they didn't * ever use it. */ archive_write_set_compression_none(&a->archive); return (&a->archive); } /* * Set write options for the format. Returns 0 if successful. */ int archive_write_set_format_options(struct archive *_a, const char *s) { struct archive_write *a = (struct archive_write *)_a; char key[64], val[64]; int len, r, ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_options"); archive_clear_error(&a->archive); if (s == NULL || *s == '\0') return (ARCHIVE_OK); if (a->format_options == NULL) /* This format does not support option. */ return (ARCHIVE_OK); while ((len = __archive_parse_options(s, a->format_name, sizeof(key), key, sizeof(val), val)) > 0) { if (val[0] == '\0') r = a->format_options(a, key, NULL); else r = a->format_options(a, key, val); if (r == ARCHIVE_FATAL) return (r); if (r < ARCHIVE_OK) { /* This key was not handled. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported option ``%s''", key); ret = ARCHIVE_WARN; } s += len; } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed options string."); return (ARCHIVE_WARN); } return (ret); } /* * Set write options for the compressor. Returns 0 if successful. */ int archive_write_set_compressor_options(struct archive *_a, const char *s) { struct archive_write *a = (struct archive_write *)_a; char key[64], val[64]; int len, r; int ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compressor_options"); archive_clear_error(&a->archive); if (s == NULL || *s == '\0') return (ARCHIVE_OK); if (a->compressor.options == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported option ``%s''", s); /* This compressor does not support option. */ return (ARCHIVE_WARN); } while ((len = __archive_parse_options(s, a->archive.compression_name, sizeof(key), key, sizeof(val), val)) > 0) { if (val[0] == '\0') r = a->compressor.options(a, key, NULL); else r = a->compressor.options(a, key, val); if (r == ARCHIVE_FATAL) return (r); if (r < ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported option ``%s''", key); ret = ARCHIVE_WARN; } s += len; } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Illegal format options."); return (ARCHIVE_WARN); } return (ret); } /* * Set write options for the format and the compressor. Returns 0 if successful. */ int archive_write_set_options(struct archive *_a, const char *s) { int r1, r2; r1 = archive_write_set_format_options(_a, s); if (r1 < ARCHIVE_WARN) return (r1); r2 = archive_write_set_compressor_options(_a, s); if (r2 < ARCHIVE_WARN) return (r2); if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN) return (ARCHIVE_WARN); return (ARCHIVE_OK); } /* * Set the block size. Returns 0 if successful. */ int archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block"); a->bytes_per_block = bytes_per_block; return (ARCHIVE_OK); } /* * Get the current block size. -1 if it has never been set. */ int archive_write_get_bytes_per_block(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block"); return (a->bytes_per_block); } /* * Set the size for the last block. * Returns 0 if successful. */ int archive_write_set_bytes_in_last_block(struct archive *_a, int bytes) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block"); a->bytes_in_last_block = bytes; return (ARCHIVE_OK); } /* * Return the value set above. -1 indicates it has not been set. */ int archive_write_get_bytes_in_last_block(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block"); return (a->bytes_in_last_block); } /* * dev/ino of a file to be rejected. Used to prevent adding * an archive to itself recursively. */ int archive_write_set_skip_file(struct archive *_a, dev_t d, ino_t i) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_skip_file"); a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } /* * Open the archive using the current settings. */ int archive_write_open(struct archive *_a, void *client_data, archive_open_callback *opener, archive_write_callback *writer, archive_close_callback *closer) { struct archive_write *a = (struct archive_write *)_a; int ret; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open"); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_HEADER; a->client_data = client_data; a->client_writer = writer; a->client_opener = opener; a->client_closer = closer; ret = (a->compressor.init)(a); if (a->format_init && ret == ARCHIVE_OK) ret = (a->format_init)(a); return (ret); } /* * Close out the archive. * * Be careful: user might just call write_new and then write_finish. * Don't assume we actually wrote anything or performed any non-trivial * initialization. */ static int _archive_write_close(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_close"); /* Finish the last entry. */ if (a->archive.state & ARCHIVE_STATE_DATA) r = ((a->format_finish_entry)(a)); /* Finish off the archive. */ if (a->format_finish != NULL) { r1 = (a->format_finish)(a); if (r1 < r) r = r1; } /* Release format resources. */ if (a->format_destroy != NULL) { r1 = (a->format_destroy)(a); if (r1 < r) r = r1; } /* Finish the compression and close the stream. */ if (a->compressor.finish != NULL) { r1 = (a->compressor.finish)(a); if (r1 < r) r = r1; } /* Close out the client stream. */ if (a->client_closer != NULL) { r1 = (a->client_closer)(&a->archive, a->client_data); if (r1 < r) r = r1; } a->archive.state = ARCHIVE_STATE_CLOSED; return (r); } /* * Destroy the archive structure. */ static int _archive_write_finish(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_finish"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = archive_write_close(&a->archive); /* Release various dynamic buffers. */ free((void *)(uintptr_t)(const void *)a->nulls); archive_string_free(&a->archive.error_string); a->archive.magic = 0; free(a); return (r); } /* * Write the appropriate header. */ static int _archive_write_header(struct archive *_a, struct archive_entry *entry) { struct archive_write *a = (struct archive_write *)_a; int ret, r2; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header"); archive_clear_error(&a->archive); /* In particular, "retry" and "fatal" get returned immediately. */ ret = archive_write_finish_entry(&a->archive); if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN) return (ret); if (a->skip_file_dev != 0 && archive_entry_dev(entry) == a->skip_file_dev && a->skip_file_ino != 0 && archive_entry_ino(entry) == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Can't add archive to itself"); return (ARCHIVE_FAILED); } /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); if (r2 < ret) ret = r2; a->archive.state = ARCHIVE_STATE_DATA; return (ret); } static int _archive_write_finish_entry(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_DATA) ret = (a->format_finish_entry)(a); a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } /* * Note that the compressor is responsible for blocking. */ static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t s) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); archive_clear_error(&a->archive); return ((a->format_write_data)(a, buff, s)); } int archive_write_skip(struct archive *_a, off_t s) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_skip"); archive_clear_error(&a->archive); if (a->format_skip_data == NULL) { archive_set_error(&a->archive, ENOSYS, "No format skip handler registered"); return (ARCHIVE_FATAL); } /* Adjust raw position. */ a->archive.raw_position += s; /* Adjust uncompressed position. Used by Tarsnap. */ _a->file_position += s; return ((a->format_skip_data)(a, s)); } tarsnap-1.0.41/libarchive/archive_write_disk.c000066400000000000000000002156451476705112600214140ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.42 2008/12/06 05:55:46 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_SYS_XATTR_H #include #endif #ifdef HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_UTIME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PWD_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_UTIME_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct fixup_entry { struct fixup_entry *next; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long fflags_set; int fixup; /* bitmask of what needs fixing */ char *name; }; /* * We use a bitmask to track which operations remain to be done for * this file. In particular, this helps us avoid unnecessary * operations when it's possible to take care of one step as a * side-effect of another. For example, mkdir() can specify the mode * for the newly-created object but symlink() cannot. This means we * can skip chmod() if mkdir() succeeded, but we must explicitly * chmod() if we're trying to create a directory that already exists * (mkdir() failed) or if we're restoring a symlink. Similarly, we * need to verify UID/GID before trying to restore SUID/SGID bits; * that verification can occur explicitly through a stat() call or * implicitly because of a successful chown() call. */ #define TODO_MODE_FORCE 0x40000000 #define TODO_MODE_BASE 0x20000000 #define TODO_SUID 0x10000000 #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 #define TODO_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 struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; uid_t user_uid; dev_t skip_file_dev; ino_t skip_file_ino; time_t start_time; gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; uid_t (*lookup_uid)(void *private, const char *gname, gid_t gid); void (*cleanup_uid)(void *private); void *lookup_uid_data; /* * Full path of last file to satisfy symlink checks. */ struct archive_string path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ struct stat st; struct stat *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ char *name; /* Name of entry, possibly edited. */ struct archive_string _name_data; /* backing store for 'name' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ int deferred; /* Options requested by the client. */ int flags; /* Handle for the file we're restoring. */ int fd; /* Current offset for writing data to the file. */ off_t offset; /* Last offset actually written to disk. */ off_t fd_offset; /* Maximum size of file, -1 if unknown. */ off_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. */ uid_t uid; gid_t gid; }; /* * Default mode for dirs created automatically (will be modified by umask). * Note that POSIX specifies 0777 for implicitly-created dirs, "modified * by the process' file creation mask." */ #define DEFAULT_DIR_MODE 0777 /* * Dir modes are restored in two steps: During the extraction, the permissions * in the archive are modified to match the following limits. During * the post-extract fixup pass, the permissions from the archive are * applied. */ #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); #ifdef HAVE_FCHDIR static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); #ifdef HAVE_POSIX_ACL static int set_acl(struct archive_write_disk *, int fd, struct archive_entry *, acl_type_t, int archive_entry_acl_type, const char *tn); #endif static int set_acls(struct archive_write_disk *); static int set_xattrs(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_fflags_platform(struct archive_write_disk *, int fd, const char *name, mode_t mode, unsigned long fflags_set, unsigned long fflags_clear); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_time(int, int, const char *, time_t, long, time_t, long); static int set_times(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static gid_t trivial_lookup_gid(void *, const char *, gid_t); static uid_t trivial_lookup_uid(void *, const char *, uid_t); 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_close(struct archive *); static int _archive_write_finish(struct archive *); static int _archive_write_header(struct archive *, struct archive_entry *); static int _archive_write_finish_entry(struct archive *); static ssize_t _archive_write_data(struct archive *, const void *, size_t); static ssize_t _archive_write_data_block(struct archive *, const void *, size_t, off_t); static int _archive_write_disk_lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ return (ARCHIVE_OK); } #ifdef HAVE_FSTAT if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } #endif /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ if (lstat(a->name, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static struct archive_vtable * archive_write_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_close; av.archive_finish = _archive_write_finish; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; av.archive_write_data = _archive_write_data; av.archive_write_data_block = _archive_write_data_block; inited = 1; } return (&av); } 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_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_finish_entry(&a->archive); if (r == ARCHIVE_FATAL) return (r); } /* Set up for this particular entry. */ a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->entry = archive_entry_clone(entry); a->fd = -1; a->fd_offset = 0; a->offset = 0; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); if (archive_entry_size_is_set(a->entry)) a->filesize = archive_entry_size(a->entry); else a->filesize = -1; archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry)); a->name = a->_name_data.s; archive_clear_error(&a->archive); /* * Clean up the requested path. This is necessary for correct * dir restores; the dir restore logic otherwise gets messed * up by nonsense like "dir/.". */ ret = cleanup_pathname(a); if (ret != ARCHIVE_OK) return (ret); /* * Set the umask to zero 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. This will be reset before we return. Note that we * don't need to do this in _finish_entry, as the chmod(), etc, * system calls don't obey umask. */ a->user_umask = umask(0); /* From here on, early exit requires "goto done" to clean up. */ /* 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 !defined(_WIN32) || defined(__CYGWIN__) 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) 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) goto done; } #ifdef HAVE_FCHDIR /* If path exceeds PATH_MAX, shorten the path. */ edit_deep_directories(a); #endif ret = restore_entry(a); /* * On the GNU tar mailing list, some people working with new * Linux filesystems observed that system xattrs used as * layout hints need to be restored before the file contents * are written, so this can't be done at file close. */ if (a->todo & TODO_XATTR) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { archive_set_error(&a->archive, errno, "chdir() failure"); ret = ARCHIVE_FATAL; } close(a->restore_pwd); a->restore_pwd = -1; } #endif /* * Fixup uses the unedited pathname from archive_entry_pathname(), * because it is relative to the base dir and the edited path * might be relative to some intermediate dir as a result of the * deep restore logic. */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname(entry)); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } if ((a->deferred & TODO_TIMES) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname(entry)); 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_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } /* We've created the object and are ready to pour data into it. */ if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. * In particular, dirs, links, etc, don't get written to. */ if (a->fd < 0) { archive_entry_set_size(entry, 0); a->filesize = 0; } done: /* Restore the user's umask before returning. */ umask(a->user_umask); return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, dev_t d, ino_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_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } static ssize_t write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t block_size = 0, bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } if (a->flags & ARCHIVE_EXTRACT_SPARSE) { #if HAVE_STRUCT_STAT_ST_BLKSIZE int r; if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; #else /* XXX TODO XXX Is there a more appropriate choice here ? */ /* This needn't match the filesystem allocation size. */ block_size = 16*1024; #endif } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (off_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; off_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { if (*p != '\0') break; } a->offset += p - buff; size -= p - buff; buff = p; if (size == 0) break; /* Calculate next block boundary after offset. */ block_end = (a->offset / block_size + 1) * block_size; /* If the adjusted write would cross block boundary, * truncate it to the block boundary. */ bytes_to_write = size; if (a->offset + bytes_to_write > block_end) bytes_to_write = block_end - a->offset; } /* Seek if necessary to the specified offset. */ if (a->offset != a->fd_offset) { if (lseek(a->fd, a->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } a->fd_offset = a->offset; a->archive.file_position = a->offset; a->archive.raw_position = a->offset; } bytes_written = write(a->fd, buff, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } buff += bytes_written; size -= bytes_written; a->offset += bytes_written; a->archive.file_position += bytes_written; a->archive.raw_position += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } static ssize_t _archive_write_data_block(struct archive *_a, const void *buff, size_t size, off_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_disk_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); } return (ARCHIVE_OK); } static ssize_t _archive_write_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_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_HEADER) return (ARCHIVE_OK); archive_clear_error(&a->archive); /* Pad or truncate file to the right size. */ if (a->fd < 0) { /* There's no file. */ } else if (a->filesize < 0) { /* File size is unknown, so we can't set the size. */ } else if (a->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ } else { #if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } #endif /* * Explicitly stat the file as some platforms might not * implement the XSI option to extend files via ftruncate. */ a->pst = NULL; if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (ret); if (a->st.st_size != a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); return (ARCHIVE_FATAL); } a->pst = NULL; } } /* Restore metadata. */ /* * 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 = a->lookup_uid(a->lookup_uid_data, 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 = a->lookup_gid(a->lookup_gid_data, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * If restoring ownership, do it before trying 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); if (a->todo & TODO_MODE) { int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } if (a->todo & TODO_ACLS) { int r2 = set_acls(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 has to be restored after all other metadata; * otherwise atime will get changed. */ if (a->todo & TODO_TIMES) { int r2 = set_times(a); if (r2 < ret) ret = r2; } /* If there's an fd, we can close it now. */ if (a->fd >= 0) { close(a->fd); a->fd = -1; } /* If there's an entry, we can release it now. */ if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, gid_t (*lookup_gid)(void *private, const char *gname, gid_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"); 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, uid_t (*lookup_uid)(void *private, const char *uname, uid_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"); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } /* * 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->lookup_uid = trivial_lookup_uid; a->lookup_gid = trivial_lookup_gid; a->start_time = time(NULL); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ if (archive_string_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } return (&a->archive); } /* * If pathname is longer than PATH_MAX, chdir to a suitable * intermediate dir and edit the path down to a shorter suffix. Note * that this routine never returns an error; if the chdir() attempt * fails for any reason, we just go ahead with the long pathname. The * object creation is likely to fail, but any error will get handled * at that time. */ #ifdef HAVE_FCHDIR static void edit_deep_directories(struct archive_write_disk *a) { int ret; char *tail = a->name; a->restore_pwd = -1; /* If path is short, avoid the open() below. */ if (strlen(tail) <= PATH_MAX) return; /* Try to record our starting dir. */ a->restore_pwd = open(".", O_RDONLY | O_BINARY); if (a->restore_pwd < 0) return; /* As long as the path is too long... */ while (strlen(tail) > PATH_MAX) { /* Locate a dir prefix shorter than PATH_MAX. */ tail += PATH_MAX - 8; while (tail > a->name && *tail != '/') tail--; /* Exit if we find a too-long path component. */ if (tail <= a->name) return; /* Create the intermediate dir and chdir to it. */ *tail = '\0'; /* Terminate dir portion */ ret = create_dir(a, a->name); if (ret == ARCHIVE_OK && chdir(a->name) != 0) ret = ARCHIVE_FAILED; *tail = '/'; /* Restore the / we removed. */ if (ret != ARCHIVE_OK) return; tail++; /* The chdir() succeeded; we've now shortened the path. */ a->name = tail; } return; } #endif /* * The main restore function. */ static int restore_entry(struct archive_write_disk *a) { int ret = ARCHIVE_OK, en; if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { /* * TODO: Fix this. Apparently, there are platforms * that still allow root to hose the entire filesystem * by unlinking a dir. The S_ISDIR() test above * prevents us from using unlink() here if the new * object is a dir, but that doesn't mean the old * object isn't a dir. */ if (unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); return(ARCHIVE_FAILED); } } /* Try creating it first; if this fails, we'll try to recover. */ en = create_filesystem_object(a); if ((en == ENOTDIR || en == ENOENT) && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) { /* If the parent dir doesn't exist, try creating it. */ create_parent_dir(a, a->name); /* Now try to create the object again. */ en = create_filesystem_object(a); } if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, "Hard-link target '%s' does not exist.", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ archive_set_error(&a->archive, en, "Already exists"); return (ARCHIVE_FAILED); } /* * Some platforms return EISDIR if you call * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some * return EEXIST. POSIX is ambiguous, requiring EISDIR * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) * on an existing item. */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; /* * The SECURE_SYMLINKS logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) r = stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) r = lstat(a->name, &a->st); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); } /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) && !S_ISDIR(a->st.st_mode)) { if (!older(&(a->st), a->entry)) { archive_entry_unset_size(a->entry); archive_set_error(&a->archive, 0, "File on disk is not older; skipping."); return (ARCHIVE_OK); } } /* If it's our archive, we're done. */ if (a->skip_file_dev > 0 && a->skip_file_ino > 0 && a->st.st_dev == a->skip_file_dev && a->st.st_ino == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } if (!S_ISDIR(a->st.st_mode)) { /* A non-dir is in the way, unlink it. */ if (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 (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 != a->st.st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ en = 0; /* Forget the EEXIST. */ } } if (en) { /* Everything failed; give up here. */ archive_set_error(&a->archive, en, "Can't create '%s'", a->name); return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ return (ret); } /* * Returns 0 if creation succeeds, or else returns errno value from * the failed system call. Note: This function should only ever perform * a single system call. */ int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const char *linkname; 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(a->entry); if (linkname != NULL) { r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. * * If the hardlink was successfully created and * the archive doesn't have carry data for it, * consider it to be non-authoritive 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->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY); if (a->fd < 0) r = errno; } return (r); } linkname = archive_entry_symlink(a->entry); if (linkname != NULL) return symlink(linkname, a->name) ? errno : 0; /* * 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; switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: a->fd = open(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode); r = (a->fd < 0); break; case AE_IFCHR: #ifdef HAVE_MKNOD /* Note: we use AE_IFCHR for the case label, and * S_IFCHR for the mknod() call. This is correct. */ r = mknod(a->name, mode | S_IFCHR, archive_entry_rdev(a->entry)); #else /* TODO: Find a better way to warn about our inability * to restore a char device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ break; case AE_IFBLK: #ifdef HAVE_MKNOD r = mknod(a->name, mode | S_IFBLK, archive_entry_rdev(a->entry)); #else /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ break; case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; r = mkdir(a->name, mode); if (r == 0) { /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; /* Never use an immediate chmod(). */ /* We can't avoid the chmod() entirely if EXTRACT_PERM * because of SysV SGID inheritance. */ if ((mode != final_mode) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; } break; case AE_IFIFO: #ifdef HAVE_MKFIFO r = mkfifo(a->name, mode); #else /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); #endif /* HAVE_MKFIFO */ break; } /* 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. * * 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_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_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) { #ifdef HAVE_UTIMES /* {f,l,}utimes() are preferred, when available. */ #if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif times[0].tv_sec = p->atime; times[0].tv_usec = p->atime_nanos / 1000; #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME /* if it's valid and not mtime, push the birthtime first */ if (((times[1].tv_sec = p->birthtime) < p->mtime) && (p->birthtime > 0)) { times[1].tv_usec = p->birthtime_nanos / 1000; utimes(p->name, times); } #endif times[1].tv_sec = p->mtime; times[1].tv_usec = p->mtime_nanos / 1000; #ifdef HAVE_LUTIMES lutimes(p->name, times); #else utimes(p->name, times); #endif #else /* utime() is more portable, but less precise. */ struct utimbuf times; times.modtime = p->mtime; times.actime = p->atime; utime(p->name, ×); #endif } if (p->fixup & TODO_MODE_BASE) chmod(p->name, p->mode); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, -1, p->name, p->mode, p->fflags_set, 0); next = p->next; free(p->name); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_finish(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret; ret = _archive_write_close(&a->archive); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); if (a->entry) archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); free(a); return (ret); } /* * Simple O(n log n) merge sort to order the fixup list. In * particular, we want to restore dir timestamps depth-first. */ static struct fixup_entry * sort_dir_list(struct fixup_entry *p) { struct fixup_entry *a, *b, *t; if (p == NULL) return (NULL); /* A one-item list is already sorted. */ if (p->next == NULL) return (p); /* Step 1: split the list. */ t = p; a = p->next->next; while (a != NULL) { /* Step a twice, t once. */ a = a->next; if (a != NULL) a = a->next; t = t->next; } /* Now, t is at the mid-point, so break the list here. */ b = t->next; t->next = NULL; a = p; /* Step 2: Recursively sort the two sub-lists. */ a = sort_dir_list(a); b = sort_dir_list(b); /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ if (strcmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { t = p = b; b = b->next; } /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { if (strcmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { t->next = b; b = b->next; } t = t->next; } /* Only one list is non-empty, so just splice it on. */ if (a != NULL) t->next = a; if (b != NULL) t->next = b; return (p); } /* * Returns a new, initialized fixup entry. * * TODO: Reduce the memory requirements for this list by using a tree * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive_write_disk *a, const char *pathname) { struct fixup_entry *fe; fe = (struct fixup_entry *)malloc(sizeof(struct fixup_entry)); if (fe == NULL) return (NULL); fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; fe->name = strdup(pathname); return (fe); } /* * Returns a fixup structure for the current entry. */ static struct fixup_entry * current_fixup(struct archive_write_disk *a, const char *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); return (a->current_fixup); } /* 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. */ static int check_symlinks(struct archive_write_disk *a) { char *pn; char c; int r; struct stat st; /* * 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; if (archive_strlen(&(a->path_safe)) > 0) { char *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 = lstat(a->name, &st); if (r != 0) { /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) break; } else if (S_ISLNK(st.st_mode)) { if (c == '\0') { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ if (unlink(a->name)) { archive_set_error(&a->archive, errno, "Could not remove symlink %s", 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 %s", 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 (unlink(a->name) != 0) { archive_set_error(&a->archive, 0, "Cannot remove intervening symlink %s", a->name); pn[0] = c; return (ARCHIVE_FAILED); } a->pst = NULL; } else { archive_set_error(&a->archive, 0, "Cannot extract through symlink %s", a->name); pn[0] = c; return (ARCHIVE_FAILED); } } } pn[0] = c; /* We've checked and/or cleaned the whole path, so remember it. */ archive_strcpy(&a->path_safe, a->name); return (ARCHIVE_OK); } #if defined(_WIN32) || defined(__CYGWIN__) /* * 1. Convert a path separator from '\' to '/' . * We shouldn't check multi-byte character directly because some * character-set have been using the '\' character for a part of * its multibyte character code. * 2. Replace unusable characters in Windows with underscore('_'). * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static void cleanup_pathname_win(struct archive_write_disk *a) { wchar_t wc; char *p; size_t alen, l; alen = 0; l = 0; for (p = a->name; *p != '\0'; p++) { ++alen; if (*p == '\\') l = 1; /* Rewrite the path name if its next character is unusable. */ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; } if (alen == 0 || l == 0) return; /* * Convert path separator. */ p = a->name; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == -1) { while (*p != '\0') { if (*p == '\\') *p = '/'; ++p; } break; } if (l == 1 && wc == L'\\') *p = '/'; p += l; alen -= l; } } #endif /* * Canonicalize the pathname. In particular, this strips duplicate * '/' characters, '.' elements, and trailing '/'. It also raises an * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is * set) any '..' in the path. */ static int cleanup_pathname(struct archive_write_disk *a) { char *dest, *src; char separator = '\0'; dest = src = a->name; if (*src == '\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid empty pathname"); return (ARCHIVE_FAILED); } #if defined(_WIN32) || defined(__CYGWIN__) cleanup_pathname_win(a); #endif /* Skip leading '/'. */ if (*src == '/') separator = *src++; /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '/' */ if (src[0] == '\0') { break; } else if (src[0] == '/') { /* Found '//', ignore second one. */ src++; continue; } else if (src[0] == '.') { if (src[1] == '\0') { /* Ignore trailing '.' */ break; } else if (src[1] == '/') { /* Skip './'. */ src += 2; continue; } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ if (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++ = '/'; while (*src != '\0' && *src != '/') { *dest++ = *src++; } if (*src == '\0') break; /* Skip '/' separator. */ separator = *src++; } /* * We've just copied zero or more path elements, not including the * final '/'. */ if (dest == a->name) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. */ if (separator) *dest++ = '/'; else *dest++ = '.'; } /* Terminate the result. */ *dest = '\0'; return (ARCHIVE_OK); } /* * Create the parent directory of the specified path, assuming path * is already in mutable storage. */ static int create_parent_dir(struct archive_write_disk *a, char *path) { char *slash; int r; /* Remove tail element to obtain parent name. */ slash = strrchr(path, '/'); if (slash == NULL) return (ARCHIVE_OK); *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } /* * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int create_dir(struct archive_write_disk *a, char *path) { struct stat st; struct fixup_entry *le; char *slash, *base; mode_t mode_final, mode; int r; /* Check for special names and just skip them. */ slash = strrchr(path, '/'); if (slash == NULL) base = path; else base = slash + 1; if (base[0] == '\0' || (base[0] == '.' && base[1] == '\0') || (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } return (ARCHIVE_OK); } /* * Yes, this should be stat() and not lstat(). Using lstat() * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ if (stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); return (ARCHIVE_FAILED); } if (unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed"); return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; if (r != ARCHIVE_OK) return (r); } /* * Mode we want for the final restored directory. Per POSIX, * implicitly-created dirs must be created obeying the umask. * There's no mention whether this is different for privileged * restores (which the rest of this code handles by pretending * umask=0). I've chosen here to always obey the user's umask for * implicit dirs, even if _EXTRACT_PERM was specified. */ mode_final = DEFAULT_DIR_MODE & ~a->user_umask; /* Mode we want on disk during the restore process. */ mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; if (mkdir(path, mode) == 0) { if (mode != mode_final) { le = new_fixup(a, path); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); } /* * Without the following check, a/b/../b/c/d fails at the * second visit to 'b', so 'd' can't be created. Note that we * don't add it to the fixup list here, as it's already been * added. */ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); return (ARCHIVE_FAILED); } /* * Note: Although we can skip setting the user id if the desired user * id matches the current user, we cannot skip setting the group, as * many systems set the gid based on the containing directory. So * we have to perform a chown syscall if we want to set the SGID * bit. (The alternative is to stat() and then possibly chown(); it's * more efficient to skip the stat() and just always chown().) Note * that a successful chown() here clears the TODO_SGID_CHECK bit, which * allows set_mode to skip the stat() check for the GID. */ static int set_ownership(struct archive_write_disk *a) { #ifndef __CYGWIN__ /* unfortunately, on win32 there is no 'root' user with uid 0, so we just have to try the chown and see if it works */ /* If we know we can't change it, don't bother trying. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%d", a->uid); return (ARCHIVE_WARN); } #endif #ifdef HAVE_FCHOWN /* If we have an fd, we can avoid a race. */ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif /* We prefer lchown() but will use chown() if that's all we have. */ /* Of course, if we have neither, this will always fail. */ #ifdef HAVE_LCHOWN if (lchown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #elif HAVE_CHOWN if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif archive_set_error(&a->archive, errno, "Can't set user=%d/group=%d for %s", a->uid, a->gid, a->name); return (ARCHIVE_WARN); } #ifdef HAVE_UTIMES /* * The utimes()-family functions provide high resolution and * a way to set time on an fd or a symlink. We prefer them * when they're available. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { #if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif times[0].tv_sec = atime; times[0].tv_usec = atime_nsec / 1000; times[1].tv_sec = mtime; times[1].tv_usec = mtime_nsec / 1000; #ifdef HAVE_FUTIMES if (fd >= 0) return (futimes(fd, times)); #else (void)fd; /* UNUSED */ #endif #ifdef HAVE_LUTIMES (void)mode; /* UNUSED */ return (lutimes(name, times)); #else if (S_ISLNK(mode)) return (0); return (utimes(name, times)); #endif } #elif defined(HAVE_UTIME) /* * utime() is an older, more standard interface that we'll use * if utimes() isn't available. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { struct utimbuf times; (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ times.actime = atime; times.modtime = mtime; if (S_ISLNK(mode)) return (ARCHIVE_OK); return (utime(name, ×)); } #else static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { return (ARCHIVE_WARN); } #endif static int set_times(struct archive_write_disk *a) { time_t atime = a->start_time, mtime = a->start_time; long atime_nsec = 0, mtime_nsec = 0; /* If no time was provided, we're done. */ if (!archive_entry_atime_is_set(a->entry) #if HAVE_STRUCT_STAT_ST_BIRTHTIME && !archive_entry_birthtime_is_set(a->entry) #endif && !archive_entry_mtime_is_set(a->entry)) return (ARCHIVE_OK); /* If no atime was specified, use start time instead. */ /* In theory, it would be marginally more correct to use * time(NULL) here, but that would cost us an extra syscall * for little gain. */ if (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } /* * If you have struct stat.st_birthtime, we assume BSD birthtime * semantics, in which {f,l,}utimes() updates birthtime to earliest * mtime. So we set the time twice, first using the birthtime, * then using the mtime. */ #if HAVE_STRUCT_STAT_ST_BIRTHTIME /* If birthtime is set, flush that through to disk first. */ if (archive_entry_birthtime_is_set(a->entry)) if (set_time(a->fd, a->mode, a->name, atime, atime_nsec, archive_entry_birthtime(a->entry), archive_entry_birthtime_nsec(a->entry))) { archive_set_error(&a->archive, errno, "Can't update time for %s", a->name); return (ARCHIVE_WARN); } #endif if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (set_time(a->fd, a->mode, a->name, atime, atime_nsec, mtime, mtime_nsec)) { archive_set_error(&a->archive, errno, "Can't update time for %s", a->name); return (ARCHIVE_WARN); } /* * Note: POSIX does not provide a portable way to restore ctime. * (Apart from resetting the system clock, which is distasteful.) * So, any restoration of ctime will necessarily be OS-specific. */ return (ARCHIVE_OK); } 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 = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; #if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { /* * This is only an error if you * requested owner restore. If you * didn't, we'll try to restore * sgid/suid, but won't consider it a * problem if we can't. */ archive_set_error(&a->archive, -1, "Can't restore SGID bit"); r = ARCHIVE_WARN; } #endif } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; #if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } #endif } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; } else if (a->todo & TODO_SUID_CHECK) { /* * If we don't know the UID is right, we can just check * the user, since all systems set the file UID from * the process UID. */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; #if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } #endif } a->todo &= ~TODO_SUID_CHECK; } if (S_ISLNK(a->mode)) { #ifdef HAVE_LCHMOD /* * If this is a symlink, use lchmod(). If the * platform doesn't support lchmod(), just skip it. A * platform that doesn't provide a way to set * permissions on symlinks probably ignores * permissions on symlinks, so a failure here has no * impact. */ if (lchmod(a->name, mode) != 0) { switch (errno) { case ENOTSUP: case ENOSYS: #if ENOTSUP != EOPNOTSUPP case EOPNOTSUPP: #endif /* * if lchmod is defined but the platform * doesn't support it, silently ignore * error */ break; default: archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } #endif } else if (!S_ISDIR(a->mode)) { /* * If it's not a symlink and not a dir, then use * fchmod() or chmod(), depending on whether we have * an fd. Dirs get their perms set during the * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD if (a->fd >= 0) { if (fchmod(a->fd, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } else #endif /* If this platform lacks fchmod(), then * we'll just use chmod(). */ if (chmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } return (r); } static int set_fflags(struct archive_write_disk *a) { struct fixup_entry *le; unsigned long set, clear; int r; int critical_flags; mode_t mode = archive_entry_mode(a->entry); /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, * SF_IMMUTABLE prevents hardlinks from being created, so * should not be set until after any hardlinks are created. To * preserve some semblance of portability, this uses #ifdef * extensively. Ugly, but it works. * * Yes, Virginia, this does create a security race. It's mitigated * somewhat by the practice of creating dirs 0700 until the extract * is done, but it would be nice if we could do more than that. * People restoring critical file systems should be wary of * other programs that might try to muck with files as they're * being restored. */ /* Hopefully, the compiler will optimize this mess into a constant. */ critical_flags = 0; #ifdef SF_IMMUTABLE critical_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE critical_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND critical_flags |= SF_APPEND; #endif #ifdef UF_APPEND critical_flags |= UF_APPEND; #endif #ifdef EXT2_APPEND_FL critical_flags |= EXT2_APPEND_FL; #endif #ifdef EXT2_IMMUTABLE_FL critical_flags |= EXT2_IMMUTABLE_FL; #endif if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); /* * The first test encourages the compiler to eliminate * all of this if it's not necessary. */ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, a->name); le->fixup |= TODO_FFLAGS; le->fflags_set = set; /* Store the mode if it's not already there. */ if ((le->fixup & TODO_MODE) == 0) le->mode = mode; } else { r = set_fflags_platform(a, a->fd, a->name, mode, set, clear); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS) /* * BSD reads flags using stat() and sets them with one of {f,l,}chflags() */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; (void)mode; /* UNUSED */ if (set == 0 && clear == 0) return (ARCHIVE_OK); /* * XXX Is the stat here really necessary? Or can I just use * the 'set' flags directly? In particular, I'm not sure * about the correct approach if we're overwriting an existing * file that already has flags on it. XXX */ if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags &= ~clear; a->st.st_flags |= set; #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif /* * If we can't use the fd to set the flags, we'll use the * pathname to set flags. We prefer lchflags() but will use * chflags() if we must. */ #ifdef HAVE_LCHFLAGS if (lchflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #elif defined(HAVE_CHFLAGS) if (S_ISLNK(a->st.st_mode)) { archive_set_error(&a->archive, errno, "Can't set file flags on symlink."); return (ARCHIVE_WARN); } if (chflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif archive_set_error(&a->archive, errno, "Failed to set file flags"); return (ARCHIVE_WARN); } #elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) /* * Linux uses ioctl() to read and write file flags. */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int ret; int myfd = fd; unsigned long newflags, oldflags; unsigned long sf_mask = 0; if (set == 0 && clear == 0) return (ARCHIVE_OK); /* Only regular files and dirs can have flags. */ if (!S_ISREG(mode) && !S_ISDIR(mode)) return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ if (myfd < 0) myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY); if (myfd < 0) return (ARCHIVE_OK); /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but * there seem to be some Linux systems that lack these * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ #ifdef EXT2_IMMUTABLE_FL sf_mask |= EXT2_IMMUTABLE_FL; #endif #ifdef EXT2_APPEND_FL sf_mask |= EXT2_APPEND_FL; #endif /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX */ ret = ARCHIVE_OK; /* Read the current file flags. */ if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) < 0) goto fail; /* Try setting the flags as given. */ newflags = (oldflags & ~clear) | set; if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) goto cleanup; if (errno != EPERM) goto fail; /* If we couldn't set all the flags, try again with a subset. */ newflags &= ~sf_mask; oldflags &= sf_mask; newflags |= oldflags; if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) goto cleanup; /* We couldn't set the flags, so report the failure. */ fail: archive_set_error(&a->archive, errno, "Failed to set file flags"); ret = ARCHIVE_WARN; cleanup: if (fd < 0) close(myfd); return (ret); } #else /* * Of course, some systems have neither BSD chflags() nor Linux' flags * support through ioctl(). */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)mode; /* UNUSED */ (void)set; /* UNUSED */ (void)clear; /* UNUSED */ return (ARCHIVE_OK); } #endif /* __linux */ #ifndef HAVE_POSIX_ACL /* Default empty function body to satisfy mainline code. */ static int set_acls(struct archive_write_disk *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } #else /* * XXX TODO: What about ACL types other than ACCESS and DEFAULT? */ static int set_acls(struct archive_write_disk *a) { int ret; ret = set_acl(a, a->fd, a->entry, ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); if (ret != ARCHIVE_OK) return (ret); ret = set_acl(a, a->fd, a->entry, ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); return (ret); } static int set_acl(struct archive_write_disk *a, int fd, struct archive_entry *entry, acl_type_t acl_type, int ae_requested_type, const char *tname) { acl_t acl; acl_entry_t acl_entry; acl_permset_t acl_permset; int ret; int ae_type, ae_permset, ae_tag, ae_id; uid_t ae_uid; gid_t ae_gid; const char *ae_name; int entries; const char *name; ret = ARCHIVE_OK; entries = archive_entry_acl_reset(entry, ae_requested_type); if (entries == 0) return (ARCHIVE_OK); acl = acl_init(entries); while (archive_entry_acl_next(entry, ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { acl_create_entry(&acl, &acl_entry); switch (ae_tag) { case ARCHIVE_ENTRY_ACL_USER: acl_set_tag_type(acl_entry, ACL_USER); ae_uid = a->lookup_uid(a->lookup_uid_data, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_uid); break; case ARCHIVE_ENTRY_ACL_GROUP: acl_set_tag_type(acl_entry, ACL_GROUP); ae_gid = a->lookup_gid(a->lookup_gid_data, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_gid); break; case ARCHIVE_ENTRY_ACL_USER_OBJ: acl_set_tag_type(acl_entry, ACL_USER_OBJ); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); break; case ARCHIVE_ENTRY_ACL_MASK: acl_set_tag_type(acl_entry, ACL_MASK); break; case ARCHIVE_ENTRY_ACL_OTHER: acl_set_tag_type(acl_entry, ACL_OTHER); break; default: /* XXX */ break; } acl_get_permset(acl_entry, &acl_permset); acl_clear_perms(acl_permset); if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) acl_add_perm(acl_permset, ACL_EXECUTE); if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) acl_add_perm(acl_permset, ACL_WRITE); if (ae_permset & ARCHIVE_ENTRY_ACL_READ) acl_add_perm(acl_permset, ACL_READ); } name = archive_entry_pathname(entry); /* Try restoring the ACL through 'fd' if we can. */ #if HAVE_ACL_SET_FD if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) ret = ARCHIVE_OK; else #else #if HAVE_ACL_SET_FD_NP if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) ret = ARCHIVE_OK; else #endif #endif if (acl_set_file(name, acl_type, acl) != 0) { archive_set_error(&a->archive, errno, "Failed to set %s acl", tname); ret = ARCHIVE_WARN; } acl_free(acl); return (ret); } #endif #if HAVE_LSETXATTR /* * Restore extended attributes - Linux implementation */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL && strncmp(name, "xfsroot.", 8) != 0 && strncmp(name, "system.", 7) != 0) { int e; #if HAVE_FSETXATTR if (a->fd >= 0) e = fsetxattr(a->fd, name, value, size, 0); else #endif { e = lsetxattr(archive_entry_pathname(entry), name, value, size, 0); } if (e == -1) { if (errno == ENOTSUP) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else archive_set_error(&a->archive, errno, "Failed to set extended attribute"); ret = ARCHIVE_WARN; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended attribute encountered"); ret = ARCHIVE_WARN; } } return (ret); } #elif HAVE_EXTATTR_SET_FILE /* * Restore extended attributes - FreeBSD implementation */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL) { int e; int namespace; if (strncmp(name, "user.", 5) == 0) { /* "user." attributes go to user namespace */ name += 5; namespace = EXTATTR_NAMESPACE_USER; } else { /* Warn about other extended attributes. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't restore extended attribute ``%s''", name); ret = ARCHIVE_WARN; continue; } errno = 0; #if HAVE_EXTATTR_SET_FD if (a->fd >= 0) e = extattr_set_fd(a->fd, namespace, name, value, size); else #endif /* TODO: should we use extattr_set_link() instead? */ { e = extattr_set_file(archive_entry_pathname(entry), namespace, name, value, size); } if (e != (int)size) { if (errno == ENOTSUP) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else { archive_set_error(&a->archive, errno, "Failed to set extended attribute"); } ret = ARCHIVE_WARN; } } } return (ret); } #else /* * Restore extended attributes - stub implementation for unsupported systems */ static int set_xattrs(struct archive_write_disk *a) { static int warning_done = 0; /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); } /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); } #endif /* * Trivial implementations of gid/uid lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static gid_t trivial_lookup_gid(void *private_data, const char *gname, gid_t gid) { (void)private_data; /* UNUSED */ (void)gname; /* UNUSED */ return (gid); } static uid_t trivial_lookup_uid(void *private_data, const char *uname, uid_t uid) { (void)private_data; /* UNUSED */ (void)uname; /* UNUSED */ return (uid); } /* * Test if file on disk is older than entry. */ static int older(struct stat *st, struct archive_entry *entry) { /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (st->st_mtime < archive_entry_mtime(entry)) return (1); /* Definitely younger. */ if (st->st_mtime > archive_entry_mtime(entry)) return (0); /* If this platform supports fractional seconds, try those. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC /* Definitely older. */ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Definitely older. */ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_N /* older. */ if (st->st_mtime_n < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_UMTIME /* older. */ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC /* older. */ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry)) return (1); #else /* This system doesn't have high-res timestamps. */ #endif /* Same age or newer, so not older. */ return (0); } tarsnap-1.0.41/libarchive/archive_write_disk_private.h000066400000000000000000000030771476705112600231450ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_write_disk_private.h,v 1.1 2007/03/03 07:37:36 kientzle Exp $ */ #ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED #define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED struct archive_write_disk; #endif tarsnap-1.0.41/libarchive/archive_write_disk_set_standard_lookup.c000066400000000000000000000152061476705112600255270ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_disk_set_standard_lookup.c,v 1.4 2007/05/29 01:00:19 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_write_disk_private.h" struct bucket { char *name; int hash; id_t id; }; static const size_t cache_size = 127; static unsigned int hash(const char *); static gid_t lookup_gid(void *, const char *uname, gid_t); static uid_t lookup_uid(void *, const char *uname, uid_t); static void cleanup(void *); /* * Installs functions that use getpwnam()/getgrnam()---along with * a simple cache to accelerate such lookups---into the archive_write_disk * object. This is in a separate file because getpwnam()/getgrnam() * can pull in a LOT of library code (including NIS/LDAP functions, which * pull in DNS resolvers, etc). This can easily top 500kB, which makes * it inappropriate for some space-constrained applications. * * Applications that are size-sensitive may want to just use the * real default functions (defined in archive_write_disk.c) that just * use the uid/gid without the lookup. Or define your own custom functions * if you prefer. * * TODO: Replace these hash tables with simpler move-to-front LRU * lists with a bounded size (128 items?). The hash is a bit faster, * but has a bad pathology in which it thrashes a single bucket. Even * walking a list of 128 items is a lot faster than calling * getpwnam()! */ int archive_write_disk_set_standard_lookup(struct archive *a) { struct bucket *ucache = malloc(cache_size * sizeof(struct bucket)); struct bucket *gcache = malloc(cache_size * sizeof(struct bucket)); if (ucache == NULL || gcache == NULL) { free(ucache); free(gcache); return (ARCHIVE_FATAL); } memset(ucache, 0, cache_size * sizeof(struct bucket)); memset(gcache, 0, cache_size * sizeof(struct bucket)); archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup); archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup); return (ARCHIVE_OK); } static gid_t lookup_gid(void *private_data, const char *gname, gid_t gid) { int h; struct bucket *b; struct bucket *gcache = (struct bucket *)private_data; /* If no gname, just use the gid provided. */ if (gname == NULL || *gname == '\0') return (gid); /* Try to find gname in the cache. */ h = hash(gname); b = &gcache[h % cache_size ]; if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0) return ((gid_t)b->id); /* Free the cache slot for a new entry. */ if (b->name != NULL) free(b->name); b->name = strdup(gname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; #if HAVE_GRP_H { char _buffer[128]; size_t bufsize = 128; char *buffer = _buffer; struct group grent, *result; int r; for (;;) { r = getgrnam_r(gname, &grent, buffer, bufsize, &result); if (r == 0) break; if (r != ERANGE) break; bufsize *= 2; if (buffer != _buffer) free(buffer); buffer = malloc(bufsize); if (buffer == NULL) break; } if (result != NULL) gid = result->gr_gid; if (buffer != _buffer) free(buffer); } #elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a gname->gid lookup for Windows. */ #else #error No way to perform gid lookups on this platform #endif b->id = gid; return (gid); } static uid_t lookup_uid(void *private_data, const char *uname, uid_t uid) { int h; struct bucket *b; struct bucket *ucache = (struct bucket *)private_data; /* If no uname, just use the uid provided. */ if (uname == NULL || *uname == '\0') return (uid); /* Try to find uname in the cache. */ h = hash(uname); b = &ucache[h % cache_size ]; if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0) return ((uid_t)b->id); /* Free the cache slot for a new entry. */ if (b->name != NULL) free(b->name); b->name = strdup(uname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; #if HAVE_PWD_H { char _buffer[128]; size_t bufsize = 128; char *buffer = _buffer; struct passwd pwent, *result; int r; for (;;) { r = getpwnam_r(uname, &pwent, buffer, bufsize, &result); if (r == 0) break; if (r != ERANGE) break; bufsize *= 2; if (buffer != _buffer) free(buffer); buffer = malloc(bufsize); if (buffer == NULL) break; } if (result != NULL) uid = result->pw_uid; if (buffer != _buffer) free(buffer); } #elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a uname->uid lookup for Windows. */ #else #error No way to look up uids on this platform #endif b->id = uid; return (uid); } static void cleanup(void *private) { size_t i; struct bucket *cache = (struct bucket *)private; for (i = 0; i < cache_size; i++) free(cache[i].name); free(cache); } static unsigned int hash(const char *p) { /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, as used by ELF for hashing function names. */ unsigned g, h = 0; while (*p != '\0') { h = ( h << 4 ) + *p++; if (( g = h & 0xF0000000 )) { h ^= g >> 24; h &= 0x0FFFFFFF; } } return h; } tarsnap-1.0.41/libarchive/archive_write_open_fd.c000066400000000000000000000073601476705112600220650ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_open_fd.c,v 1.9 2007/01/09 08:05:56 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct write_fd_data { off_t offset; int fd; }; static int file_close(struct archive *, void *); static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, const void *buff, size_t); int archive_write_open_fd(struct archive *a, int fd) { struct write_fd_data *mine; mine = (struct write_fd_data *)malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } mine->fd = fd; return (archive_write_open(a, mine, file_open, file_write, file_close)); } static int file_open(struct archive *a, void *client_data) { struct write_fd_data *mine; struct stat st; mine = (struct write_fd_data *)client_data; if (fstat(mine->fd, &st) != 0) { archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd); return (ARCHIVE_FATAL); } /* * If this is a regular file, don't add it to itself. */ if (S_ISREG(st.st_mode)) archive_write_set_skip_file(a, st.st_dev, st.st_ino); /* * If client hasn't explicitly set the last block handling, * then set it here. */ if (archive_write_get_bytes_in_last_block(a) < 0) { /* If the output is a block or character device, fifo, * or stdout, pad the last block, otherwise leave it * unpadded. */ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || (mine->fd == 1)) /* Last block will be fully padded. */ archive_write_set_bytes_in_last_block(a, 0); else archive_write_set_bytes_in_last_block(a, 1); } return (ARCHIVE_OK); } static ssize_t file_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_fd_data *mine; ssize_t bytesWritten; mine = (struct write_fd_data *)client_data; bytesWritten = write(mine->fd, buff, length); if (bytesWritten <= 0) { archive_set_error(a, errno, "Write error"); return (-1); } return (bytesWritten); } static int file_close(struct archive *a, void *client_data) { struct write_fd_data *mine = (struct write_fd_data *)client_data; (void)a; /* UNUSED */ free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_write_open_file.c000066400000000000000000000056611476705112600224150ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct write_FILE_data { FILE *f; }; static int file_close(struct archive *, void *); static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, const void *buff, size_t); int archive_write_open_FILE(struct archive *a, FILE *f) { struct write_FILE_data *mine; mine = (struct write_FILE_data *)malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } mine->f = f; return (archive_write_open(a, mine, file_open, file_write, file_close)); } static int file_open(struct archive *a, void *client_data) { (void)a; /* UNUSED */ (void)client_data; /* UNUSED */ return (ARCHIVE_OK); } static ssize_t file_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_FILE_data *mine; size_t bytesWritten; mine = client_data; bytesWritten = fwrite(buff, 1, length, mine->f); if (bytesWritten < length) { archive_set_error(a, errno, "Write error"); return (-1); } return (bytesWritten); } static int file_close(struct archive *a, void *client_data) { struct write_FILE_data *mine = client_data; (void)a; /* UNUSED */ free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_write_open_filename.c000066400000000000000000000106511476705112600232510ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_open_filename.c,v 1.20 2008/02/19 05:46:58 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct write_file_data { int fd; char filename[1]; }; static int file_close(struct archive *, void *); static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, const void *buff, size_t); int archive_write_open_file(struct archive *a, const char *filename) { return (archive_write_open_filename(a, filename)); } int archive_write_open_filename(struct archive *a, const char *filename) { struct write_file_data *mine; if (filename == NULL || filename[0] == '\0') return (archive_write_open_fd(a, 1)); mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } strcpy(mine->filename, filename); mine->fd = -1; return (archive_write_open(a, mine, file_open, file_write, file_close)); } static int file_open(struct archive *a, void *client_data) { int flags; struct write_file_data *mine; struct stat st; mine = (struct write_file_data *)client_data; flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; /* * Open the file. */ mine->fd = open(mine->filename, flags, 0666); if (mine->fd < 0) { archive_set_error(a, errno, "Failed to open '%s'", mine->filename); return (ARCHIVE_FATAL); } if (fstat(mine->fd, &st) != 0) { archive_set_error(a, errno, "Couldn't stat '%s'", mine->filename); return (ARCHIVE_FATAL); } /* * Set up default last block handling. */ if (archive_write_get_bytes_in_last_block(a) < 0) { if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode)) /* Pad last block when writing to device or FIFO. */ archive_write_set_bytes_in_last_block(a, 0); else /* Don't pad last block otherwise. */ archive_write_set_bytes_in_last_block(a, 1); } /* * If the output file is a regular file, don't add it to * itself. If it's a device file, it's okay to add the device * entry to the output archive. */ if (S_ISREG(st.st_mode)) archive_write_set_skip_file(a, st.st_dev, st.st_ino); return (ARCHIVE_OK); } static ssize_t file_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_file_data *mine; ssize_t bytesWritten; mine = (struct write_file_data *)client_data; bytesWritten = write(mine->fd, buff, length); if (bytesWritten <= 0) { archive_set_error(a, errno, "Write error"); return (-1); } return (bytesWritten); } static int file_close(struct archive *a, void *client_data) { struct write_file_data *mine = (struct write_file_data *)client_data; (void)a; /* UNUSED */ close(mine->fd); free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_write_open_memory.c000066400000000000000000000102301476705112600227720ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $"); #include #include #include #include "archive.h" /* * This is a little tricky. I used to allow the * compression handling layer to fork the compressor, * which means this write function gets invoked in * a separate process. That would, of course, make it impossible * to actually use the data stored into memory here. * Fortunately, none of the compressors fork today and * I'm reluctant to use that route in the future but, if * forking compressors ever do reappear, this will have * to get a lot more complicated. */ struct write_memory_data { size_t used; size_t size; size_t * client_size; unsigned char * buff; }; static int memory_write_close(struct archive *, void *); static int memory_write_open(struct archive *, void *); static ssize_t memory_write(struct archive *, void *, const void *buff, size_t); /* * Client provides a pointer to a block of memory to receive * the data. The 'size' param both tells us the size of the * client buffer and lets us tell the client the final size. */ int archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used) { struct write_memory_data *mine; mine = (struct write_memory_data *)malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } memset(mine, 0, sizeof(*mine)); mine->buff = buff; mine->size = buffSize; mine->client_size = used; return (archive_write_open(a, mine, memory_write_open, memory_write, memory_write_close)); } static int memory_write_open(struct archive *a, void *client_data) { struct write_memory_data *mine; mine = client_data; mine->used = 0; if (mine->client_size != NULL) *mine->client_size = mine->used; /* Disable padding if it hasn't been set explicitly. */ if (-1 == archive_write_get_bytes_in_last_block(a)) archive_write_set_bytes_in_last_block(a, 1); return (ARCHIVE_OK); } /* * Copy the data into the client buffer. * Note that we update mine->client_size on every write. * In particular, this means the client can follow exactly * how much has been written into their buffer at any time. */ static ssize_t memory_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_memory_data *mine; mine = client_data; if (mine->used + length > mine->size) { archive_set_error(a, ENOMEM, "Buffer exhausted"); return (ARCHIVE_FATAL); } memcpy(mine->buff + mine->used, buff, length); mine->used += length; if (mine->client_size != NULL) *mine->client_size = mine->used; return (length); } static int memory_write_close(struct archive *a, void *client_data) { struct write_memory_data *mine; (void)a; /* UNUSED */ mine = client_data; free(mine); return (ARCHIVE_OK); } tarsnap-1.0.41/libarchive/archive_write_private.h000066400000000000000000000104541476705112600221300ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/archive_write_private.h,v 1.3 2008/03/15 11:04:45 kientzle Exp $ */ #ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED #define ARCHIVE_WRITE_PRIVATE_H_INCLUDED #include "archive.h" #include "archive_string.h" #include "archive_private.h" struct archive_write { struct archive archive; /* Dev/ino of the archive being written. */ dev_t skip_file_dev; ino_t skip_file_ino; /* Utility: Pointer to a block of nulls. */ const unsigned char *nulls; size_t null_length; /* Callbacks to open/read/write/close archive stream. */ archive_open_callback *client_opener; archive_write_callback *client_writer; archive_close_callback *client_closer; void *client_data; /* * Blocking information. Note that bytes_in_last_block is * misleadingly named; I should find a better name. These * control the final output from all compressors, including * compression_none. */ int bytes_per_block; int bytes_in_last_block; /* * These control whether data within a gzip/bzip2 compressed * stream gets padded or not. If pad_uncompressed is set, * the data will be padded to a full block before being * compressed. The pad_uncompressed_byte determines the value * that will be used for padding. Note that these have no * effect on compression "none." */ int pad_uncompressed; int pad_uncompressed_byte; /* TODO: Support this. */ /* * On write, the client just invokes an archive_write_set function * which sets up the data here directly. */ struct { void *data; void *config; int (*init)(struct archive_write *); int (*options)(struct archive_write *, const char *key, const char *value); int (*finish)(struct archive_write *); int (*write)(struct archive_write *, const void *, size_t); } compressor; /* * Pointers to format-specific functions for writing. They're * initialized by archive_write_set_format_XXX() calls. */ void *format_data; const char *format_name; int (*format_init)(struct archive_write *); int (*format_options)(struct archive_write *, const char *key, const char *value); int (*format_finish)(struct archive_write *); int (*format_destroy)(struct archive_write *); int (*format_finish_entry)(struct archive_write *); int (*format_write_header)(struct archive_write *, struct archive_entry *); ssize_t (*format_write_data)(struct archive_write *, const void *buff, size_t); int (*format_skip_data)(struct archive_write *, off_t); }; /* * Utility function to format a USTAR header into a buffer. If * "strict" is set, this tries to create the absolutely most portable * version of a ustar header. If "strict" is set to 0, then it will * relax certain requirements. * * Generally, format-specific declarations don't belong in this * header; this is a rare example of a function that is shared by * two very similar formats (ustar and pax). */ int __archive_write_format_header_ustar(struct archive_write *, char buff[512], struct archive_entry *, int tartype, int strict); #endif tarsnap-1.0.41/libarchive/archive_write_set_compression_none.c000066400000000000000000000170231476705112600247030ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_set_compression_none.c,v 1.16 2007/12/30 04:58:22 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_write_private.h" static int archive_compressor_none_finish(struct archive_write *a); static int archive_compressor_none_init(struct archive_write *); static int archive_compressor_none_write(struct archive_write *, const void *, size_t); struct archive_none { char *buffer; ssize_t buffer_size; char *next; /* Current insert location */ ssize_t avail; /* Free space left in buffer */ }; int archive_write_set_compression_none(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_none"); a->compressor.init = &archive_compressor_none_init; return (0); } /* * Setup callback. */ static int archive_compressor_none_init(struct archive_write *a) { int ret; struct archive_none *state; a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; a->archive.compression_name = "none"; if (a->client_opener != NULL) { ret = (a->client_opener)(&a->archive, a->client_data); if (ret != 0) return (ret); } state = (struct archive_none *)malloc(sizeof(*state)); if (state == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for output buffering"); return (ARCHIVE_FATAL); } memset(state, 0, sizeof(*state)); state->buffer_size = a->bytes_per_block; if (state->buffer_size != 0) { state->buffer = (char *)malloc(state->buffer_size); if (state->buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate output buffer"); free(state); return (ARCHIVE_FATAL); } } state->next = state->buffer; state->avail = state->buffer_size; a->compressor.data = state; a->compressor.write = archive_compressor_none_write; a->compressor.finish = archive_compressor_none_finish; return (ARCHIVE_OK); } /* * Write data to the stream. */ static int archive_compressor_none_write(struct archive_write *a, const void *vbuff, size_t length) { const char *buff; ssize_t remaining, to_copy; ssize_t bytes_written; struct archive_none *state; state = (struct archive_none *)a->compressor.data; buff = (const char *)vbuff; if (a->client_writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No write callback is registered? " "This is probably an internal programming error."); return (ARCHIVE_FATAL); } remaining = length; /* * If there is no buffer for blocking, just pass the data * straight through to the client write callback. In * particular, this supports "no write delay" operation for * special applications. Just set the block size to zero. */ if (state->buffer_size == 0) { while (remaining > 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, buff, remaining); if (bytes_written <= 0) return (ARCHIVE_FATAL); a->archive.raw_position += bytes_written; remaining -= bytes_written; buff += bytes_written; } a->archive.file_position += length; return (ARCHIVE_OK); } /* If the copy buffer isn't empty, try to fill it. */ if (state->avail < state->buffer_size) { /* If buffer is not empty... */ /* ... copy data into buffer ... */ to_copy = (remaining > state->avail) ? state->avail : remaining; memcpy(state->next, buff, to_copy); state->next += to_copy; state->avail -= to_copy; buff += to_copy; remaining -= to_copy; /* ... if it's full, write it out. */ if (state->avail == 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, state->buffer, state->buffer_size); if (bytes_written <= 0) return (ARCHIVE_FATAL); /* XXX TODO: if bytes_written < state->buffer_size */ a->archive.raw_position += bytes_written; state->next = state->buffer; state->avail = state->buffer_size; } } while (remaining > state->buffer_size) { /* Write out full blocks directly to client. */ bytes_written = (a->client_writer)(&a->archive, a->client_data, buff, state->buffer_size); if (bytes_written <= 0) return (ARCHIVE_FATAL); a->archive.raw_position += bytes_written; buff += bytes_written; remaining -= bytes_written; } if (remaining > 0) { /* Copy last bit into copy buffer. */ memcpy(state->next, buff, remaining); state->next += remaining; state->avail -= remaining; } a->archive.file_position += length; return (ARCHIVE_OK); } /* * Finish the compression. */ static int archive_compressor_none_finish(struct archive_write *a) { ssize_t block_length; ssize_t target_block_length; ssize_t bytes_written; int ret; struct archive_none *state; state = (struct archive_none *)a->compressor.data; ret = ARCHIVE_OK; if (a->client_writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No write callback is registered? " "This is probably an internal programming error."); return (ARCHIVE_FATAL); } /* If there's pending data, pad and write the last block */ if (state->next != state->buffer) { block_length = state->buffer_size - state->avail; /* Tricky calculation to determine size of last block */ if (a->bytes_in_last_block <= 0) /* Default or Zero: pad to full block */ target_block_length = a->bytes_per_block; else /* Round to next multiple of bytes_in_last_block. */ target_block_length = a->bytes_in_last_block * ( (block_length + a->bytes_in_last_block - 1) / a->bytes_in_last_block); if (target_block_length > a->bytes_per_block) target_block_length = a->bytes_per_block; if (block_length < target_block_length) { memset(state->next, 0, target_block_length - block_length); block_length = target_block_length; } bytes_written = (a->client_writer)(&a->archive, a->client_data, state->buffer, block_length); if (bytes_written <= 0) ret = ARCHIVE_FATAL; else { a->archive.raw_position += bytes_written; ret = ARCHIVE_OK; } } if (state->buffer) free(state->buffer); free(state); a->compressor.data = NULL; return (ret); } tarsnap-1.0.41/libarchive/archive_write_set_format_pax.c000066400000000000000000001242341476705112600234660ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_set_format_pax.c,v 1.49 2008/09/30 03:57:07 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" struct pax { uint64_t entry_bytes_remaining; uint64_t entry_padding; struct archive_string pax_header; }; static void add_pax_attr(struct archive_string *, const char *key, const char *value); static void add_pax_attr_int(struct archive_string *, const char *key, int64_t value); static void add_pax_attr_time(struct archive_string *, const char *key, int64_t sec, unsigned long nanos); static void add_pax_attr_w(struct archive_string *, const char *key, const wchar_t *wvalue); static ssize_t archive_write_pax_data(struct archive_write *, const void *, size_t); static int archive_write_pax_data_skip(struct archive_write *, off_t); static int archive_write_pax_finish(struct archive_write *); static int archive_write_pax_destroy(struct archive_write *); static int archive_write_pax_finish_entry(struct archive_write *); static int archive_write_pax_header(struct archive_write *, struct archive_entry *); static char *base64_encode(const char *src, size_t len); static char *build_pax_attribute_name(char *dest, const char *src); static char *build_ustar_entry_name(char *dest, const char *src, size_t src_length, const char *insert); static char *format_int(char *dest, int64_t); static int has_non_ASCII(const wchar_t *); static char *url_encode(const char *in); static int write_nulls(struct archive_write *, size_t); static void errmsg(const char *m) { size_t s = strlen(m); ssize_t written; while (s > 0) { written = write(2, m, strlen(m)); if (written <= 0) return; m += written; s -= written; } } /* * Set output format to 'restricted pax' format. * * This is the same as normal 'pax', but tries to suppress * the pax header whenever possible. This is the default for * bsdtar, for instance. */ int archive_write_set_format_pax_restricted(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r; r = archive_write_set_format_pax(&a->archive); a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; a->archive.archive_format_name = "restricted POSIX pax interchange"; return (r); } /* * Set output format to 'pax' format. */ int archive_write_set_format_pax(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct pax *pax; if (a->format_destroy != NULL) (a->format_destroy)(a); pax = (struct pax *)malloc(sizeof(*pax)); if (pax == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); return (ARCHIVE_FATAL); } memset(pax, 0, sizeof(*pax)); a->format_data = pax; a->pad_uncompressed = 1; a->format_name = "pax"; a->format_write_header = archive_write_pax_header; a->format_write_data = archive_write_pax_data; a->format_finish = archive_write_pax_finish; a->format_destroy = archive_write_pax_destroy; a->format_finish_entry = archive_write_pax_finish_entry; a->format_skip_data = archive_write_pax_data_skip; a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange"; return (ARCHIVE_OK); } /* * Note: This code assumes that 'nanos' has the same sign as 'sec', * which implies that sec=-1, nanos=200000000 represents -1.2 seconds * and not -0.8 seconds. This is a pretty pedantic point, as we're * unlikely to encounter many real files created before Jan 1, 1970, * much less ones with timestamps recorded to sub-second resolution. */ static void add_pax_attr_time(struct archive_string *as, const char *key, int64_t sec, unsigned long nanos) { int digit, i; char *t; /* * Note that each byte contributes fewer than 3 base-10 * digits, so this will always be big enough. */ char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)]; tmp[sizeof(tmp) - 1] = 0; t = tmp + sizeof(tmp) - 1; /* Skip trailing zeros in the fractional part. */ for (digit = 0, i = 10; i > 0 && digit == 0; i--) { digit = nanos % 10; nanos /= 10; } /* Only format the fraction if it's non-zero. */ if (i > 0) { while (i > 0) { *--t = "0123456789"[digit]; digit = nanos % 10; nanos /= 10; i--; } *--t = '.'; } t = format_int(t, sec); add_pax_attr(as, key, t); } static char * format_int(char *t, int64_t i) { uint64_t ui; if (i < 0) ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i); else ui = i; do { *--t = "0123456789"[ui % 10]; } while (ui /= 10); if (i < 0) *--t = '-'; return (t); } static void add_pax_attr_int(struct archive_string *as, const char *key, int64_t value) { char tmp[1 + 3 * sizeof(value)]; tmp[sizeof(tmp) - 1] = 0; add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value)); } static char * utf8_encode(const wchar_t *wval) { int utf8len; const wchar_t *wp; unsigned long wc; char *utf8_value, *p; utf8len = 0; for (wp = wval; *wp != L'\0'; ) { wc = *wp++; if (wc >= 0xd800 && wc <= 0xdbff && *wp >= 0xdc00 && *wp <= 0xdfff) { /* This is a surrogate pair. Combine into a * full Unicode value before encoding into * UTF-8. */ wc = (wc - 0xd800) << 10; /* High 10 bits */ wc += (*wp++ - 0xdc00); /* Low 10 bits */ wc += 0x10000; /* Skip BMP */ } if (wc <= 0x7f) utf8len++; else if (wc <= 0x7ff) utf8len += 2; else if (wc <= 0xffff) utf8len += 3; else if (wc <= 0x1fffff) utf8len += 4; else if (wc <= 0x3ffffff) utf8len += 5; else if (wc <= 0x7fffffff) utf8len += 6; /* Ignore larger values; UTF-8 can't encode them. */ } utf8_value = (char *)malloc(utf8len + 1); if (utf8_value == NULL) { __archive_errx(1, "Not enough memory for attributes"); return (NULL); } for (wp = wval, p = utf8_value; *wp != L'\0'; ) { wc = *wp++; if (wc >= 0xd800 && wc <= 0xdbff && *wp >= 0xdc00 && *wp <= 0xdfff) { /* Combine surrogate pair. */ wc = (wc - 0xd800) << 10; wc += *wp++ - 0xdc00 + 0x10000; } if (wc <= 0x7f) { *p++ = (char)wc; } else if (wc <= 0x7ff) { p[0] = 0xc0 | ((wc >> 6) & 0x1f); p[1] = 0x80 | (wc & 0x3f); p += 2; } else if (wc <= 0xffff) { p[0] = 0xe0 | ((wc >> 12) & 0x0f); p[1] = 0x80 | ((wc >> 6) & 0x3f); p[2] = 0x80 | (wc & 0x3f); p += 3; } else if (wc <= 0x1fffff) { p[0] = 0xf0 | ((wc >> 18) & 0x07); p[1] = 0x80 | ((wc >> 12) & 0x3f); p[2] = 0x80 | ((wc >> 6) & 0x3f); p[3] = 0x80 | (wc & 0x3f); p += 4; } else if (wc <= 0x3ffffff) { p[0] = 0xf8 | ((wc >> 24) & 0x03); p[1] = 0x80 | ((wc >> 18) & 0x3f); p[2] = 0x80 | ((wc >> 12) & 0x3f); p[3] = 0x80 | ((wc >> 6) & 0x3f); p[4] = 0x80 | (wc & 0x3f); p += 5; } else if (wc <= 0x7fffffff) { p[0] = 0xfc | ((wc >> 30) & 0x01); p[1] = 0x80 | ((wc >> 24) & 0x3f); p[1] = 0x80 | ((wc >> 18) & 0x3f); p[2] = 0x80 | ((wc >> 12) & 0x3f); p[3] = 0x80 | ((wc >> 6) & 0x3f); p[4] = 0x80 | (wc & 0x3f); p += 6; } /* Ignore larger values; UTF-8 can't encode them. */ } *p = '\0'; return (utf8_value); } static void add_pax_attr_w(struct archive_string *as, const char *key, const wchar_t *wval) { char *utf8_value = utf8_encode(wval); if (utf8_value == NULL) return; add_pax_attr(as, key, utf8_value); free(utf8_value); } /* * Add a key/value attribute to the pax header. This function handles * the length field and various other syntactic requirements. */ static void add_pax_attr(struct archive_string *as, const char *key, const char *value) { int digits, i, len, next_ten; char tmp[1 + 3 * sizeof(int)]; /* < 3 base-10 digits per byte */ /*- * PAX attributes have the following layout: * <=> */ len = 1 + strlen(key) + 1 + strlen(value) + 1; /* * The field includes the length of the field, so * computing the correct length is tricky. I start by * counting the number of base-10 digits in 'len' and * computing the next higher power of 10. */ next_ten = 1; digits = 0; i = len; while (i > 0) { i = i / 10; digits++; next_ten = next_ten * 10; } /* * For example, if string without the length field is 99 * chars, then adding the 2 digit length "99" will force the * total length past 100, requiring an extra digit. The next * statement adjusts for this effect. */ if (len + digits >= next_ten) digits++; /* Now, we have the right length so we can build the line. */ tmp[sizeof(tmp) - 1] = 0; /* Null-terminate the work area. */ archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits)); archive_strappend_char(as, ' '); archive_strcat(as, key); archive_strappend_char(as, '='); archive_strcat(as, value); archive_strappend_char(as, '\n'); } static void archive_write_pax_header_xattrs(struct pax *pax, struct archive_entry *entry) { struct archive_string s; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; char *encoded_value; char *url_encoded_name = NULL, *encoded_name = NULL; wchar_t *wcs_name = NULL; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); /* Name is URL-encoded, then converted to wchar_t, * then UTF-8 encoded. */ url_encoded_name = url_encode(name); if (url_encoded_name != NULL) { /* Convert narrow-character to wide-character. */ int wcs_length = strlen(url_encoded_name); wcs_name = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); if (wcs_name == NULL) __archive_errx(1, "No memory for xattr conversion"); mbstowcs(wcs_name, url_encoded_name, wcs_length); wcs_name[wcs_length] = 0; free(url_encoded_name); /* Done with this. */ } if (wcs_name != NULL) { encoded_name = utf8_encode(wcs_name); free(wcs_name); /* Done with wchar_t name. */ } encoded_value = base64_encode((const char *)value, size); if (encoded_name != NULL && encoded_value != NULL) { archive_string_init(&s); archive_strcpy(&s, "LIBARCHIVE.xattr."); archive_strcat(&s, encoded_name); add_pax_attr(&(pax->pax_header), s.s, encoded_value); archive_string_free(&s); } free(encoded_name); free(encoded_value); } } /* * TODO: Consider adding 'comment' and 'charset' fields to * archive_entry so that clients can specify them. Also, consider * adding generic key/value tags so clients can add arbitrary * key/value data. */ static int archive_write_pax_header(struct archive_write *a, struct archive_entry *entry_original) { struct archive_entry *entry_main; const char *p; char *t; const wchar_t *wp; const char *suffix; int need_extension, r, ret; struct pax *pax; const char *hdrcharset = NULL; const char *hardlink; const char *path = NULL, *linkpath = NULL; const char *uname = NULL, *gname = NULL; const wchar_t *path_w = NULL, *linkpath_w = NULL; const wchar_t *uname_w = NULL, *gname_w = NULL; char paxbuff[512]; char ustarbuff[512]; char ustar_entry_name[256]; char pax_entry_name[256]; ret = ARCHIVE_OK; need_extension = 0; pax = (struct pax *)a->format_data; hardlink = archive_entry_hardlink(entry_original); /* Make sure this is a type of entry that we can handle here */ if (hardlink == NULL) { switch (archive_entry_filetype(entry_original)) { case AE_IFBLK: case AE_IFCHR: case AE_IFIFO: case AE_IFLNK: case AE_IFREG: break; case AE_IFDIR: /* * Ensure a trailing '/'. Modify the original * entry so the client sees the change. */ p = archive_entry_pathname(entry_original); if (p[strlen(p) - 1] != '/') { t = (char *)malloc(strlen(p) + 2); if (t == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); return(ARCHIVE_FATAL); } strcpy(t, p); strcat(t, "/"); archive_entry_copy_pathname(entry_original, t); free(t); } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (type=0%lo)", (unsigned long)archive_entry_filetype(entry_original)); return (ARCHIVE_WARN); } } /* Copy entry so we can modify it as needed. */ entry_main = archive_entry_clone(entry_original); archive_string_empty(&(pax->pax_header)); /* Blank our work area. */ /* * First, check the name fields and see if any of them * require binary coding. If any of them does, then all of * them do. */ hdrcharset = NULL; path = archive_entry_pathname(entry_main); path_w = archive_entry_pathname_w(entry_main); if (path != NULL && path_w == NULL) { archive_set_error(&a->archive, EILSEQ, "Can't translate pathname '%s' to UTF-8", path); ret = ARCHIVE_WARN; hdrcharset = "BINARY"; } uname = archive_entry_uname(entry_main); uname_w = archive_entry_uname_w(entry_main); if (uname != NULL && uname_w == NULL) { archive_set_error(&a->archive, EILSEQ, "Can't translate uname '%s' to UTF-8", uname); ret = ARCHIVE_WARN; hdrcharset = "BINARY"; } gname = archive_entry_gname(entry_main); gname_w = archive_entry_gname_w(entry_main); if (gname != NULL && gname_w == NULL) { archive_set_error(&a->archive, EILSEQ, "Can't translate gname '%s' to UTF-8", gname); ret = ARCHIVE_WARN; hdrcharset = "BINARY"; } linkpath = hardlink; if (linkpath != NULL) { linkpath_w = archive_entry_hardlink_w(entry_main); } else { linkpath = archive_entry_symlink(entry_main); if (linkpath != NULL) linkpath_w = archive_entry_symlink_w(entry_main); } if (linkpath != NULL && linkpath_w == NULL) { archive_set_error(&a->archive, EILSEQ, "Can't translate linkpath '%s' to UTF-8", linkpath); ret = ARCHIVE_WARN; hdrcharset = "BINARY"; } /* Store the header encoding first, to be nice to readers. */ if (hdrcharset != NULL) add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset); /* * If name is too long, or has non-ASCII characters, add * 'path' to pax extended attrs. (Note that an unconvertible * name must have non-ASCII characters.) */ if (path == NULL) { /* We don't have a narrow version, so we have to store * the wide version. */ add_pax_attr_w(&(pax->pax_header), "path", path_w); archive_entry_set_pathname(entry_main, "@WidePath"); need_extension = 1; } else if (has_non_ASCII(path_w)) { /* We have non-ASCII characters. */ if (path_w == NULL || hdrcharset != NULL) { /* Can't do UTF-8, so store it raw. */ add_pax_attr(&(pax->pax_header), "path", path); } else { /* Store UTF-8 */ add_pax_attr_w(&(pax->pax_header), "path", path_w); } archive_entry_set_pathname(entry_main, build_ustar_entry_name(ustar_entry_name, path, strlen(path), NULL)); need_extension = 1; } else { /* We have an all-ASCII path; we'd like to just store * it in the ustar header if it will fit. Yes, this * duplicates some of the logic in * archive_write_set_format_ustar.c */ if (strlen(path) <= 100) { /* Fits in the old 100-char tar name field. */ } else { /* Find largest suffix that will fit. */ /* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */ suffix = strchr(path + strlen(path) - 100 - 1, '/'); /* Don't attempt an empty prefix. */ if (suffix == path) suffix = strchr(suffix + 1, '/'); /* We can put it in the ustar header if it's * all ASCII and it's either <= 100 characters * or can be split at a '/' into a prefix <= * 155 chars and a suffix <= 100 chars. (Note * the strchr() above will return NULL exactly * when the path can't be split.) */ if (suffix == NULL /* Suffix > 100 chars. */ || suffix[1] == '\0' /* empty suffix */ || suffix - path > 155) /* Prefix > 155 chars */ { if (path_w == NULL || hdrcharset != NULL) { /* Can't do UTF-8, so store it raw. */ add_pax_attr(&(pax->pax_header), "path", path); } else { /* Store UTF-8 */ add_pax_attr_w(&(pax->pax_header), "path", path_w); } archive_entry_set_pathname(entry_main, build_ustar_entry_name(ustar_entry_name, path, strlen(path), NULL)); need_extension = 1; } } } if (linkpath != NULL) { /* If link name is too long or has non-ASCII characters, add * 'linkpath' to pax extended attrs. */ if (strlen(linkpath) > 100 || linkpath_w == NULL || linkpath_w == NULL || has_non_ASCII(linkpath_w)) { if (linkpath_w == NULL || hdrcharset != NULL) /* If the linkpath is not convertible * to wide, or we're encoding in * binary anyway, store it raw. */ add_pax_attr(&(pax->pax_header), "linkpath", linkpath); else /* If the link is long or has a * non-ASCII character, store it as a * pax extended attribute. */ add_pax_attr_w(&(pax->pax_header), "linkpath", linkpath_w); if (strlen(linkpath) > 100) { if (hardlink != NULL) archive_entry_set_hardlink(entry_main, "././@LongHardLink"); else archive_entry_set_symlink(entry_main, "././@LongSymLink"); } need_extension = 1; } } /* If file size is too large, add 'size' to pax extended attrs. */ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) { add_pax_attr_int(&(pax->pax_header), "size", archive_entry_size(entry_main)); need_extension = 1; } /* If numeric GID is too large, add 'gid' to pax extended attrs. */ if (archive_entry_gid(entry_main) >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "gid", archive_entry_gid(entry_main)); need_extension = 1; } /* If group name is too large or has non-ASCII characters, add * 'gname' to pax extended attrs. */ if (gname != NULL) { if (strlen(gname) > 31 || gname_w == NULL || has_non_ASCII(gname_w)) { if (gname_w == NULL || hdrcharset != NULL) { add_pax_attr(&(pax->pax_header), "gname", gname); } else { add_pax_attr_w(&(pax->pax_header), "gname", gname_w); } need_extension = 1; } } /* If numeric UID is too large, add 'uid' to pax extended attrs. */ if (archive_entry_uid(entry_main) >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "uid", archive_entry_uid(entry_main)); need_extension = 1; } /* Add 'uname' to pax extended attrs if necessary. */ if (uname != NULL) { if (strlen(uname) > 31 || uname_w == NULL || has_non_ASCII(uname_w)) { if (uname_w == NULL || hdrcharset != NULL) { add_pax_attr(&(pax->pax_header), "uname", uname); } else { add_pax_attr_w(&(pax->pax_header), "uname", uname_w); } need_extension = 1; } } /* * POSIX/SUSv3 doesn't provide a standard key for large device * numbers. I use the same keys here that Joerg Schilling * used for 'star.' (Which, somewhat confusingly, are called * "devXXX" even though they code "rdev" values.) No doubt, * other implementations use other keys. Note that there's no * reason we can't write the same information into a number of * different keys. * * Of course, this is only needed for block or char device entries. */ if (archive_entry_filetype(entry_main) == AE_IFBLK || archive_entry_filetype(entry_main) == AE_IFCHR) { /* * If rdevmajor is too large, add 'SCHILY.devmajor' to * extended attributes. */ dev_t rdevmajor, rdevminor; rdevmajor = archive_entry_rdevmajor(entry_main); rdevminor = archive_entry_rdevminor(entry_main); if (rdevmajor >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor", rdevmajor); /* * Non-strict formatting below means we don't * have to truncate here. Not truncating improves * the chance that some more modern tar archivers * (such as GNU tar 1.13) can restore the full * value even if they don't understand the pax * extended attributes. See my rant below about * file size fields for additional details. */ /* archive_entry_set_rdevmajor(entry_main, rdevmajor & ((1 << 18) - 1)); */ need_extension = 1; } /* * If devminor is too large, add 'SCHILY.devminor' to * extended attributes. */ if (rdevminor >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor", rdevminor); /* Truncation is not necessary here, either. */ /* archive_entry_set_rdevminor(entry_main, rdevminor & ((1 << 18) - 1)); */ need_extension = 1; } } /* * Technically, the mtime field in the ustar header can * support 33 bits, but many platforms use signed 32-bit time * values. The cutoff of 0x7fffffff here is a compromise. * Yes, this check is duplicated just below; this helps to * avoid writing an mtime attribute just to handle a * high-resolution timestamp in "restricted pax" mode. */ if (!need_extension && ((archive_entry_mtime(entry_main) < 0) || (archive_entry_mtime(entry_main) >= 0x7fffffff))) need_extension = 1; /* I use a star-compatible file flag attribute. */ p = archive_entry_fflags_text(entry_main); if (!need_extension && p != NULL && *p != '\0') need_extension = 1; /* If there are non-trivial ACL entries, we need an extension. */ if (!need_extension && archive_entry_acl_count(entry_original, ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0) need_extension = 1; /* If there are non-trivial ACL entries, we need an extension. */ if (!need_extension && archive_entry_acl_count(entry_original, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0) need_extension = 1; /* If there are extended attributes, we need an extension */ if (!need_extension && archive_entry_xattr_count(entry_original) > 0) need_extension = 1; /* * The following items are handled differently in "pax * restricted" format. In particular, in "pax restricted" * format they won't be added unless need_extension is * already set (we're already generating an extended header, so * may as well include these). */ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED || need_extension) { if (archive_entry_mtime(entry_main) < 0 || archive_entry_mtime(entry_main) >= 0x7fffffff || archive_entry_mtime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "mtime", archive_entry_mtime(entry_main), archive_entry_mtime_nsec(entry_main)); if (archive_entry_ctime(entry_main) != 0 || archive_entry_ctime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "ctime", archive_entry_ctime(entry_main), archive_entry_ctime_nsec(entry_main)); if (archive_entry_atime(entry_main) != 0 || archive_entry_atime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "atime", archive_entry_atime(entry_main), archive_entry_atime_nsec(entry_main)); /* Store birth/creationtime only if it's earlier than mtime */ if (archive_entry_birthtime_is_set(entry_main) && archive_entry_birthtime(entry_main) < archive_entry_mtime(entry_main)) add_pax_attr_time(&(pax->pax_header), "LIBARCHIVE.creationtime", archive_entry_birthtime(entry_main), archive_entry_birthtime_nsec(entry_main)); /* I use a star-compatible file flag attribute. */ p = archive_entry_fflags_text(entry_main); if (p != NULL && *p != '\0') add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p); /* I use star-compatible ACL attributes. */ wp = archive_entry_acl_text_w(entry_original, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID); if (wp != NULL && *wp != L'\0') add_pax_attr_w(&(pax->pax_header), "SCHILY.acl.access", wp); wp = archive_entry_acl_text_w(entry_original, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID); if (wp != NULL && *wp != L'\0') add_pax_attr_w(&(pax->pax_header), "SCHILY.acl.default", wp); /* Include star-compatible metadata info. */ /* Note: "SCHILY.dev{major,minor}" are NOT the * major/minor portions of "SCHILY.dev". */ add_pax_attr_int(&(pax->pax_header), "SCHILY.dev", archive_entry_dev(entry_main)); add_pax_attr_int(&(pax->pax_header), "SCHILY.ino", archive_entry_ino(entry_main)); add_pax_attr_int(&(pax->pax_header), "SCHILY.nlink", archive_entry_nlink(entry_main)); /* Store extended attributes */ archive_write_pax_header_xattrs(pax, entry_original); } /* Only regular files have data. */ if (archive_entry_filetype(entry_main) != AE_IFREG) archive_entry_set_size(entry_main, 0); /* * Pax-restricted does not store data for hardlinks, in order * to improve compatibility with ustar. */ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && hardlink != NULL) archive_entry_set_size(entry_main, 0); /* * XXX Full pax interchange format does permit a hardlink * entry to have data associated with it. I'm not supporting * that here because the client expects me to tell them whether * or not this format expects data for hardlinks. If I * don't check here, then every pax archive will end up with * duplicated data for hardlinks. Someday, there may be * need to select this behavior, in which case the following * will need to be revisited. XXX */ if (hardlink != NULL) archive_entry_set_size(entry_main, 0); /* Format 'ustar' header for main entry. * * The trouble with file size: If the reader can't understand * the file size, they may not be able to locate the next * entry and the rest of the archive is toast. Pax-compliant * readers are supposed to ignore the file size in the main * header, so the question becomes how to maximize portability * for readers that don't support pax attribute extensions. * For maximum compatibility, I permit numeric extensions in * the main header so that the file size stored will always be * correct, even if it's in a format that only some * implementations understand. The technique used here is: * * a) If possible, follow the standard exactly. This handles * files up to 8 gigabytes minus 1. * * b) If that fails, try octal but omit the field terminator. * That handles files up to 64 gigabytes minus 1. * * c) Otherwise, use base-256 extensions. That handles files * up to 2^63 in this implementation, with the potential to * go up to 2^94. That should hold us for a while. ;-) * * The non-strict formatter uses similar logic for other * numeric fields, though they're less critical. */ __archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0); /* If we built any extended attributes, write that entry first. */ if (archive_strlen(&(pax->pax_header)) > 0) { struct archive_entry *pax_attr_entry; time_t s; uid_t uid; gid_t gid; mode_t mode; long ns; pax_attr_entry = archive_entry_new(); p = archive_entry_pathname(entry_main); archive_entry_set_pathname(pax_attr_entry, build_pax_attribute_name(pax_entry_name, p)); archive_entry_set_size(pax_attr_entry, archive_strlen(&(pax->pax_header))); /* Copy uid/gid (but clip to ustar limits). */ uid = archive_entry_uid(entry_main); if (uid >= 1 << 18) uid = (1 << 18) - 1; archive_entry_set_uid(pax_attr_entry, uid); gid = archive_entry_gid(entry_main); if (gid >= 1 << 18) gid = (1 << 18) - 1; archive_entry_set_gid(pax_attr_entry, gid); /* Copy mode over (but not setuid/setgid bits) */ mode = archive_entry_mode(entry_main); #ifdef S_ISUID mode &= ~S_ISUID; #endif #ifdef S_ISGID mode &= ~S_ISGID; #endif #ifdef S_ISVTX mode &= ~S_ISVTX; #endif archive_entry_set_mode(pax_attr_entry, mode); /* Copy uname/gname. */ archive_entry_set_uname(pax_attr_entry, archive_entry_uname(entry_main)); archive_entry_set_gname(pax_attr_entry, archive_entry_gname(entry_main)); /* Copy mtime, but clip to ustar limits. */ s = archive_entry_mtime(entry_main); ns = archive_entry_mtime_nsec(entry_main); if (s < 0) { s = 0; ns = 0; } if (s > 0x7fffffff) { s = 0x7fffffff; ns = 0; } archive_entry_set_mtime(pax_attr_entry, s, ns); /* Ditto for atime. */ s = archive_entry_atime(entry_main); ns = archive_entry_atime_nsec(entry_main); if (s < 0) { s = 0; ns = 0; } if (s > 0x7fffffff) { s = 0x7fffffff; ns = 0; } archive_entry_set_atime(pax_attr_entry, s, ns); /* Standard ustar doesn't support ctime. */ archive_entry_set_ctime(pax_attr_entry, 0, 0); r = __archive_write_format_header_ustar(a, paxbuff, pax_attr_entry, 'x', 1); archive_entry_free(pax_attr_entry); /* Note that the 'x' header shouldn't ever fail to format */ if (r != 0) { const char *msg = "archive_write_pax_header: " "'x' header failed?! This can't happen.\n"; errmsg(msg); exit(1); } r = (a->compressor.write)(a, paxbuff, 512); if (r != ARCHIVE_OK) { pax->entry_bytes_remaining = 0; pax->entry_padding = 0; return (ARCHIVE_FATAL); } pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)); pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); r = (a->compressor.write)(a, pax->pax_header.s, archive_strlen(&(pax->pax_header))); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ return (ARCHIVE_FATAL); } /* Pad out the end of the entry. */ r = write_nulls(a, pax->entry_padding); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ return (ARCHIVE_FATAL); } pax->entry_bytes_remaining = pax->entry_padding = 0; } /* Write the header for main entry. */ r = (a->compressor.write)(a, ustarbuff, 512); if (r != ARCHIVE_OK) return (r); /* * Inform the client of the on-disk size we're using, so * they can avoid unnecessarily writing a body for something * that we're just going to ignore. */ archive_entry_set_size(entry_original, archive_entry_size(entry_main)); pax->entry_bytes_remaining = archive_entry_size(entry_main); pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); archive_entry_free(entry_main); return (ret); } /* * We need a valid name for the regular 'ustar' entry. This routine * tries to hack something more-or-less reasonable. * * The approach here tries to preserve leading dir names. We do so by * working with four sections: * 1) "prefix" directory names, * 2) "suffix" directory names, * 3) inserted dir name (optional), * 4) filename. * * These sections must satisfy the following requirements: * * Parts 1 & 2 together form an initial portion of the dir name. * * Part 3 is specified by the caller. (It should not contain a leading * or trailing '/'.) * * Part 4 forms an initial portion of the base filename. * * The filename must be <= 99 chars to fit the ustar 'name' field. * * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld. * * Part 1 must be <= 155 chars to fit the ustar 'prefix' field. * * If the original name ends in a '/', the new name must also end in a '/' * * Trailing '/.' sequences may be stripped. * * Note: Recall that the ustar format does not store the '/' separating * parts 1 & 2, but does store the '/' separating parts 2 & 3. */ static char * build_ustar_entry_name(char *dest, const char *src, size_t src_length, const char *insert) { const char *prefix, *prefix_end; const char *suffix, *suffix_end; const char *filename, *filename_end; char *p; int need_slash = 0; /* Was there a trailing slash? */ size_t suffix_length = 99; int insert_length; /* Length of additional dir element to be added. */ if (insert == NULL) insert_length = 0; else /* +2 here allows for '/' before and after the insert. */ insert_length = strlen(insert) + 2; /* Step 0: Quick bailout in a common case. */ if (src_length < 100 && insert == NULL) { strncpy(dest, src, src_length); dest[src_length] = '\0'; return (dest); } /* Step 1: Locate filename and enforce the length restriction. */ filename_end = src + src_length; /* Remove trailing '/' chars and '/.' pairs. */ for (;;) { if (filename_end > src && filename_end[-1] == '/') { filename_end --; need_slash = 1; /* Remember to restore trailing '/'. */ continue; } if (filename_end > src + 1 && filename_end[-1] == '.' && filename_end[-2] == '/') { filename_end -= 2; need_slash = 1; /* "foo/." will become "foo/" */ continue; } break; } if (need_slash) suffix_length--; /* Find start of filename. */ filename = filename_end - 1; while ((filename > src) && (*filename != '/')) filename --; if ((*filename == '/') && (filename < filename_end - 1)) filename ++; /* Adjust filename_end so that filename + insert fits in 99 chars. */ suffix_length -= insert_length; if (filename_end > filename + suffix_length) filename_end = filename + suffix_length; /* Calculate max size for "suffix" section (#3 above). */ suffix_length -= filename_end - filename; /* Step 2: Locate the "prefix" section of the dirname, including * trailing '/'. */ prefix = src; prefix_end = prefix + 155; if (prefix_end > filename) prefix_end = filename; while (prefix_end > prefix && *prefix_end != '/') prefix_end--; if ((prefix_end < filename) && (*prefix_end == '/')) prefix_end++; /* Step 3: Locate the "suffix" section of the dirname, * including trailing '/'. */ suffix = prefix_end; suffix_end = suffix + suffix_length; /* Enforce limit. */ if (suffix_end > filename) suffix_end = filename; if (suffix_end < suffix) suffix_end = suffix; while (suffix_end > suffix && *suffix_end != '/') suffix_end--; if ((suffix_end < filename) && (*suffix_end == '/')) suffix_end++; /* Step 4: Build the new name. */ /* The OpenBSD strlcpy function is safer, but less portable. */ /* Rather than maintain two versions, just use the strncpy version. */ p = dest; if (prefix_end > prefix) { strncpy(p, prefix, prefix_end - prefix); p += prefix_end - prefix; } if (suffix_end > suffix) { strncpy(p, suffix, suffix_end - suffix); p += suffix_end - suffix; } if (insert != NULL) { /* Note: assume insert does not have leading or trailing '/' */ strcpy(p, insert); p += strlen(insert); *p++ = '/'; } strncpy(p, filename, filename_end - filename); p += filename_end - filename; if (need_slash) *p++ = '/'; *p++ = '\0'; return (dest); } /* * The ustar header for the pax extended attributes must have a * reasonable name: SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename' * where 'pid' is the PID of the archiving process. Unfortunately, * that makes testing a pain since the output varies for each run, * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename' * for now. (Someday, I'll make this settable. Then I can use the * SUS recommendation as default and test harnesses can override it * to get predictable results.) * * Joerg Schilling has argued that this is unnecessary because, in * practice, if the pax extended attributes get extracted as regular * files, no one is going to bother reading those attributes to * manually restore them. Based on this, 'star' uses * /tmp/PaxHeader/'basename' as the ustar header name. This is a * tempting argument, in part because it's simpler than the SUSv3 * recommendation, but I'm not entirely convinced. I'm also * uncomfortable with the fact that "/tmp" is a Unix-ism. * * The following routine leverages build_ustar_entry_name() above and * so is simpler than you might think. It just needs to provide the * additional path element and handle a few pathological cases). */ static char * build_pax_attribute_name(char *dest, const char *src) { char buff[64]; const char *p; /* Handle the null filename case. */ if (src == NULL || *src == '\0') { strcpy(dest, "PaxHeader/blank"); return (dest); } /* Prune final '/' and other unwanted final elements. */ p = src + strlen(src); for (;;) { /* Ends in "/", remove the '/' */ if (p > src && p[-1] == '/') { --p; continue; } /* Ends in "/.", remove the '.' */ if (p > src + 1 && p[-1] == '.' && p[-2] == '/') { --p; continue; } break; } /* Pathological case: After above, there was nothing left. * This includes "/." "/./." "/.//./." etc. */ if (p == src) { strcpy(dest, "/PaxHeader/rootdir"); return (dest); } /* Convert unadorned "." into a suitable filename. */ if (*src == '.' && p == src + 1) { strcpy(dest, "PaxHeader/currentdir"); return (dest); } /* * TODO: Push this string into the 'pax' structure to avoid * recomputing it every time. That will also open the door * to having clients override it. */ #if HAVE_GETPID && 0 /* Disable this for now; see above comment. */ sprintf(buff, "PaxHeader.%d", getpid()); #else /* If the platform can't fetch the pid, don't include it. */ strcpy(buff, "PaxHeader"); #endif /* General case: build a ustar-compatible name adding "/PaxHeader/". */ build_ustar_entry_name(dest, src, p - src, buff); return (dest); } /* Write two null blocks for the end of archive */ static int archive_write_pax_finish(struct archive_write *a) { if (a->compressor.write == NULL) return (ARCHIVE_OK); return (write_nulls(a, 512 * 2)); } static int archive_write_pax_destroy(struct archive_write *a) { struct pax *pax; pax = (struct pax *)a->format_data; if (pax == NULL) return (ARCHIVE_OK); archive_string_free(&pax->pax_header); free(pax); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_pax_finish_entry(struct archive_write *a) { struct pax *pax; int ret; pax = (struct pax *)a->format_data; ret = write_nulls(a, pax->entry_bytes_remaining + pax->entry_padding); pax->entry_bytes_remaining = pax->entry_padding = 0; return (ret); } static int write_nulls(struct archive_write *a, size_t padding) { int ret, to_write; while (padding > 0) { to_write = padding < a->null_length ? padding : a->null_length; ret = (a->compressor.write)(a, a->nulls, to_write); if (ret != ARCHIVE_OK) return (ret); padding -= to_write; } return (ARCHIVE_OK); } static ssize_t archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) { struct pax *pax; int ret; pax = (struct pax *)a->format_data; if (s > pax->entry_bytes_remaining) s = pax->entry_bytes_remaining; ret = (a->compressor.write)(a, buff, s); pax->entry_bytes_remaining -= s; if (ret == ARCHIVE_OK) return (s); else return (ret); } static int archive_write_pax_data_skip(struct archive_write *a, off_t s) { struct pax *pax; off_t skiplen; pax = (struct pax *)a->format_data; /* Skip entry bytes. */ if (s > (off_t)(pax->entry_bytes_remaining)) skiplen = pax->entry_bytes_remaining; else skiplen = s; pax->entry_bytes_remaining -= skiplen; s -= skiplen; /* Skip padding bytes. */ if (s > (off_t)(pax->entry_padding)) skiplen = pax->entry_padding; else skiplen = s; pax->entry_padding -= skiplen; s -= skiplen; /* Anything left is an error. */ if (s > 0) { archive_set_error(&a->archive, E2BIG, "Skip length too long"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static int has_non_ASCII(const wchar_t *wp) { if (wp == NULL) return (1); while (*wp != L'\0' && *wp < 128) wp++; return (*wp != L'\0'); } /* * Used by extended attribute support; encodes the name * so that there will be no '=' characters in the result. */ static char * url_encode(const char *in) { const char *s; char *d; int out_len = 0; char *out; for (s = in; *s != '\0'; s++) { if (*s < 33 || *s > 126 || *s == '%' || *s == '=') out_len += 3; else out_len++; } out = (char *)malloc(out_len + 1); if (out == NULL) return (NULL); for (s = in, d = out; *s != '\0'; s++) { /* encode any non-printable ASCII character or '%' or '=' */ if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { /* URL encoding is '%' followed by two hex digits */ *d++ = '%'; *d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)]; *d++ = "0123456789ABCDEF"[0x0f & *s]; } else { *d++ = *s; } } *d = '\0'; return (out); } /* * Encode a sequence of bytes into a C string using base-64 encoding. * * Returns a null-terminated C string allocated with malloc(); caller * is responsible for freeing the result. */ static char * base64_encode(const char *s, size_t len) { static const char digits[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d', 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s', 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7', '8','9','+','/' }; int v; char *d, *out; /* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */ out = (char *)malloc((len * 4 + 2) / 3 + 1); if (out == NULL) return (NULL); d = out; /* Convert each group of 3 bytes into 4 characters. */ while (len >= 3) { v = (((int)s[0] << 16) & 0xff0000) | (((int)s[1] << 8) & 0xff00) | (((int)s[2]) & 0x00ff); s += 3; len -= 3; *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; *d++ = digits[(v >> 6) & 0x3f]; *d++ = digits[(v) & 0x3f]; } /* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */ switch (len) { case 0: break; case 1: v = (((int)s[0] << 16) & 0xff0000); *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; break; case 2: v = (((int)s[0] << 16) & 0xff0000) | (((int)s[1] << 8) & 0xff00); *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; *d++ = digits[(v >> 6) & 0x3f]; break; } /* Add trailing NUL character so output is a valid C string. */ *d++ = '\0'; return (out); } tarsnap-1.0.41/libarchive/archive_write_set_format_ustar.c000066400000000000000000000417011476705112600240310ustar00rootroot00000000000000/*- * 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: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.27 2008/05/26 17:00:23 kientzle Exp $"); #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_private.h" #include "archive_write_private.h" struct ustar { uint64_t entry_bytes_remaining; uint64_t entry_padding; }; /* * Define structure of POSIX 'ustar' tar header. */ #define USTAR_name_offset 0 #define USTAR_name_size 100 #define USTAR_mode_offset 100 #define USTAR_mode_size 6 #define USTAR_mode_max_size 8 #define USTAR_uid_offset 108 #define USTAR_uid_size 6 #define USTAR_uid_max_size 8 #define USTAR_gid_offset 116 #define USTAR_gid_size 6 #define USTAR_gid_max_size 8 #define USTAR_size_offset 124 #define USTAR_size_size 11 #define USTAR_size_max_size 12 #define USTAR_mtime_offset 136 #define USTAR_mtime_size 11 #define USTAR_mtime_max_size 11 #define USTAR_checksum_offset 148 #define USTAR_checksum_size 8 #define USTAR_typeflag_offset 156 #define USTAR_typeflag_size 1 #define USTAR_linkname_offset 157 #define USTAR_linkname_size 100 #define USTAR_magic_offset 257 #define USTAR_magic_size 6 #define USTAR_version_offset 263 #define USTAR_version_size 2 #define USTAR_uname_offset 265 #define USTAR_uname_size 32 #define USTAR_gname_offset 297 #define USTAR_gname_size 32 #define USTAR_rdevmajor_offset 329 #define USTAR_rdevmajor_size 6 #define USTAR_rdevmajor_max_size 8 #define USTAR_rdevminor_offset 337 #define USTAR_rdevminor_size 6 #define USTAR_rdevminor_max_size 8 #define USTAR_prefix_offset 345 #define USTAR_prefix_size 155 #define USTAR_padding_offset 500 #define USTAR_padding_size 12 /* * 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, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* uid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* gid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* mtime, space termination: 12 bytes */ '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: 6 bytes, Version: 2 bytes */ 'u','s','t','a','r','\0', '0','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 + space/null padding: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* rdevminor + space/null padding: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* Prefix: 155 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, /* Padding: 12 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0 }; static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s); static int archive_write_ustar_destroy(struct archive_write *); static int archive_write_ustar_finish(struct archive_write *); static int archive_write_ustar_finish_entry(struct archive_write *); static int archive_write_ustar_header(struct archive_write *, struct archive_entry *entry); static int format_256(int64_t, char *, int); static int format_number(int64_t, char *, int size, int max, int strict); static int format_octal(int64_t, char *, int); static int write_nulls(struct archive_write *a, size_t); /* * Set output format to 'ustar' format. */ int archive_write_set_format_ustar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct ustar *ustar; /* If someone else was already registered, unregister them. */ if (a->format_destroy != NULL) (a->format_destroy)(a); /* Basic internal sanity test. */ if (sizeof(template_header) != 512) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header)); return (ARCHIVE_FATAL); } ustar = (struct ustar *)malloc(sizeof(*ustar)); if (ustar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return (ARCHIVE_FATAL); } memset(ustar, 0, sizeof(*ustar)); a->format_data = ustar; a->pad_uncompressed = 1; /* Mimic gtar in this respect. */ a->format_name = "ustar"; a->format_write_header = archive_write_ustar_header; a->format_write_data = archive_write_ustar_data; a->format_finish = archive_write_ustar_finish; a->format_destroy = archive_write_ustar_destroy; a->format_finish_entry = archive_write_ustar_finish_entry; a->format_skip_data = NULL; a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; a->archive.archive_format_name = "POSIX ustar"; return (ARCHIVE_OK); } static int archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int ret, ret2; struct ustar *ustar; ustar = (struct ustar *)a->format_data; /* 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; char *t; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ p = archive_entry_pathname(entry); if (p[strlen(p) - 1] != '/') { t = (char *)malloc(strlen(p) + 2); if (t == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } strcpy(t, p); strcat(t, "/"); archive_entry_copy_pathname(entry, t); free(t); } } ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1); if (ret < ARCHIVE_WARN) return (ret); ret2 = (a->compressor.write)(a, buff, 512); if (ret2 < ARCHIVE_WARN) return (ret2); if (ret2 < ret) ret = ret2; ustar->entry_bytes_remaining = archive_entry_size(entry); ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining); return (ret); } /* * Format a basic 512-byte "ustar" header. * * Returns -1 if format failed (due to field overflow). * Note that this always formats as much of the header as possible. * If "strict" is set to zero, it will extend numeric fields as * necessary (overwriting terminators or using base-256 extensions). * * This is exported so that other 'tar' formats can use it. */ int __archive_write_format_header_ustar(struct archive_write *a, char h[512], struct archive_entry *entry, int tartype, int strict) { unsigned int checksum; int i, ret; size_t copy_length; const char *p, *pp; int mytartype; ret = 0; mytartype = -1; /* * The "template header" already includes the "ustar" * 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. */ pp = archive_entry_pathname(entry); if (strlen(pp) <= USTAR_name_size) memcpy(h + USTAR_name_offset, pp, strlen(pp)); else { /* Store in two pieces, splitting at a '/'. */ p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/'); /* * Look for the next '/' if we chose the first character * as the separator. (ustar format doesn't permit * an empty prefix.) */ if (p == pp) p = strchr(p + 1, '/'); /* Fail if the name won't fit. */ if (!p) { /* No separator. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } else if (p[1] == '\0') { /* * The only feasible separator is a final '/'; * this would result in a non-empty prefix and * an empty name, which POSIX doesn't * explicitly forbid, but it just feels wrong. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } else if (p > pp + USTAR_prefix_size) { /* Prefix is too long. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } else { /* Copy prefix and remainder to appropriate places */ memcpy(h + USTAR_prefix_offset, pp, p - pp); memcpy(h + USTAR_name_offset, p + 1, pp + strlen(pp) - p - 1); } } p = archive_entry_hardlink(entry); if (p != NULL) mytartype = '1'; else p = archive_entry_symlink(entry); if (p != NULL && p[0] != '\0') { copy_length = strlen(p); if (copy_length > USTAR_linkname_size) { archive_set_error(&a->archive, ENAMETOOLONG, "Link contents too long"); ret = ARCHIVE_FAILED; copy_length = USTAR_linkname_size; } memcpy(h + USTAR_linkname_offset, p, copy_length); } p = archive_entry_uname(entry); if (p != NULL && p[0] != '\0') { copy_length = strlen(p); if (copy_length > USTAR_uname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Username too long"); ret = ARCHIVE_FAILED; copy_length = USTAR_uname_size; } memcpy(h + USTAR_uname_offset, p, copy_length); } p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { copy_length = strlen(p); if (strlen(p) > USTAR_gname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Group name too long"); ret = ARCHIVE_FAILED; copy_length = USTAR_gname_size; } memcpy(h + USTAR_gname_offset, p, copy_length); } if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); ret = ARCHIVE_FAILED; } if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset, USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } if (tartype >= 0) { h[USTAR_typeflag_offset] = tartype; } else if (mytartype >= 0) { h[USTAR_typeflag_offset] = mytartype; } else { switch (archive_entry_filetype(entry)) { case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break; case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break; case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break; case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break; case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break; case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break; 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; } } checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ /* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ format_octal(checksum, h + USTAR_checksum_offset, 6); return (ret); } /* * Format a number into a field, with some intelligence. */ static int format_number(int64_t v, char *p, int s, int maxsize, int strict) { int64_t limit; limit = ((int64_t)1 << (s*3)); /* "Strict" only permits octal values with proper termination. */ if (strict) return (format_octal(v, p, s)); /* * In non-strict mode, we allow the number to overwrite one or * more bytes of the field termination. Even old tar * implementations should be able to handle this with no * problem. */ if (v >= 0) { while (s <= maxsize) { if (v < limit) return (format_octal(v, p, s)); s++; limit <<= 3; } } /* Base-256 can handle any number, positive or negative. */ 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. */ static int format_octal(int64_t v, char *p, int s) { int len; len = s; /* Octal values can't be negative, so use 0. */ if (v < 0) { while (len-- > 0) *p++ = '0'; return (-1); } 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); } static int archive_write_ustar_finish(struct archive_write *a) { int r; if (a->compressor.write == NULL) return (ARCHIVE_OK); r = write_nulls(a, 512*2); return (r); } static int archive_write_ustar_destroy(struct archive_write *a) { struct ustar *ustar; ustar = (struct ustar *)a->format_data; free(ustar); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_ustar_finish_entry(struct archive_write *a) { struct ustar *ustar; int ret; ustar = (struct ustar *)a->format_data; ret = write_nulls(a, ustar->entry_bytes_remaining + ustar->entry_padding); ustar->entry_bytes_remaining = ustar->entry_padding = 0; return (ret); } static int write_nulls(struct archive_write *a, size_t padding) { int ret; size_t to_write; while (padding > 0) { to_write = padding < a->null_length ? padding : a->null_length; ret = (a->compressor.write)(a, a->nulls, to_write); if (ret != ARCHIVE_OK) return (ret); padding -= to_write; } return (ARCHIVE_OK); } static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s) { struct ustar *ustar; int ret; ustar = (struct ustar *)a->format_data; if (s > ustar->entry_bytes_remaining) s = ustar->entry_bytes_remaining; ret = (a->compressor.write)(a, buff, s); ustar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) return (ret); return (s); } tarsnap-1.0.41/libarchive/config_freebsd.h000066400000000000000000000104711476705112600205010ustar00rootroot00000000000000/*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.15 2008/09/30 03:53:03 kientzle Exp $ */ /* FreeBSD 5.0 and later have ACL and extattr support. */ #if __FreeBSD__ > 4 #define HAVE_ACL_CREATE_ENTRY 1 #define HAVE_ACL_GET_PERM_NP 1 #define HAVE_ACL_INIT 1 #define HAVE_ACL_SET_FD 1 #define HAVE_ACL_SET_FD_NP 1 #define HAVE_ACL_SET_FILE 1 #define HAVE_ACL_USER 1 #define HAVE_EXTATTR_GET_FILE 1 #define HAVE_EXTATTR_LIST_FILE 1 #define HAVE_EXTATTR_SET_FD 1 #define HAVE_EXTATTR_SET_FILE 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 #endif #define HAVE_BZLIB_H 1 #define HAVE_CHFLAGS 1 #define HAVE_CHOWN 1 #define HAVE_DECL_INT64_MAX 1 #define HAVE_DECL_INT64_MIN 1 #define HAVE_DECL_SIZE_MAX 1 #define HAVE_DECL_SSIZE_MAX 1 #define HAVE_DECL_STRERROR_R 1 #define HAVE_DECL_UINT32_MAX 1 #define HAVE_DECL_UINT64_MAX 1 #define HAVE_EFTYPE 1 #define HAVE_EILSEQ 1 #define HAVE_ERRNO_H 1 #define HAVE_FCHDIR 1 #define HAVE_FCHFLAGS 1 #define HAVE_FCHMOD 1 #define HAVE_FCHOWN 1 #define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 #define HAVE_FTRUNCATE 1 #define HAVE_FUTIMES 1 #define HAVE_GETEUID 1 #define HAVE_GETPID 1 #define HAVE_GRP_H 1 #define HAVE_INTTYPES_H 1 #define HAVE_LCHFLAGS 1 #define HAVE_LCHMOD 1 #define HAVE_LCHOWN 1 #define HAVE_LIMITS_H 1 #define HAVE_LUTIMES 1 #define HAVE_MALLOC 1 #define HAVE_MD5 1 #define HAVE_MD5_H 1 #define HAVE_MEMMOVE 1 #define HAVE_MEMSET 1 #define HAVE_MKDIR 1 #define HAVE_MKFIFO 1 #define HAVE_MKNOD 1 #define HAVE_OPENSSL_MD5_H 1 #define HAVE_OPENSSL_RIPEMD_H 1 #define HAVE_OPENSSL_SHA_H 1 #define HAVE_PIPE 1 #define HAVE_POLL 1 #define HAVE_POLL_H 1 #define HAVE_PWD_H 1 #define HAVE_READLINK 1 #define HAVE_RMD160 1 #define HAVE_SELECT 1 #define HAVE_SETENV 1 #define HAVE_SHA_H 1 #define HAVE_SHA1 1 #define HAVE_SHA256 1 #define HAVE_SHA256_H 1 #define HAVE_SHA384 1 #define HAVE_SHA512 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRCHR 1 #define HAVE_STRDUP 1 #define HAVE_STRERROR 1 #define HAVE_STRERROR_R 1 #define HAVE_STRINGS_H 1 #define HAVE_STRING_H 1 #define HAVE_STRRCHR 1 #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_FLAGS 1 #define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 #define HAVE_SYS_CDEFS_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TIME_H 1 #define HAVE_SYS_TYPES_H 1 #undef HAVE_SYS_UTIME_H #define HAVE_SYS_WAIT_H 1 #define HAVE_TIMEGM 1 #define HAVE_TZSET 1 #define HAVE_UNISTD_H 1 #define HAVE_UNSETENV 1 #define HAVE_UTIME 1 #define HAVE_UTIMES 1 #define HAVE_UTIME_H 1 #define HAVE_VFORK 1 #define HAVE_WCHAR_H 1 #define HAVE_WCSCPY 1 #define HAVE_WCSLEN 1 #define HAVE_WCTOMB 1 #define HAVE_WMEMCMP 1 #define HAVE_WMEMCPY 1 #define HAVE_ZLIB_H 1 #define STDC_HEADERS 1 #define TIME_WITH_SYS_TIME 1 /* FreeBSD 4 and earlier lack intmax_t/uintmax_t */ #if __FreeBSD__ < 5 #define intmax_t int64_t #define uintmax_t uint64_t #endif tarsnap-1.0.41/libarchive/config_windows.h000066400000000000000000000400541476705112600205610ustar00rootroot00000000000000/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED /////////////////////////////////////////////////////////////////////////// // Check for Watcom and Microsoft Visual C compilers (WIN32 only) /////// /////////////////////////////////////////////////////////////////////////// #if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) #define IS_WIN32 1 #if defined(__TURBOC__) || defined(__BORLANDC__) /* Borland compilers */ #elif defined( __WATCOMC__ ) || defined(__WATCOMCPP__) /* Watcom compilers */ #define IS_WATCOM 1 /* Define to 1 if __INT64 is defined */ #elif defined(__IBMC__) || defined(__IBMCPP__) /* IBM compilers */ #elif defined( __SC__ ) /* Symantec C++ compilers */ #elif defined( M_I86 ) && defined( MSDOS ) /* Microsoft DOS/Win 16 compilers */ #elif defined( _M_IX86 ) || defined( _68K_ ) /* Microsoft Win32 compilers */ #define IS_VISUALC 1 /* Define to 1 if __INT64 is defined */ #else #endif /* Define to 1 if UID should be unsigned */ #define USE_UNSIGNED_UID 1 /* Define to 1 if UID should be unsigned */ #define USE_UNSIGNED_GID 1 #endif /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /* Define to 1 if you have the `acl_create_entry' function. */ /* #undef HAVE_ACL_CREATE_ENTRY */ /* Define to 1 if you have the `acl_get_perm' function. */ /* #undef HAVE_ACL_GET_PERM */ /* Define to 1 if you have the `acl_get_perm_np' function. */ /* #undef HAVE_ACL_GET_PERM_NP */ /* Define to 1 if you have the `acl_init' function. */ /* #undef HAVE_ACL_INIT */ /* Define to 1 if the system has the type `acl_permset_t'. */ /* #undef HAVE_ACL_PERMSET_T */ /* Define to 1 if you have the `acl_set_fd' function. */ /* #undef HAVE_ACL_SET_FD */ /* Define to 1 if you have the `acl_set_fd_np' function. */ /* #undef HAVE_ACL_SET_FD_NP */ /* Define to 1 if you have the `acl_set_file' function. */ /* #undef HAVE_ACL_SET_FILE */ /* True for systems with POSIX ACL support */ /* #undef HAVE_ACL_USER */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ATTR_XATTR_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_BZLIB_H */ /* Define to 1 if you have the `chflags' function. */ /* #undef HAVE_CHFLAGS */ /* Define to 1 if you have the `chown' function. */ /* #undef HAVE_CHOWN */ /* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you don't. */ #if defined(_MSC_VER) /* #undef HAVE_DECL_INT64_MAX */ #else #define HAVE_DECL_INT64_MAX 1 #endif /* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you don't. */ #if defined(_MSC_VER) /* #undef HAVE_DECL_INT64_MIN */ #else #define HAVE_DECL_INT64_MIN 1 #endif /* Define to 1 if you have the declaration of `optarg', and to 0 if you don't. */ /* #undef HAVE_DECL_OPTARG */ /* Define to 1 if you have the declaration of `optind', and to 0 if you don't. */ /* #undef HAVE_DECL_OPTIND */ /* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you don't. */ #if defined(_MSC_VER) #if _MSC_VER >= 1400 #define HAVE_DECL_SIZE_MAX 1 #else /* #undef HAVE_DECL_SIZE_MAX */ #endif #else #define HAVE_DECL_SIZE_MAX 1 #endif /* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you don't. */ /* #undef HAVE_DECL_SSIZE_MAX */ /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ /* #undef HAVE_DECL_STRERROR_R */ /* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you don't. */ #if defined(_MSC_VER) /* #undef HAVE_DECL_UINT32_MAX */ #else #define HAVE_DECL_UINT32_MAX 1 #endif /* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you don't. */ #if defined(_MSC_VER) /* #undef HAVE_DECL_UINT64_MAX */ #else #define HAVE_DECL_UINT64_MAX 1 #endif /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_DIRENT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_DLFCN_H */ /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ /* #undef HAVE_DOPRNT */ /* Define to 1 if nl_langinfo supports D_MD_ORDER */ /* #undef HAVE_D_MD_ORDER */ /* A possible errno value for invalid file format errors */ /* #undef HAVE_EFTYPE */ /* A possible errno value for invalid file format errors */ #define HAVE_EILSEQ 1 /* Define to 1 if you have the header file. */ #define HAVE_ERRNO_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_EXT2FS_EXT2_FS_H */ /* Define to 1 if you have the `fchdir' function. */ /* #undef HAVE_FCHDIR */ /* Define to 1 if you have the `fchflags' function. */ /* #undef HAVE_FCHFLAGS */ /* Define to 1 if you have the `fchmod' function. */ /* #undef HAVE_FCHMOD */ /* Define to 1 if you have the `fchown' function. */ /* #undef HAVE_FCHOWN */ /* Define to 1 if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define to 1 if you have the fcntl() function. */ /* #undef HAVE_FCNTL_FN */ /* Define to 1 if your system has a working POSIX `fnmatch' function. */ /* #undef HAVE_FNMATCH */ /* Define to 1 if fnmatch(3) supports the FNM_LEADING_DIR flag */ /* #undef HAVE_FNM_LEADING_DIR */ /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ /* #undef HAVE_FSEEKO */ /* Define to 1 if you have the `fsetxattr' function. */ /* #undef HAVE_FSETXATTR */ /* Define to 1 if you have the `ftruncate' function. */ #define HAVE_FTRUNCATE 1 /* Define to 1 if you have the `futimes' function. */ #define HAVE_FUTIMES 1 /* Define to 1 if you have the `geteuid' function. */ /* #undef HAVE_GETEUID */ /* Define to 1 if you have the `getxattr' function. */ /* #undef HAVE_GETXATTR */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GRP_H */ /* Define to 1 if the system has the type `intmax_t'. */ /* #undef HAVE_INTMAX_T */ /* Define to 1 if you have the header file. */ /* #undef HAVE_INTTYPES_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LANGINFO_H */ /* Define to 1 if you have the `lchflags' function. */ /* #undef HAVE_LCHFLAGS */ /* Define to 1 if you have the `lchmod' function. */ /* #undef HAVE_LCHMOD */ /* Define to 1 if you have the `lchown' function. */ /* #undef HAVE_LCHOWN */ /* Define to 1 if you have the `lgetxattr' function. */ /* #undef HAVE_LGETXATTR */ /* Define to 1 if you have the `acl' library (-lacl). */ /* #undef HAVE_LIBACL */ /* Define to 1 if you have the `attr' library (-lattr). */ /* #undef HAVE_LIBATTR */ /* Define to 1 if you have the `bz2' library (-lbz2). */ /* #undef HAVE_LIBBZ2 */ /* Define to 1 if you have the `z' library (-lz). */ /* #undef HAVE_LIBZ */ /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_EXT2_FS_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_FS_H */ /* Define to 1 if you have the `listxattr' function. */ /* #undef HAVE_LISTXATTR */ /* Define to 1 if you have the `llistxattr' function. */ /* #undef HAVE_LLISTXATTR */ /* Define to 1 if you have the header file. */ #define HAVE_LOCALE_H 1 /* Define to 1 if the system has the type `long long int'. */ #define HAVE_LONG_LONG_INT 1 /* Define to 1 if you have the `lsetxattr' function. */ /* #undef HAVE_LSETXATTR */ /* Define to 1 if `lstat' has the bug that it succeeds when given the zero-length file name argument. */ /* #undef HAVE_LSTAT_EMPTY_STRING_BUG */ /* Define to 1 if you have the `lutimes' function. */ /* #undef HAVE_LUTIMES */ /* Define to 1 if you have the `memmove' function. */ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `memset' function. */ #define HAVE_MEMSET 1 /* Define to 1 if you have the `mkdir' function. */ #define HAVE_MKDIR 1 /* Define to 1 if you have the `mkfifo' function. */ /* #undef HAVE_MKFIFO */ /* Define to 1 if you have the `mknod' function. */ /* #undef HAVE_MKNOD */ /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_NDIR_H */ /* Define to 1 if you have the `nl_langinfo' function. */ /* #undef HAVE_NL_LANGINFO */ /* Define to 1 if you have the header file. */ /* #undef HAVE_PATHS_H */ /* Define to 1 if you have the `poll' function. */ /* #undef HAVE_POLL */ /* Define to 1 if you have the header file. */ /* #undef HAVE_POLL_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_PWD_H */ /* Define to 1 if you have the `select' function. */ /* #undef HAVE_SELECT */ /* Define to 1 if you have the `setlocale' function. */ #define HAVE_SETLOCALE 1 /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ /* #undef HAVE_STAT_EMPTY_STRING_BUG */ /* Define to 1 if you have the header file. */ #define HAVE_STDARG_H 1 /* Define to 1 if you have the header file. */ #if defined(_MSC_VER) /* #undef HAVE_STDINT_H */ #else #define HAVE_STDINT_H 1 #endif /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strchr' function. */ #define HAVE_STRCHR 1 /* Define to 1 if you have the `strdup' function. */ #define HAVE_STRDUP 1 /* Define to 1 if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define to 1 if you have the `strerror_r' function. */ /* #undef HAVE_STRERROR_R */ /* Define to 1 if you have the `strftime' function. */ #define HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strrchr' function. */ #define HAVE_STRRCHR 1 /* Define to 1 if `st_mtimespec.tv_nsec' is member of `struct stat'. */ /* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC */ /* Define to 1 if `st_mtim.tv_nsec' is member of `struct stat'. */ /* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_ACL_H */ /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_SYS_DIR_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_IOCTL_H */ /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_SYS_NDIR_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PARAM_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_POLL_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_SELECT_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #if defined(_MSC_VER) /* #undef HAVE_SYS_TIME_H */ #else #define HAVE_SYS_TIME_H 1 #endif /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_UTIME_H 1 /* Define to 1 if you have that is POSIX.1 compatible. */ /* #undef HAVE_SYS_WAIT_H */ /* Define to 1 if you have the `timegm' function. */ /* #undef HAVE_TIMEGM */ /* Define to 1 if you have the header file. */ #define HAVE_TIME_H 1 /* Define to 1 if the system has the type `uintmax_t'. */ #if defined(_MSC_VER) /* #undef HAVE_UINTMAX_T */ #else #define HAVE_UINTMAX_T 1 #endif /* Define to 1 if you have the header file. */ #if defined(_MSC_VER) /* #undef HAVE_UNISTD_H */ #else #define HAVE_UNISTD_H 1 #endif /* Define to 1 if the system has the type `unsigned long long'. */ #define HAVE_UNSIGNED_LONG_LONG 1 /* Define to 1 if the system has the type `unsigned long long int'. */ #define HAVE_UNSIGNED_LONG_LONG_INT 1 /* Define to 1 if you have the `utime' function. */ #define HAVE_UTIME 1 /* Define to 1 if you have the `utimes' function. */ #define HAVE_UTIMES 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UTIME_H */ /* Define to 1 if you have the `vprintf' function. */ #define HAVE_VPRINTF 1 /* Define to 1 if you have the header file. */ #define HAVE_WCHAR_H 1 /* Define to 1 if you have the `wcscpy' function. */ #define HAVE_WCSCPY 1 /* Define to 1 if you have the `wcslen' function. */ #define HAVE_WCSLEN 1 /* Define to 1 if you have the `wctomb' function. */ #define HAVE_WCTOMB 1 /* Define to 1 if you have the `wmemcmp' function. */ /* #undef HAVE_WMEMCMP */ /* Define to 1 if you have the `wmemcpy' function. */ /* #undef HAVE_WMEMCPY */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ZLIB_H */ /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ /* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ /* Define to 1 if `major', `minor', and `makedev' are declared in . */ /* #undef MAJOR_IN_MKDEV */ /* Define to 1 if `major', `minor', and `makedev' are declared in . */ /* #undef MAJOR_IN_SYSMACROS */ /* Define to 1 if your C compiler doesn't accept -c and -o together. */ /* #undef NO_MINUS_C_MINUS_O */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to 1 if strerror_r returns char *. */ /* #undef STRERROR_R_CHAR_P */ /* Define to 1 if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Number of bits in a file offset, on hosts where this is settable. */ /* #undef _FILE_OFFSET_BITS */ /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ /* #undef _LARGEFILE_SOURCE */ /* Define for large files, on AIX-style hosts. */ /* #undef _LARGE_FILES */ /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef was allowed, the #define below would cause a syntax error. */ /* #undef _UINT64_T */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define to `int' if doesn't define. */ #if (USE_UNSIGNED_GID) #define gid_t unsigned int #else #define gid_t int #endif /* Define to `unsigned long' if does not define. */ #define id_t int /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #if defined(_MSC_VER) #define int64_t __int64 #else /* #undef int64_t */ #endif /* Define to the widest signed integer type if and do not define. */ #if defined(_MSC_VER) #define intmax_t long long #else /* #undef intmax_t */ #endif /* Define to `int' if does not define. */ #if defined(_MSC_VER) #define mode_t unsigned short #else /* #undef mode_t */ #endif /* Define to `long long' if does not define. */ /* #undef off_t */ /* Define to `unsigned int' if does not define. */ /* #undef size_t */ /* Define to `int' if doesn't define. */ #if (USE_UNSIGNED_UID) #define uid_t unsigned int #else #define uid_t int #endif /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #if defined(_MSC_VER) #define uint64_t unsigned __int64 #else /* #undef uint64_t */ #endif /* Define to the widest unsigned integer type if and do not define. */ #if defined(_MSC_VER) #define uintmax_t unsigned long long #else /* #undef uintmax_t */ #endif /* Define to `unsigned int' if does not define. */ /* #undef uintptr_t */ /* Define to `unsigned int' if does not define. */ #if defined(_MSC_VER) #define pid_t unsigned int #else /* #undef pid_t */ #endif #if defined(_MSC_VER) #define uint32_t unsigned long #define uint16_t unsigned short #ifdef _WIN64 #define ssize_t __int64 #else #define ssize_t long #endif #endif #include "archive_windows.h" #endif /* !CONFIG_H_INCLUDED */ tarsnap-1.0.41/libarchive/filter_fork.c000066400000000000000000000102221476705112600200350ustar00rootroot00000000000000/*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" /* This capability is only available on POSIX systems. */ #if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \ (defined(HAVE_FORK) || defined(HAVE_VFORK)) __FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.5 2008/09/12 05:33:00 kientzle Exp $"); #if defined(HAVE_POLL) # if defined(HAVE_POLL_H) # include # elif defined(HAVE_SYS_POLL_H) # include # endif #elif defined(HAVE_SELECT) # if defined(HAVE_SYS_SELECT_H) # include # elif defined(HAVE_UNISTD_H) # include # endif #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "filter_fork.h" pid_t __archive_create_child(const char *path, int *child_stdin, int *child_stdout) { pid_t child; int stdin_pipe[2], stdout_pipe[2], tmp; if (pipe(stdin_pipe) == -1) goto state_allocated; if (stdin_pipe[0] == 1 /* stdout */) { if ((tmp = dup(stdin_pipe[0])) == -1) goto stdin_opened; close(stdin_pipe[0]); stdin_pipe[0] = tmp; } if (pipe(stdout_pipe) == -1) goto stdin_opened; if (stdout_pipe[1] == 0 /* stdin */) { if ((tmp = dup(stdout_pipe[1])) == -1) goto stdout_opened; close(stdout_pipe[1]); stdout_pipe[1] = tmp; } #if HAVE_VFORK switch ((child = vfork())) { #else switch ((child = fork())) { #endif case -1: goto stdout_opened; case 0: close(stdin_pipe[1]); close(stdout_pipe[0]); if (dup2(stdin_pipe[0], 0 /* stdin */) == -1) _exit(254); if (stdin_pipe[0] != 0 /* stdin */) close(stdin_pipe[0]); if (dup2(stdout_pipe[1], 1 /* stdout */) == -1) _exit(254); if (stdout_pipe[1] != 1 /* stdout */) close(stdout_pipe[1]); execlp(path, path, (char *)NULL); _exit(254); default: close(stdin_pipe[0]); close(stdout_pipe[1]); *child_stdin = stdin_pipe[1]; fcntl(*child_stdin, F_SETFL, O_NONBLOCK); *child_stdout = stdout_pipe[0]; fcntl(*child_stdout, F_SETFL, O_NONBLOCK); } return child; stdout_opened: close(stdout_pipe[0]); close(stdout_pipe[1]); stdin_opened: close(stdin_pipe[0]); close(stdin_pipe[1]); state_allocated: return -1; } void __archive_check_child(int in, int out) { #if defined(HAVE_POLL) struct pollfd fds[2]; int idx; idx = 0; if (in != -1) { fds[idx].fd = in; fds[idx].events = POLLOUT; ++idx; } if (out != -1) { fds[idx].fd = out; fds[idx].events = POLLIN; ++idx; } poll(fds, idx, -1); /* -1 == INFTIM, wait forever */ #elif defined(HAVE_SELECT) fd_set fds_in, fds_out, fds_error; FD_ZERO(&fds_in); FD_ZERO(&fds_out); FD_ZERO(&fds_error); if (out != -1) { FD_SET(out, &fds_in); FD_SET(out, &fds_error); } if (in != -1) { FD_SET(in, &fds_out); FD_SET(in, &fds_error); } select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL); #else sleep(1); #endif } #endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */ tarsnap-1.0.41/libarchive/filter_fork.h000066400000000000000000000031041476705112600200430ustar00rootroot00000000000000/*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/lib/libarchive/filter_fork.h,v 1.1 2007/05/29 01:00:20 kientzle Exp $ */ #ifndef FILTER_FORK_H #define FILTER_FORK_H pid_t __archive_create_child(const char *path, int *child_stdin, int *child_stdout); void __archive_check_child(int in, int out); #endif tarsnap-1.0.41/libarchive/filter_fork_windows.c000066400000000000000000000071661476705112600216240ustar00rootroot00000000000000/*- * Copyright (c) 2009 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #if defined(_WIN32) && !defined(__CYGWIN__) #include "filter_fork.h" pid_t __archive_create_child(const char *path, int *child_stdin, int *child_stdout) { HANDLE childStdout[2], childStdin[2], childStdinWr, childStdoutRd; SECURITY_ATTRIBUTES secAtts; STARTUPINFO staInfo; PROCESS_INFORMATION childInfo; char cmd[MAX_PATH]; DWORD mode; secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); secAtts.bInheritHandle = TRUE; secAtts.lpSecurityDescriptor = NULL; if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0) goto fail; if (DuplicateHandle(GetCurrentProcess(), childStdout[0], GetCurrentProcess(), &childStdoutRd, 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) { CloseHandle(childStdout[0]); CloseHandle(childStdout[1]); goto fail; } CloseHandle(childStdout[0]); if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0) { CloseHandle(childStdoutRd); CloseHandle(childStdout[1]); goto fail; } if (DuplicateHandle(GetCurrentProcess(), childStdin[1], GetCurrentProcess(), &childStdinWr, 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) { CloseHandle(childStdoutRd); CloseHandle(childStdout[1]); CloseHandle(childStdin[0]); CloseHandle(childStdin[1]); goto fail; } CloseHandle(childStdin[1]); memset(&staInfo, 0, sizeof(staInfo)); staInfo.cb = sizeof(staInfo); staInfo.hStdOutput = childStdout[1]; staInfo.hStdInput = childStdin[0]; staInfo.wShowWindow = SW_HIDE; staInfo.dwFlags = STARTF_USEFILLATTRIBUTE | STARTF_USECOUNTCHARS | STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; strncpy(cmd, path, sizeof(cmd)-1); cmd[sizeof(cmd)-1] = '\0'; if (CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &staInfo, &childInfo) == 0) { CloseHandle(childStdoutRd); CloseHandle(childStdout[1]); CloseHandle(childStdin[0]); CloseHandle(childStdinWr); goto fail; } WaitForInputIdle(childInfo.hProcess, INFINITE); CloseHandle(childInfo.hProcess); CloseHandle(childInfo.hThread); mode = PIPE_NOWAIT; SetNamedPipeHandleState(childStdoutRd, &mode, NULL, NULL); *child_stdout = _open_osfhandle((intptr_t)childStdoutRd, _O_RDONLY); *child_stdin = _open_osfhandle((intptr_t)childStdinWr, _O_WRONLY); return (childInfo.dwProcessId); fail: return (-1); } void __archive_check_child(int in, int out) { Sleep(100); } #endif /* _WIN32 && !__CYGWIN__ */ tarsnap-1.0.41/libcperciva/000077500000000000000000000000001476705112600155415ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/COPYRIGHT000066400000000000000000000031041476705112600170320ustar00rootroot00000000000000The code in the "libcperciva/external" directory is distributed under the terms specified in each file. The code and documentation in this directory ("libcperciva") is distributed under the following terms: Copyright 2011-2025 Tarsnap Backup Inc. All rights reserved. Copyright 2005-2025 Colin Percival. All rights reserved. Copyright 2014 Sean Kelly. 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. tarsnap-1.0.41/libcperciva/POSIX/000077500000000000000000000000001476705112600164435ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/POSIX/README000066400000000000000000000037421476705112600173310ustar00rootroot00000000000000POSIX compatibility code ------------------------ This code exists to work around some common POSIX compatibility issues. POSIX specifies that if the first line of a Makefile is ".POSIX:" then the Makefile should be processed according to POSIX rules, including with CC=c99; thus we should see behaviour consistent with a standard-compliant C99 compiler. One POSIX compatibility check requires a runtime test. This will be automatically disabled if the build system detects that you are cross-compiling for another platform, or it can be manually disabled by setting DISABLE_POSIX_RUNTIME_CHECKS to a non-zero value. - posix-cflags.sh: Detects if ${CC} supports certain POSIX features, and outputs a POSIXFAIL_ define if it is not supported so that we can work around the problem. The potential command-line flags are: - DPOSIXFAIL_MSG_NOSIGNAL: not defining MSG_NOSIGNAL. - DPOSIXFAIL_CLOCK_REALTIME: not defining CLOCK_REALTIME. - DPOSIXFAIL_CLOCK_GETTIME: not declaring clock_gettime(), or clock_gettime() is not linkable. The latter test requires a runtime check. - DPOSIXFAIL_INET_ADDRSTRLEN: not defining INET_ADDRSTRLEN. - DPOSIXFAIL_INET6_ADDRSTRLEN: not defining INET6_ADDRSTRLEN. - DPOSIXFAIL_ABSTRACT_DECLARATOR: ${CC} does not accept qualifiers in an abstract declarator. - DPOSIXFAIL_STAT_ST_MTIM: struct stat does not contain st_mtim. - std=c99: ${CC} does not accept the `restrict` keyword by default, but accepts it when given this flag. - posix-cflags-filter.sh: Detects if ${CC} supports expected ${CFLAG} value(s). The potential ${CFLAGS} flags it checks are: - O2: some compilers only accept -O. - posix-l.sh: Detects whether the linker supports certain POSIX features. The potential command-line flags are: - lrt lxnet: c99 is required to understand these options, and ignore them if the routines for which they specify linkage are already in the standard C library tarsnap-1.0.41/libcperciva/POSIX/posix-abstract-declarator.c000066400000000000000000000004741476705112600236750ustar00rootroot00000000000000#ifdef POSIXFAIL_ABSTRACT_DECLARATOR static int func(int ARGNAME[static restrict 1]); #else static int func(int [static restrict 1]); #endif int func(int arr[static restrict 1]) { (void)arr; /* UNUSED */ /* Success! */ return (0); } int main(void) { (void)func; /* UNUSED */ /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/POSIX/posix-cflags.sh000066400000000000000000000103341476705112600213770ustar00rootroot00000000000000# Should be sourced by # command -p sh posix-cflags.sh "$PATH" # from within a Makefile. # Sanity check environment variables if [ -z "${CC}" ]; then echo "\$CC is not defined! Cannot run any compiler tests." 1>&2 exit 1 fi if ! [ "${PATH}" = "$1" ]; then echo "WARNING: POSIX violation: ${SHELL}'s command -p resets \$PATH" 1>&2 PATH=$1 fi # Find directory of this script and the source files D=$(dirname "$0") # Check if we can compile & run a binary. if ! ${CC} ${CFLAGS} "${D}/posix-trivial.c" 2>/dev/null; then echo "WARNING: failed to compile posix-trivial.c" 1>&2 else # If the user hasn't disabled runtime checks... if [ "${DISABLE_POSIX_RUNTIME_CHECKS:-0}" -eq "0" ]; then # ... test if we can run the trivial binary. if ! ./a.out ; then echo "WARNING: failed to run a trivial binary; " 1>&2 echo "disabling runtime POSIX compatibility checks" 1>&2 DISABLE_POSIX_RUNTIME_CHECKS=1 fi fi fi FIRST=YES if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L "${D}/posix-msg_nosignal.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_MSG_NOSIGNAL" echo "WARNING: POSIX violation: not defining MSG_NOSIGNAL" 1>&2 fi if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L "${D}/posix-clock_realtime.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_CLOCK_REALTIME" echo "WARNING: POSIX violation: not defining CLOCK_REALTIME" 1>&2 fi if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 "${D}/posix-inet-addrstrlen.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_INET_ADDRSTRLEN" echo "WARNING: POSIX violation: not defining INET_ADDRSTRLEN" 1>&2 fi if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 "${D}/posix-inet6-addrstrlen.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_INET6_ADDRSTRLEN" echo "WARNING: POSIX violation: not defining INET6_ADDRSTRLEN" 1>&2 fi if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L "${D}/posix-clock_gettime.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_CLOCK_GETTIME" echo "WARNING: POSIX violation: not declaring clock_gettime()" 1>&2 elif [ "${DISABLE_POSIX_RUNTIME_CHECKS:-0}" -ne "0" ]; then # Do nothing true else # Even if the compilation succeeds, we still need to run the binary # because OS X 10.11 with XCode 8 _will_ contain clock_gettime() in the # header (because it was added in 10.12 and they only use one SDK per # XCode version), but it will link to the 10.11 library (which doesn't # include it). Annoyingly, there's two levels of error output on OS X: # one from the binary itself, and one from the signal it sends to the # calling process. The "( ./x 2>y ) 2>y" captures both types of error # message. if ! ( ./a.out 2>/dev/null ) 2>/dev/null ; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_CLOCK_GETTIME" echo "WARNING: POSIX violation: clock_gettime() is not linkable" 1>&2 fi fi if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 "${D}/posix-stat-st_mtim.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_STAT_ST_MTIM" echo "WARNING: POSIX violation: struct stat does not contain st_mtim" 1>&2 fi CFLAGS_C99="" if ! ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L "${D}/posix-restrict.c" 2>/dev/null; then echo "WARNING: POSIX violation: ${CC} does not accept the 'restrict' keyword" 1>&2 if ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L -std=c99 "${D}/posix-restrict.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-std=c99" CFLAGS_C99="-std=c99" fi fi if ! ${CC} ${CFLAGS} ${CFLAGS_C99} -D_POSIX_C_SOURCE=200809L -DARGNAME="" "${D}/posix-abstract-declarator.c" 2>/dev/null; then echo "WARNING: POSIX violation: ${CC} does not accept qualifiers in an abstract declarator" 1>&2 # Test compile with -DPOSIXFAIL_ABSTRACT_DECLARATOR if ${CC} ${CFLAGS} ${CFLAGS_C99} -D_POSIX_C_SOURCE=200809L -DPOSIXFAIL_ABSTRACT_DECLARATOR "${D}/posix-abstract-declarator.c" 2>/dev/null; then [ "${FIRST}" = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_ABSTRACT_DECLARATOR" fi fi rm -f a.out tarsnap-1.0.41/libcperciva/POSIX/posix-clock_gettime.c000066400000000000000000000001521476705112600225560ustar00rootroot00000000000000#include int main(void) { struct timespec ts; return (clock_gettime(CLOCK_REALTIME, &ts)); } tarsnap-1.0.41/libcperciva/POSIX/posix-clock_realtime.c000066400000000000000000000001011476705112600227140ustar00rootroot00000000000000#include int main(void) { return (CLOCK_REALTIME); } tarsnap-1.0.41/libcperciva/POSIX/posix-inet-addrstrlen.c000066400000000000000000000001101476705112600230360ustar00rootroot00000000000000#include int main(void) { return (INET_ADDRSTRLEN); } tarsnap-1.0.41/libcperciva/POSIX/posix-inet6-addrstrlen.c000066400000000000000000000001111476705112600231250ustar00rootroot00000000000000#include int main(void) { return (INET6_ADDRSTRLEN); } tarsnap-1.0.41/libcperciva/POSIX/posix-l.sh000066400000000000000000000013161476705112600203730ustar00rootroot00000000000000# Should be sourced by # command -p sh posix-l.sh "$PATH" # from within a Makefile. # Sanity check environment variables if [ -z "${CC}" ]; then echo "\$CC is not defined! Cannot run any compiler tests." 1>&2 exit 1 fi if ! [ "${PATH}" = "$1" ]; then echo "WARNING: POSIX violation: ${SHELL}'s command -p resets \$PATH" 1>&2 PATH=$1 fi # Find directory of this script and the source files D=$(dirname "$0") FIRST=YES for LIB in rt xnet; do if ${CC} ${CFLAGS} -l"${LIB}" "${D}/posix-trivial.c" 2>/dev/null; then if [ "${FIRST}" = "NO" ]; then printf " "; fi printf "%s" "-l${LIB}"; FIRST=NO; else echo "WARNING: POSIX violation: ${CC} does not understand -l${LIB}" 1>&2 fi rm -f a.out done tarsnap-1.0.41/libcperciva/POSIX/posix-msg_nosignal.c000066400000000000000000000001051476705112600224230ustar00rootroot00000000000000#include int main(void) { return (MSG_NOSIGNAL); } tarsnap-1.0.41/libcperciva/POSIX/posix-restrict.c000066400000000000000000000002341476705112600216050ustar00rootroot00000000000000static int foo(const char * restrict x, const char * restrict y) { return (x == y); } int main(void) { char x[10]; char y[10]; return (foo(x, y)); } tarsnap-1.0.41/libcperciva/POSIX/posix-stat-st_mtim.c000066400000000000000000000002251476705112600223730ustar00rootroot00000000000000#include int main(void) { struct stat sb; /* Can we reference st_mtim? */ (void)sb.st_mtim.tv_sec; /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/POSIX/posix-trivial.c000066400000000000000000000000611476705112600214160ustar00rootroot00000000000000int main(void) { /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/alg/000077500000000000000000000000001476705112600163045ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/alg/sha256.c000066400000000000000000000444071476705112600174710ustar00rootroot00000000000000#include #include #include #include "cpusupport.h" #include "insecure_memzero.h" #include "sha256_arm.h" #include "sha256_shani.h" #include "sha256_sse2.h" #include "sysendian.h" #include "warnp.h" #include "sha256.h" #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) || \ defined(CPUSUPPORT_X86_SSE2) || \ defined(CPUSUPPORT_ARM_SHA256) #define HWACCEL static enum { HW_SOFTWARE = 0, #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) HW_X86_SHANI, #endif #if defined(CPUSUPPORT_X86_SSE2) HW_X86_SSE2, #endif #if defined(CPUSUPPORT_ARM_SHA256) HW_ARM_SHA256, #endif HW_UNSET } hwaccel = HW_UNSET; #endif #ifdef POSIXFAIL_ABSTRACT_DECLARATOR static void SHA256_Transform(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]); #else static void SHA256_Transform(uint32_t[static restrict 8], const uint8_t[static restrict 64], uint32_t[static restrict 64], uint32_t[static restrict 8]); #endif /* * Encode a length len/4 vector of (uint32_t) into a length len vector of * (uint8_t) in big-endian form. Assumes len is a multiple of 4. */ static void be32enc_vect(uint8_t * dst, const uint32_t * src, size_t len) { size_t i; /* Sanity-check. */ assert(len % 4 == 0); /* Encode vector, one word at a time. */ for (i = 0; i < len / 4; i++) be32enc(dst + i * 4, src[i]); } /* * Decode a big-endian length len vector of (uint8_t) into a length * len/4 vector of (uint32_t). Assumes len is a multiple of 4. */ static void be32dec_vect(uint32_t * dst, const uint8_t * src, size_t len) { size_t i; /* Sanity-check. */ assert(len % 4 == 0); /* Decode vector, one word at a time. */ for (i = 0; i < len / 4; i++) dst[i] = be32dec(src + i * 4); } /* SHA256 round constants. */ static const uint32_t Krnd[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; /* Magic initialization constants. */ static const uint32_t initial_state[8] = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; #ifdef HWACCEL #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) /* Shim so that we can test SHA256_Transform_shani() in the standard manner. */ static void SHA256_Transform_shani_with_W_S(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]) { (void)W; /* UNUSED */ (void)S; /* UNUSED */ SHA256_Transform_shani(state, block); } #endif #if defined(CPUSUPPORT_ARM_SHA256) /* Shim so that we can test SHA256_Transform_arm() in the standard manner. */ static void SHA256_Transform_arm_with_W_S(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]) { (void)W; /* UNUSED */ (void)S; /* UNUSED */ SHA256_Transform_arm(state, block); } #endif /* * Test whether software and hardware extensions transform code produce the * same results. Must be called with (hwaccel == HW_SOFTWARE). */ static int hwtest(const uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8], void (* func)(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8])) { uint32_t state_sw[8]; uint32_t state_hw[8]; /* Software transform. */ memcpy(state_sw, state, sizeof(state_sw)); SHA256_Transform(state_sw, block, W, S); /* Hardware transform. */ memcpy(state_hw, state, sizeof(state_hw)); func(state_hw, block, W, S); /* Do the results match? */ return (memcmp(state_sw, state_hw, sizeof(state_sw))); } /* Which type of hardware acceleration should we use, if any? */ static void hwaccel_init(void) { uint32_t W[64]; uint32_t S[8]; uint8_t block[64]; uint8_t i; /* If we've already set hwaccel, we're finished. */ if (hwaccel != HW_UNSET) return; /* Default to software. */ hwaccel = HW_SOFTWARE; /* Test case: Hash 0x00 0x01 0x02 ... 0x3f. */ for (i = 0; i < 64; i++) block[i] = i; #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) CPUSUPPORT_VALIDATE(hwaccel, HW_X86_SHANI, cpusupport_x86_shani() && cpusupport_x86_ssse3(), hwtest(initial_state, block, W, S, SHA256_Transform_shani_with_W_S)); #endif #if defined(CPUSUPPORT_X86_SSE2) CPUSUPPORT_VALIDATE(hwaccel, HW_X86_SSE2, cpusupport_x86_sse2(), hwtest(initial_state, block, W, S, SHA256_Transform_sse2)); #endif #if defined(CPUSUPPORT_ARM_SHA256) CPUSUPPORT_VALIDATE(hwaccel, HW_ARM_SHA256, cpusupport_arm_sha256(), hwtest(initial_state, block, W, S, SHA256_Transform_arm_with_W_S)); #endif } #endif /* HWACCEL */ /* Elementary functions used by SHA256 */ #define Ch(x, y, z) ((x & (y ^ z)) ^ z) #define Maj(x, y, z) ((x & (y | z)) | (y & z)) #define SHR(x, n) (x >> n) #define ROTR(x, n) ((x >> n) | (x << (32 - n))) #define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) #define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) #define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) #define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) /* SHA256 round function */ #define RND(a, b, c, d, e, f, g, h, k) \ h += S1(e) + Ch(e, f, g) + k; \ d += h; \ h += S0(a) + Maj(a, b, c) /* Adjusted round function for rotating state */ #define RNDr(S, W, i, ii) \ RND(S[(64 - i) % 8], S[(65 - i) % 8], \ S[(66 - i) % 8], S[(67 - i) % 8], \ S[(68 - i) % 8], S[(69 - i) % 8], \ S[(70 - i) % 8], S[(71 - i) % 8], \ W[i + ii] + Krnd[i + ii]) /* Message schedule computation */ #define MSCH(W, ii, i) \ W[i + ii + 16] = s1(W[i + ii + 14]) + W[i + ii + 9] + s0(W[i + ii + 1]) + W[i + ii] /* * SHA256 block compression function. The 256-bit state is transformed via * the 512-bit input block to produce a new state. The arrays W and S may be * filled with sensitive data, and should be sanitized by the callee. */ static void SHA256_Transform(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]) { int i; #ifdef HWACCEL #if defined(__GNUC__) && defined(__aarch64__) /* * We require that SHA256_Init() is called before SHA256_Transform(), * but the compiler has no way of knowing that. This assert adds a * significant speed boost for gcc on 64-bit ARM, and a minor penalty * on other systems & compilers. */ assert(hwaccel != HW_UNSET); #endif switch (hwaccel) { #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) case HW_X86_SHANI: SHA256_Transform_shani(state, block); return; #endif #if defined(CPUSUPPORT_X86_SSE2) case HW_X86_SSE2: SHA256_Transform_sse2(state, block, W, S); return; #endif #if defined(CPUSUPPORT_ARM_SHA256) case HW_ARM_SHA256: SHA256_Transform_arm(state, block); return; #endif case HW_SOFTWARE: case HW_UNSET: break; } #endif /* HWACCEL */ /* 1. Prepare the first part of the message schedule W. */ be32dec_vect(W, block, 64); /* 2. Initialize working variables. */ memcpy(S, state, 32); /* 3. Mix. */ for (i = 0; i < 64; i += 16) { RNDr(S, W, 0, i); RNDr(S, W, 1, i); RNDr(S, W, 2, i); RNDr(S, W, 3, i); RNDr(S, W, 4, i); RNDr(S, W, 5, i); RNDr(S, W, 6, i); RNDr(S, W, 7, i); RNDr(S, W, 8, i); RNDr(S, W, 9, i); RNDr(S, W, 10, i); RNDr(S, W, 11, i); RNDr(S, W, 12, i); RNDr(S, W, 13, i); RNDr(S, W, 14, i); RNDr(S, W, 15, i); if (i == 48) break; MSCH(W, 0, i); MSCH(W, 1, i); MSCH(W, 2, i); MSCH(W, 3, i); MSCH(W, 4, i); MSCH(W, 5, i); MSCH(W, 6, i); MSCH(W, 7, i); MSCH(W, 8, i); MSCH(W, 9, i); MSCH(W, 10, i); MSCH(W, 11, i); MSCH(W, 12, i); MSCH(W, 13, i); MSCH(W, 14, i); MSCH(W, 15, i); } /* 4. Mix local working variables into global state. */ for (i = 0; i < 8; i++) state[i] += S[i]; } static const uint8_t PAD[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Add padding and terminating bit-count. */ static void SHA256_Pad(SHA256_CTX * ctx, uint32_t tmp32[static restrict 72]) { size_t r; /* Figure out how many bytes we have buffered. */ r = (ctx->count >> 3) & 0x3f; /* Pad to 56 mod 64, transforming if we finish a block en route. */ if (r < 56) { /* Pad to 56 mod 64. */ memcpy(&ctx->buf[r], PAD, 56 - r); } else { /* Finish the current block and mix. */ memcpy(&ctx->buf[r], PAD, 64 - r); SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); /* The start of the final block is all zeroes. */ memset(&ctx->buf[0], 0, 56); } /* Add the terminating bit-count. */ be64enc(&ctx->buf[56], ctx->count); /* Mix in the final block. */ SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); } /** * SHA256_Init(ctx): * Initialize the SHA256 context ${ctx}. */ void SHA256_Init(SHA256_CTX * ctx) { /* Zero bits processed so far. */ ctx->count = 0; /* Initialize state. */ memcpy(ctx->state, initial_state, sizeof(initial_state)); #ifdef HWACCEL /* Ensure that we've chosen the type of hardware acceleration. */ hwaccel_init(); #endif } /** * SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the SHA256 context ${ctx}. */ static void SHA256_Update_internal(SHA256_CTX * ctx, const void * in, size_t len, uint32_t tmp32[static restrict 72]) { uint32_t r; const uint8_t * src = in; /* Return immediately if we have nothing to do. */ if (len == 0) return; /* Number of bytes left in the buffer from previous updates. */ r = (ctx->count >> 3) & 0x3f; /* Update number of bits. */ ctx->count += (uint64_t)(len) << 3; /* Handle the case where we don't need to perform any transforms. */ if (len < 64 - r) { memcpy(&ctx->buf[r], src, len); return; } /* Finish the current block. */ memcpy(&ctx->buf[r], src, 64 - r); SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); src += 64 - r; len -= 64 - r; /* Perform complete blocks. */ while (len >= 64) { SHA256_Transform(ctx->state, src, &tmp32[0], &tmp32[64]); src += 64; len -= 64; } /* Copy left over data into buffer. */ memcpy(ctx->buf, src, len); } /* Wrapper function for intermediate-values sanitization. */ void SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len) { uint32_t tmp32[72]; /* Call the real function. */ SHA256_Update_internal(ctx, in, len, tmp32); /* Clean the stack. */ insecure_memzero(tmp32, sizeof(uint32_t) * 72); } /** * SHA256_Final(digest, ctx): * Output the SHA256 hash of the data input to the context ${ctx} into the * buffer ${digest}, and clear the context state. */ static void SHA256_Final_internal(uint8_t digest[32], SHA256_CTX * ctx, uint32_t tmp32[static restrict 72]) { /* Add padding. */ SHA256_Pad(ctx, tmp32); /* Write the hash. */ be32enc_vect(digest, ctx->state, 32); } /* Wrapper function for intermediate-values sanitization. */ void SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx) { uint32_t tmp32[72]; /* Call the real function. */ SHA256_Final_internal(digest, ctx, tmp32); /* Clear the context state. */ insecure_memzero(ctx, sizeof(SHA256_CTX)); /* Clean the stack. */ insecure_memzero(tmp32, sizeof(uint32_t) * 72); } /** * SHA256_Buf(in, len, digest): * Compute the SHA256 hash of ${len} bytes from ${in} and write it to ${digest}. */ void SHA256_Buf(const void * in, size_t len, uint8_t digest[32]) { SHA256_CTX ctx; uint32_t tmp32[72]; SHA256_Init(&ctx); SHA256_Update_internal(&ctx, in, len, tmp32); SHA256_Final_internal(digest, &ctx, tmp32); /* Clean the stack. */ insecure_memzero(&ctx, sizeof(SHA256_CTX)); insecure_memzero(tmp32, sizeof(uint32_t) * 72); } /** * HMAC_SHA256_Init(ctx, K, Klen): * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from * ${K}. */ static void HMAC_SHA256_Init_internal(HMAC_SHA256_CTX * ctx, const void * _k, size_t Klen, uint32_t tmp32[static restrict 72], uint8_t pad[static restrict 64], uint8_t khash[static restrict 32]) { const uint8_t * K = _k; size_t i; /* If Klen > 64, the key is really SHA256(K). */ if (Klen > 64) { SHA256_Init(&ctx->ictx); SHA256_Update_internal(&ctx->ictx, K, Klen, tmp32); SHA256_Final_internal(khash, &ctx->ictx, tmp32); K = khash; Klen = 32; } /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ SHA256_Init(&ctx->ictx); memset(pad, 0x36, 64); for (i = 0; i < Klen; i++) pad[i] ^= K[i]; SHA256_Update_internal(&ctx->ictx, pad, 64, tmp32); /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ SHA256_Init(&ctx->octx); memset(pad, 0x5c, 64); for (i = 0; i < Klen; i++) pad[i] ^= K[i]; SHA256_Update_internal(&ctx->octx, pad, 64, tmp32); } /* Wrapper function for intermediate-values sanitization. */ void HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * K, size_t Klen) { uint32_t tmp32[72]; uint8_t pad[64]; uint8_t khash[32]; /* Call the real function. */ HMAC_SHA256_Init_internal(ctx, K, Klen, tmp32, pad, khash); /* Clean the stack. */ insecure_memzero(tmp32, sizeof(uint32_t) * 72); insecure_memzero(khash, 32); insecure_memzero(pad, 64); } /** * HMAC_SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}. */ static void HMAC_SHA256_Update_internal(HMAC_SHA256_CTX * ctx, const void * in, size_t len, uint32_t tmp32[static restrict 72]) { /* Feed data to the inner SHA256 operation. */ SHA256_Update_internal(&ctx->ictx, in, len, tmp32); } /* Wrapper function for intermediate-values sanitization. */ void HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len) { uint32_t tmp32[72]; /* Call the real function. */ HMAC_SHA256_Update_internal(ctx, in, len, tmp32); /* Clean the stack. */ insecure_memzero(tmp32, sizeof(uint32_t) * 72); } /** * HMAC_SHA256_Final(digest, ctx): * Output the HMAC-SHA256 of the data input to the context ${ctx} into the * buffer ${digest}, and clear the context state. */ static void HMAC_SHA256_Final_internal(uint8_t digest[32], HMAC_SHA256_CTX * ctx, uint32_t tmp32[static restrict 72], uint8_t ihash[static restrict 32]) { /* Finish the inner SHA256 operation. */ SHA256_Final_internal(ihash, &ctx->ictx, tmp32); /* Feed the inner hash to the outer SHA256 operation. */ SHA256_Update_internal(&ctx->octx, ihash, 32, tmp32); /* Finish the outer SHA256 operation. */ SHA256_Final_internal(digest, &ctx->octx, tmp32); } /* Wrapper function for intermediate-values sanitization. */ void HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx) { uint32_t tmp32[72]; uint8_t ihash[32]; /* Call the real function. */ HMAC_SHA256_Final_internal(digest, ctx, tmp32, ihash); /* Clear the context state. */ insecure_memzero(ctx, sizeof(HMAC_SHA256_CTX)); /* Clean the stack. */ insecure_memzero(tmp32, sizeof(uint32_t) * 72); insecure_memzero(ihash, 32); } /** * HMAC_SHA256_Buf(K, Klen, in, len, digest): * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of * length ${Klen}, and write the result to ${digest}. */ void HMAC_SHA256_Buf(const void * K, size_t Klen, const void * in, size_t len, uint8_t digest[32]) { HMAC_SHA256_CTX ctx; uint32_t tmp32[72]; uint8_t tmp8[96]; HMAC_SHA256_Init_internal(&ctx, K, Klen, tmp32, &tmp8[0], &tmp8[64]); HMAC_SHA256_Update_internal(&ctx, in, len, tmp32); HMAC_SHA256_Final_internal(digest, &ctx, tmp32, &tmp8[0]); /* Clean the stack. */ insecure_memzero(&ctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(tmp32, sizeof(uint32_t) * 72); insecure_memzero(tmp8, 96); } /** * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). */ void PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) { HMAC_SHA256_CTX Phctx, PShctx, hctx; uint32_t tmp32[72]; uint8_t tmp8[96]; size_t i; uint8_t ivec[4]; uint8_t U[32]; uint8_t T[32]; uint64_t j; int k; size_t clen; #if SIZE_MAX >= (32 * UINT32_MAX) /* Sanity-check. */ assert(dkLen <= 32 * (size_t)(UINT32_MAX)); #endif /* Compute HMAC state after processing P. */ HMAC_SHA256_Init_internal(&Phctx, passwd, passwdlen, tmp32, &tmp8[0], &tmp8[64]); /* Compute HMAC state after processing P and S. */ memcpy(&PShctx, &Phctx, sizeof(HMAC_SHA256_CTX)); HMAC_SHA256_Update_internal(&PShctx, salt, saltlen, tmp32); /* Iterate through the blocks. */ for (i = 0; i * 32 < dkLen; i++) { /* Generate INT(i + 1). */ be32enc(ivec, (uint32_t)(i + 1)); /* Compute U_1 = PRF(P, S || INT(i)). */ memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); HMAC_SHA256_Update_internal(&hctx, ivec, 4, tmp32); HMAC_SHA256_Final_internal(U, &hctx, tmp32, tmp8); /* T_i = U_1 ... */ memcpy(T, U, 32); for (j = 2; j <= c; j++) { /* Compute U_j. */ memcpy(&hctx, &Phctx, sizeof(HMAC_SHA256_CTX)); HMAC_SHA256_Update_internal(&hctx, U, 32, tmp32); HMAC_SHA256_Final_internal(U, &hctx, tmp32, tmp8); /* ... xor U_j ... */ for (k = 0; k < 32; k++) T[k] ^= U[k]; } /* Copy as many bytes as necessary into buf. */ clen = dkLen - i * 32; if (clen > 32) clen = 32; memcpy(&buf[i * 32], T, clen); } /* Clean the stack. */ insecure_memzero(&Phctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(&PShctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(&hctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(tmp32, sizeof(uint32_t) * 72); insecure_memzero(tmp8, 96); insecure_memzero(U, 32); insecure_memzero(T, 32); } tarsnap-1.0.41/libcperciva/alg/sha256.h000066400000000000000000000054131476705112600174700ustar00rootroot00000000000000#ifndef SHA256_H_ #define SHA256_H_ #include #include /* * Use #defines in order to avoid namespace collisions with anyone else's * SHA256 code (e.g., the code in OpenSSL). */ #define SHA256_Init libcperciva_SHA256_Init #define SHA256_Update libcperciva_SHA256_Update #define SHA256_Final libcperciva_SHA256_Final #define SHA256_Buf libcperciva_SHA256_Buf #define SHA256_CTX libcperciva_SHA256_CTX #define HMAC_SHA256_Init libcperciva_HMAC_SHA256_Init #define HMAC_SHA256_Update libcperciva_HMAC_SHA256_Update #define HMAC_SHA256_Final libcperciva_HMAC_SHA256_Final #define HMAC_SHA256_Buf libcperciva_HMAC_SHA256_Buf #define HMAC_SHA256_CTX libcperciva_HMAC_SHA256_CTX /* Context structure for SHA256 operations. */ typedef struct { uint32_t state[8]; uint64_t count; uint8_t buf[64]; } SHA256_CTX; /** * SHA256_Init(ctx): * Initialize the SHA256 context ${ctx}. */ void SHA256_Init(SHA256_CTX *); /** * SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the SHA256 context ${ctx}. */ void SHA256_Update(SHA256_CTX *, const void *, size_t); /** * SHA256_Final(digest, ctx): * Output the SHA256 hash of the data input to the context ${ctx} into the * buffer ${digest}, and clear the context state. */ void SHA256_Final(uint8_t[32], SHA256_CTX *); /** * SHA256_Buf(in, len, digest): * Compute the SHA256 hash of ${len} bytes from ${in} and write it to ${digest}. */ void SHA256_Buf(const void *, size_t, uint8_t[32]); /* Context structure for HMAC-SHA256 operations. */ typedef struct { SHA256_CTX ictx; SHA256_CTX octx; } HMAC_SHA256_CTX; /** * HMAC_SHA256_Init(ctx, K, Klen): * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from * ${K}. */ void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); /** * HMAC_SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}. */ void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); /** * HMAC_SHA256_Final(digest, ctx): * Output the HMAC-SHA256 of the data input to the context ${ctx} into the * buffer ${digest}, and clear the context state. */ void HMAC_SHA256_Final(uint8_t[32], HMAC_SHA256_CTX *); /** * HMAC_SHA256_Buf(K, Klen, in, len, digest): * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of * length ${Klen}, and write the result to ${digest}. */ void HMAC_SHA256_Buf(const void *, size_t, const void *, size_t, uint8_t[32]); /** * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). */ void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, uint8_t *, size_t); #endif /* !SHA256_H_ */ tarsnap-1.0.41/libcperciva/alg/sha256_arm.c000066400000000000000000000057421476705112600203270ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_ARM_SHA256 /** * CPUSUPPORT CFLAGS: ARM_SHA256 */ #include #include #include #ifdef __ARM_NEON #include #endif #include "sha256_arm.h" /* SHA256 round constants. */ static const uint32_t Krnd[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; /* Round computation. */ #define RND4(S, M, Kp) do { \ uint32x4_t S0_step; \ uint32x4_t Wk; \ S0_step = S[0]; \ Wk = vaddq_u32(M, vld1q_u32(Kp)); \ S[0] = vsha256hq_u32(S[0], S[1], Wk); \ S[1] = vsha256h2q_u32(S[1], S0_step, Wk); \ } while (0) /* Message schedule computation */ #define MSG4(X0, X1, X2, X3) \ X0 = vsha256su1q_u32(vsha256su0q_u32(X0, X1), X2, X3) /** * SHA256_Transform_arm(state, block): * Compute the SHA256 block compression function, transforming ${state} using * the data in ${block}. This implementation uses ARM SHA256 instructions, * and should only be used if _SHA256 is defined and cpusupport_arm_sha256() * returns nonzero. */ #ifdef POSIXFAIL_ABSTRACT_DECLARATOR void SHA256_Transform_arm(uint32_t state[8], const uint8_t block[64]) #else void SHA256_Transform_arm(uint32_t state[static restrict 8], const uint8_t block[static restrict 64]) #endif { uint32x4_t Y[4]; uint32x4_t S[2]; uint32x4_t _state[2]; int i; /* 1. Prepare the first part of the message schedule W. */ Y[0] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(&block[0]))); Y[1] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(&block[16]))); Y[2] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(&block[32]))); Y[3] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(&block[48]))); /* 2. Initialize working variables. */ S[0] = _state[0] = vld1q_u32(&state[0]); S[1] = _state[1] = vld1q_u32(&state[4]); /* 3. Mix. */ for (i = 0; i < 64; i += 16) { RND4(S, Y[0], &Krnd[i + 0]); RND4(S, Y[1], &Krnd[i + 4]); RND4(S, Y[2], &Krnd[i + 8]); RND4(S, Y[3], &Krnd[i + 12]); if (i == 48) break; MSG4(Y[0], Y[1], Y[2], Y[3]); MSG4(Y[1], Y[2], Y[3], Y[0]); MSG4(Y[2], Y[3], Y[0], Y[1]); MSG4(Y[3], Y[0], Y[1], Y[2]); } /* 4. Mix local working variables into global state. */ vst1q_u32(&state[0], vaddq_u32(_state[0], S[0])); vst1q_u32(&state[4], vaddq_u32(_state[1], S[1])); } #endif /* CPUSUPPORT_ARM_SHA256 */ tarsnap-1.0.41/libcperciva/alg/sha256_arm.h000066400000000000000000000011411476705112600203210ustar00rootroot00000000000000#ifndef SHA256_ARM_H_ #define SHA256_ARM_H_ #include /** * SHA256_Transform_arm(state, block): * Compute the SHA256 block compression function, transforming ${state} using * the data in ${block}. This implementation uses ARM SHA256 instructions, * and should only be used if _SHA256 is defined and cpusupport_arm_sha256() * returns nonzero. */ #ifdef POSIXFAIL_ABSTRACT_DECLARATOR void SHA256_Transform_arm(uint32_t state[8], const uint8_t block[64]); #else void SHA256_Transform_arm(uint32_t[static restrict 8], const uint8_t[static restrict 64]); #endif #endif /* !SHA256_ARM_H_ */ tarsnap-1.0.41/libcperciva/alg/sha256_shani.c000066400000000000000000000123331476705112600206440ustar00rootroot00000000000000#include "cpusupport.h" #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) /** * CPUSUPPORT CFLAGS: X86_SHANI X86_SSSE3 */ #include #include #include "sha256_shani.h" /** * This code uses intrinsics from the following feature sets: * SHANI: _mm_sha256msg1_epu32, _mm_sha256msg2_epu32, _mm_sha256rnds2_epu32 * SSSE3: _mm_shuffle_epi8, _mm_alignr_epi8 * SSE2: Everything else * * The SSSE3 intrinsics could be avoided at a slight cost by using a few SSE2 * instructions in their place; we have not done this since to our knowledge * there are presently no CPUs which support the SHANI instruction set but do * not support SSSE3. */ /* Load 32-bit big-endian words. */ static __m128i be32dec_128(const uint8_t * src) { const __m128i SHUF = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); __m128i x; /* Load four 32-bit words. */ x = _mm_loadu_si128((const __m128i *)src); /* Reverse the order of the bytes in each word. */ return (_mm_shuffle_epi8(x, SHUF)); } /* Convert an unsigned 32-bit immediate into a signed value. */ #define I32(a) ((UINT32_C(a) >= UINT32_C(0x80000000)) ? \ -(int32_t)(UINT32_C(0xffffffff) - UINT32_C(a)) - 1 : (int32_t)INT32_C(a)) /* Load four unsigned 32-bit immediates into a vector register. */ #define IMM4(a, b, c, d) _mm_set_epi32(I32(a), I32(b), I32(c), I32(d)) /* Run four rounds of SHA256. */ #define RND4(S, W, K0, K1, K2, K3) do { \ __m128i M; \ \ /* Add the next four words of message schedule and round constants. */ \ M = _mm_add_epi32(W, IMM4(K3, K2, K1, K0)); \ \ /* Perform two rounds of SHA256, using the low two words in M. */ \ S[1] = _mm_sha256rnds2_epu32(S[1], S[0], M); \ \ /* Shift the two words of M down and perform the next two rounds. */ \ M = _mm_srli_si128(M, 8); \ S[0] = _mm_sha256rnds2_epu32(S[0], S[1], M); \ } while (0) /* Compute the ith set of four words of message schedule. */ #define MSG4(W, i) do { \ W[(i + 0) % 4] = _mm_sha256msg1_epu32(W[(i + 0) % 4], W[(i + 1) % 4]); \ W[(i + 0) % 4] = _mm_add_epi32(W[(i + 0) % 4], \ _mm_alignr_epi8(W[(i + 3) % 4], W[(i + 2) % 4], 4)); \ W[(i + 0) % 4] = _mm_sha256msg2_epu32(W[(i + 0) % 4], W[(i + 3) % 4]); \ } while (0) /* Perform 4 rounds of SHA256 and generate more message schedule if needed. */ #define RNDMSG(S, W, i, K0, K1, K2, K3) do { \ RND4(S, W[i % 4], K0, K1, K2, K3); \ if (i < 12) \ MSG4(W, i + 4); \ } while (0) /** * SHA256_Transform_shani(state, block): * Compute the SHA256 block compression function, transforming ${state} using * the data in ${block}. This implementation uses x86 SHANI and SSSE3 * instructions, and should only be used if CPUSUPPORT_X86_SHANI and _SSSE3 * are defined and cpusupport_x86_shani() and _ssse3() return nonzero. */ void SHA256_Transform_shani(uint32_t state[static restrict 8], const uint8_t block[static restrict 64]) { __m128i S3210, S7654; __m128i S0123, S4567; __m128i S0145, S2367; __m128i W[4]; __m128i S[2]; /* Load state. */ S3210 = _mm_loadu_si128((const __m128i *)&state[0]); S7654 = _mm_loadu_si128((const __m128i *)&state[4]); /* Shuffle the 8 32-bit values into the order we need them. */ S0123 = _mm_shuffle_epi32(S3210, 0x1B); S4567 = _mm_shuffle_epi32(S7654, 0x1B); S0145 = _mm_unpackhi_epi64(S4567, S0123); S2367 = _mm_unpacklo_epi64(S4567, S0123); /* Load input block; this is the start of the message schedule. */ W[0] = be32dec_128(&block[0]); W[1] = be32dec_128(&block[16]); W[2] = be32dec_128(&block[32]); W[3] = be32dec_128(&block[48]); /* Initialize working variables. */ S[0] = S0145; S[1] = S2367; /* Perform 64 rounds, 4 at a time. */ RNDMSG(S, W, 0, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5); RNDMSG(S, W, 1, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5); RNDMSG(S, W, 2, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3); RNDMSG(S, W, 3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174); RNDMSG(S, W, 4, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc); RNDMSG(S, W, 5, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da); RNDMSG(S, W, 6, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7); RNDMSG(S, W, 7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967); RNDMSG(S, W, 8, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13); RNDMSG(S, W, 9, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85); RNDMSG(S, W, 10, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3); RNDMSG(S, W, 11, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070); RNDMSG(S, W, 12, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5); RNDMSG(S, W, 13, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3); RNDMSG(S, W, 14, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208); RNDMSG(S, W, 15, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2); /* Mix local working variables into global state. */ S0145 = _mm_add_epi32(S0145, S[0]); S2367 = _mm_add_epi32(S2367, S[1]); /* Shuffle state back to the original word order and store. */ S0123 = _mm_unpackhi_epi64(S2367, S0145); S4567 = _mm_unpacklo_epi64(S2367, S0145); S3210 = _mm_shuffle_epi32(S0123, 0x1B); S7654 = _mm_shuffle_epi32(S4567, 0x1B); _mm_storeu_si128((__m128i *)&state[0], S3210); _mm_storeu_si128((__m128i *)&state[4], S7654); } #endif /* CPUSUPPORT_X86_SHANI && CPUSUPPORT_X86_SSSE3 */ tarsnap-1.0.41/libcperciva/alg/sha256_shani.h000066400000000000000000000012761476705112600206550ustar00rootroot00000000000000#ifndef SHA256_SHANI_H_ #define SHA256_SHANI_H_ #include /** * SHA256_Transform_shani(state, block): * Compute the SHA256 block compression function, transforming ${state} using * the data in ${block}. This implementation uses x86 SHANI and SSSE3 * instructions, and should only be used if CPUSUPPORT_X86_SHANI and _SSSE3 * are defined and cpusupport_x86_shani() and _ssse3() return nonzero. */ #ifdef POSIXFAIL_ABSTRACT_DECLARATOR void SHA256_Transform_shani(uint32_t state[static restrict 8], const uint8_t block[static restrict 64]); #else void SHA256_Transform_shani(uint32_t[static restrict 8], const uint8_t[static restrict 64]); #endif #endif /* !SHA256_SHANI_H_ */ tarsnap-1.0.41/libcperciva/alg/sha256_sse2.c000066400000000000000000000160051476705112600204160ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_SSE2 /** * CPUSUPPORT CFLAGS: X86_SSE2 */ #include #include #include #include #include "sha256_sse2.h" /** * mm_bswap_epi32(a): * Byte-swap each 32-bit word. */ static inline __m128i mm_bswap_epi32(__m128i a) { /* Swap bytes in each 16-bit word. */ a = _mm_or_si128(_mm_slli_epi16(a, 8), _mm_srli_epi16(a, 8)); /* Swap all 16-bit words. */ a = _mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); return (a); } /* SHA256 round constants. */ static const uint32_t Krnd[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; /* Elementary functions used by SHA256 */ #define Ch(x, y, z) ((x & (y ^ z)) ^ z) #define Maj(x, y, z) ((x & (y | z)) | (y & z)) #define ROTR(x, n) ((x >> n) | (x << (32 - n))) #define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) #define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) /* SHA256 round function */ #define RND(a, b, c, d, e, f, g, h, k) \ h += S1(e) + Ch(e, f, g) + k; \ d += h; \ h += S0(a) + Maj(a, b, c) /* Adjusted round function for rotating state */ #define RNDr(S, W, i, ii) \ RND(S[(64 - i) % 8], S[(65 - i) % 8], \ S[(66 - i) % 8], S[(67 - i) % 8], \ S[(68 - i) % 8], S[(69 - i) % 8], \ S[(70 - i) % 8], S[(71 - i) % 8], \ W[i + ii] + Krnd[i + ii]) /* Message schedule computation */ #define SHR32(x, n) (_mm_srli_epi32(x, n)) #define ROTR32(x, n) (_mm_or_si128(SHR32(x, n), _mm_slli_epi32(x, (32-n)))) #define s0_128(x) _mm_xor_si128(_mm_xor_si128( \ ROTR32(x, 7), ROTR32(x, 18)), SHR32(x, 3)) static inline __m128i s1_128_high(__m128i a) { __m128i b; __m128i c; /* ROTR, loading data as {B, B, A, A}; lanes 1 & 3 will be junk. */ b = _mm_shuffle_epi32(a, _MM_SHUFFLE(1, 1, 0, 0)); c = _mm_xor_si128(_mm_srli_epi64(b, 17), _mm_srli_epi64(b, 19)); /* Shift and XOR with rotated data; lanes 1 & 3 will be junk. */ c = _mm_xor_si128(c, _mm_srli_epi32(b, 10)); /* Shuffle good data back and zero unwanted lanes. */ c = _mm_shuffle_epi32(c, _MM_SHUFFLE(2, 0, 2, 0)); c = _mm_slli_si128(c, 8); return (c); } static inline __m128i s1_128_low(__m128i a) { __m128i b; __m128i c; /* ROTR, loading data as {B, B, A, A}; lanes 1 & 3 will be junk. */ b = _mm_shuffle_epi32(a, _MM_SHUFFLE(3, 3, 2, 2)); c = _mm_xor_si128(_mm_srli_epi64(b, 17), _mm_srli_epi64(b, 19)); /* Shift and XOR with rotated data; lanes 1 & 3 will be junk. */ c = _mm_xor_si128(c, _mm_srli_epi32(b, 10)); /* Shuffle good data back and zero unwanted lanes. */ c = _mm_shuffle_epi32(c, _MM_SHUFFLE(2, 0, 2, 0)); c = _mm_srli_si128(c, 8); return (c); } /** * SPAN_ONE_THREE(a, b): * Combine the upper three words of ${a} with the lowest word of ${b}. This * could also be thought of returning bits [159:32] of the 256-bit value * consisting of (b[127:0] a[127:0]). In other words, set: * dst[31:0] := a[63:32] * dst[63:32] := a[95:64] * dst[95:64] := a[127:96] * dst[127:96] := b[31:0] */ #define SPAN_ONE_THREE(a, b) (_mm_shuffle_epi32(_mm_castps_si128( \ _mm_move_ss(_mm_castsi128_ps(a), _mm_castsi128_ps(b))), \ _MM_SHUFFLE(0, 3, 2, 1))) /** * MSG4(X0, X1, X2, X3): * Calculate the next four values of the message schedule. If we define * ${W[j]} as the first unknown value in the message schedule, then the input * arguments are: * X0 = W[j - 16] : W[j - 13] * X1 = W[j - 12] : W[j - 9] * X2 = W[j - 8] : W[j - 5] * X3 = W[j - 4] : W[j - 1] * This function therefore calculates: * X4 = W[j + 0] : W[j + 3] */ static inline __m128i MSG4(__m128i X0, __m128i X1, __m128i X2, __m128i X3) { __m128i X4; __m128i Xj_minus_seven, Xj_minus_fifteen; /* Set up variables which span X values. */ Xj_minus_seven = SPAN_ONE_THREE(X2, X3); Xj_minus_fifteen = SPAN_ONE_THREE(X0, X1); /* Begin computing X4. */ X4 = _mm_add_epi32(X0, Xj_minus_seven); X4 = _mm_add_epi32(X4, s0_128(Xj_minus_fifteen)); /* First half of s1. */ X4 = _mm_add_epi32(X4, s1_128_low(X3)); /* Second half of s1; this depends on the above value of X4. */ X4 = _mm_add_epi32(X4, s1_128_high(X4)); return (X4); } /** * SHA256_Transform_sse2(state, block, W, S): * Compute the SHA256 block compression function, transforming ${state} using * the data in ${block}. This implementation uses x86 SSE2 instructions, and * should only be used if _SSE2 is defined and cpusupport_x86_sse2() returns * nonzero. The arrays W and S may be filled with sensitive data, and should * be cleared by the callee. */ #ifdef POSIXFAIL_ABSTRACT_DECLARATOR void SHA256_Transform_sse2(uint32_t state[8], const uint8_t block[64], uint32_t W[64], uint32_t S[8]) #else void SHA256_Transform_sse2(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]) #endif { __m128i Y[4]; int i; /* 1. Prepare the first part of the message schedule W. */ Y[0] = mm_bswap_epi32(_mm_loadu_si128((const __m128i *)&block[0])); _mm_storeu_si128((__m128i *)&W[0], Y[0]); Y[1] = mm_bswap_epi32(_mm_loadu_si128((const __m128i *)&block[16])); _mm_storeu_si128((__m128i *)&W[4], Y[1]); Y[2] = mm_bswap_epi32(_mm_loadu_si128((const __m128i *)&block[32])); _mm_storeu_si128((__m128i *)&W[8], Y[2]); Y[3] = mm_bswap_epi32(_mm_loadu_si128((const __m128i *)&block[48])); _mm_storeu_si128((__m128i *)&W[12], Y[3]); /* 2. Initialize working variables. */ memcpy(S, state, 32); /* 3. Mix. */ for (i = 0; i < 64; i += 16) { RNDr(S, W, 0, i); RNDr(S, W, 1, i); RNDr(S, W, 2, i); RNDr(S, W, 3, i); RNDr(S, W, 4, i); RNDr(S, W, 5, i); RNDr(S, W, 6, i); RNDr(S, W, 7, i); RNDr(S, W, 8, i); RNDr(S, W, 9, i); RNDr(S, W, 10, i); RNDr(S, W, 11, i); RNDr(S, W, 12, i); RNDr(S, W, 13, i); RNDr(S, W, 14, i); RNDr(S, W, 15, i); if (i == 48) break; Y[0] = MSG4(Y[0], Y[1], Y[2], Y[3]); _mm_storeu_si128((__m128i *)&W[16 + i + 0], Y[0]); Y[1] = MSG4(Y[1], Y[2], Y[3], Y[0]); _mm_storeu_si128((__m128i *)&W[16 + i + 4], Y[1]); Y[2] = MSG4(Y[2], Y[3], Y[0], Y[1]); _mm_storeu_si128((__m128i *)&W[16 + i + 8], Y[2]); Y[3] = MSG4(Y[3], Y[0], Y[1], Y[2]); _mm_storeu_si128((__m128i *)&W[16 + i + 12], Y[3]); } /* 4. Mix local working variables into global state. */ for (i = 0; i < 8; i++) state[i] += S[i]; } #endif /* CPUSUPPORT_X86_SSE2 */ tarsnap-1.0.41/libcperciva/alg/sha256_sse2.h000066400000000000000000000014541476705112600204250ustar00rootroot00000000000000#ifndef SHA256_SSE2_H_ #define SHA256_SSE2_H_ #include /** * SHA256_Transform_sse2(state, block, W, S): * Compute the SHA256 block compression function, transforming ${state} using * the data in ${block}. This implementation uses x86 SSE2 instructions, and * should only be used if _SSE2 is defined and cpusupport_x86_sse2() returns * nonzero. The arrays W and S may be filled with sensitive data, and should * be cleared by the callee. */ #ifdef POSIXFAIL_ABSTRACT_DECLARATOR void SHA256_Transform_sse2(uint32_t state[8], const uint8_t block[64], uint32_t W[64], uint32_t S[8]); #else void SHA256_Transform_sse2(uint32_t[static restrict 8], const uint8_t[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]); #endif #endif /* !SHA256_SSE2_H_ */ tarsnap-1.0.41/libcperciva/apisupport/000077500000000000000000000000001476705112600177475ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/apisupport/Build/000077500000000000000000000000001476705112600210065ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/apisupport/Build/apisupport-LIBCRYPTO-LOW_LEVEL_AES.c000066400000000000000000000003331476705112600270020ustar00rootroot00000000000000#include #include int main(void) { AES_KEY kexp_actual; const uint8_t key_unexpanded[16] = { 0 }; AES_set_encrypt_key(key_unexpanded, 128, &kexp_actual); /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/apisupport/Build/apisupport-LIBCRYPTO-LOW_LEVEL_RSA.c000066400000000000000000000002361476705112600270210ustar00rootroot00000000000000#include int main(void) { RSA * rsa; /* Allocate and free a RSA key. */ rsa = RSA_new(); RSA_free(rsa); /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/apisupport/Build/apisupport.sh000077500000000000000000000045661476705112600235660ustar00rootroot00000000000000# Should be sourced by `command -p sh path/to/apisupport.sh "$PATH"` from # within a Makefile. if ! [ "${PATH}" = "$1" ]; then echo "WARNING: POSIX violation: ${SHELL}'s command -p resets \$PATH" 1>&2 PATH=$1 fi # Standard output should be written to apisupport-config.h, which is both a # C header file defining APISUPPORT_PLATFORM_FEATURE macros and sourceable sh # code which sets CFLAGS_PLATFORM_FEATURE environment variables. SRCDIR=$(command -p dirname "$0") CFLAGS_HARDCODED="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700" # Do we want to record stderr to a file? if [ "${DEBUG:-0}" -eq "0" ]; then outcc="/dev/null" else outcc="apisupport-stderr.log" rm -f "${outcc}" fi feature() { PLATFORM=$1 FEATURE=$2 EXTRALIB=$3 shift 3; # Bail if we didn't include this feature in this source tree. feature_filename="${SRCDIR}/apisupport-${PLATFORM}-${FEATURE}.c" if ! [ -f "${feature_filename}" ]; then return fi # Check if we can compile this feature (and any required arguments). printf "Checking if compiler supports %s %s feature..." \ "${PLATFORM}" "${FEATURE}" 1>&2 for API_CFLAGS in "$@"; do if ${CC} ${CPPFLAGS} ${CFLAGS} ${CFLAGS_HARDCODED} \ ${API_CFLAGS} "${feature_filename}" ${LDADD_EXTRA} \ ${EXTRALIB} 2>>"${outcc}"; then rm -f a.out break; fi API_CFLAGS=NOTSUPPORTED; done case ${API_CFLAGS} in NOTSUPPORTED) echo " no" 1>&2 ;; "") echo " yes" 1>&2 echo "#define APISUPPORT_${PLATFORM}_${FEATURE} 1" ;; *) echo " yes, via ${API_CFLAGS}" 1>&2 echo "#define APISUPPORT_${PLATFORM}_${FEATURE} 1" echo "#ifdef apisupport_dummy" echo "export CFLAGS_${PLATFORM}_${FEATURE}=\"${API_CFLAGS}\"" echo "#endif" ;; esac } if [ "$2" = "--all" ]; then feature() { PLATFORM=$1 FEATURE=$2 echo "#define APISUPPORT_${PLATFORM}_${FEATURE} 1" } fi # Detect how to compile non-POSIX code. feature NONPOSIX SETGROUPS "" "" \ "-U_POSIX_C_SOURCE -U_XOPEN_SOURCE" \ "-U_POSIX_C_SOURCE -U_XOPEN_SOURCE -Wno-reserved-id-macro" feature NONPOSIX MEMLIMIT "" "" \ "-U_POSIX_C_SOURCE -U_XOPEN_SOURCE" \ "-U_POSIX_C_SOURCE -U_XOPEN_SOURCE -Wno-reserved-id-macro" # Detect how to compile libssl and libcrypto code. feature LIBSSL HOST_NAME "-lssl" "" \ "-Wno-cast-qual" feature LIBCRYPTO LOW_LEVEL_AES "-lcrypto" "" \ "-Wno-deprecated-declarations" feature LIBCRYPTO LOW_LEVEL_RSA "-lcrypto" "" \ "-Wno-deprecated-declarations" tarsnap-1.0.41/libcperciva/cpusupport/000077500000000000000000000000001476705112600177655ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/cpusupport/Build/000077500000000000000000000000001476705112600210245ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-ARM-AES.c000066400000000000000000000006571476705112600244470ustar00rootroot00000000000000#include #ifdef __ARM_NEON #include #endif int main(void) { uint8x16_t data; uint8x16_t key = {0}; uint8x16_t output; uint32x4_t lanes = {0}; uint8_t arr[16] = {0}; /* Check AES. */ data = vld1q_u8(arr); output = vaeseq_u8(data, key); (void)output; /* UNUSED */ /* Check _u32: some compilers only support the _u8 variant. */ lanes = vdupq_laneq_u32(lanes, 0); /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-ARM-SHA256.c000066400000000000000000000003411476705112600246750ustar00rootroot00000000000000#ifdef __ARM_NEON #include #endif int main(void) { uint32x4_t w0 = {0}; uint32x4_t w4 = {0}; uint32x4_t output; output = vsha256su0q_u32(w0, w4); (void)output; /* UNUSED */ /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-HWCAP-ELF_AUX_INFO.c000066400000000000000000000002461476705112600262120ustar00rootroot00000000000000#include int main(void) { int res; unsigned long val; res = elf_aux_info(AT_HWCAP, &val, sizeof(unsigned long)); (void)res; return (val != 0); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-HWCAP-GETAUXVAL.c000066400000000000000000000001601476705112600256070ustar00rootroot00000000000000#include int main(void) { unsigned long val; val = getauxval(AT_HWCAP); return (val != 0); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-AESNI.c000066400000000000000000000011411476705112600246310ustar00rootroot00000000000000#include #include /* * Use a separate function for this, because that means that the alignment of * the _mm_loadu_si128() will move to function level, which may require * -Wno-cast-align. */ static __m128i load_128(const uint8_t * src) { __m128i x; x = _mm_loadu_si128((const __m128i *)src); return (x); } int main(void) { __m128i x, y; uint8_t a[16] = {0}; x = load_128(a); #ifdef BROKEN_MM_LOADU_SI64 y = _mm_loadu_si128((const __m128i *)a); #else y = _mm_loadu_si64(a); #endif y = _mm_aesenc_si128(x, y); _mm_storeu_si128((__m128i *)&a[0], y); return (a[0]); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-CPUID.c000066400000000000000000000001541476705112600246410ustar00rootroot00000000000000#include int main(void) { unsigned int a, b, c, d; return (__get_cpuid(0, &a, &b, &c, &d)); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-CPUID_COUNT.c000066400000000000000000000001661476705112600256140ustar00rootroot00000000000000#include int main(void) { unsigned int a, b, c, d; __cpuid_count(7, 0, a, b, c, d); return ((int)a); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-RDRAND.c000066400000000000000000000001341476705112600247450ustar00rootroot00000000000000#include int main(void) { unsigned int x; return (!_rdrand32_step(&x)); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-SHANI.c000066400000000000000000000007621476705112600246440ustar00rootroot00000000000000#include #include /* * Use a separate function for this, because that means that the alignment of * the _mm_loadu_si128() will move to function level, which may require * -Wno-cast-align. */ static __m128i load_128(const uint8_t * src) { __m128i x; x = _mm_loadu_si128((const __m128i *)src); return (x); } int main(void) { __m128i x; uint8_t a[16] = {0}; x = load_128(a); x = _mm_sha256msg1_epu32(x, x); _mm_storeu_si128((__m128i *)a, x); return (a[0]); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-SSE2.c000066400000000000000000000006701476705112600245140ustar00rootroot00000000000000#include static char a[16]; /* * Use a separate function for this, because that means that the alignment of * the _mm_loadu_si128() will move to function level, which may require * -Wno-cast-align. */ static __m128i load_128(const char * src) { __m128i x; x = _mm_loadu_si128((const __m128i *)src); return (x); } int main(void) { __m128i x; x = load_128(a); _mm_storeu_si128((__m128i *)a, x); return (a[0]); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport-X86-SSSE3.c000066400000000000000000000007561476705112600246450ustar00rootroot00000000000000#include #include static char a[16]; /* * Use a separate function for this, because that means that the alignment of * the _mm_loadu_si128() will move to function level, which may require * -Wno-cast-align. */ static __m128i load_128(const char * src) { __m128i x; x = _mm_loadu_si128((const __m128i *)src); return (x); } int main(void) { __m128i x; x = load_128(a); x = _mm_alignr_epi8(x, x, 8); _mm_storeu_si128((__m128i *)a, x); return (a[0]); } tarsnap-1.0.41/libcperciva/cpusupport/Build/cpusupport.sh000066400000000000000000000064061476705112600236120ustar00rootroot00000000000000# Should be sourced by `command -p sh path/to/cpusupport.sh "$PATH"` from # within a Makefile. if ! [ "${PATH}" = "$1" ]; then echo "WARNING: POSIX violation: ${SHELL}'s command -p resets \$PATH" 1>&2 PATH=$1 fi # Standard output should be written to cpusupport-config.h, which is both a # C header file defining CPUSUPPORT_ARCH_FEATURE macros and sourceable sh # code which sets CFLAGS_ARCH_FEATURE environment variables. SRCDIR=$(command -p dirname "$0") CFLAGS_HARDCODED="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700" # Do we want to record stderr to a file? if [ "${DEBUG:-0}" -eq "0" ]; then outcc="/dev/null" else outcc="cpusupport-stderr.log" rm -f "${outcc}" fi feature() { ARCH=$1 FEATURE=$2 shift 2; # Bail if we didn't include this feature in this source tree. feature_filename="${SRCDIR}/cpusupport-${ARCH}-${FEATURE}.c" if ! [ -f "${feature_filename}" ]; then return fi # Check if we can compile this feature (and any required arguments). printf "Checking if compiler supports %s %s feature..." \ "${ARCH}" "${FEATURE}" 1>&2 for CPU_CFLAGS in "$@"; do if ${CC} ${CPPFLAGS} ${CFLAGS} ${CFLAGS_HARDCODED} \ ${CPU_CFLAGS} "${feature_filename}" 2>>"${outcc}"; then rm -f a.out break; fi CPU_CFLAGS=NOTSUPPORTED; done case ${CPU_CFLAGS} in NOTSUPPORTED) echo " no" 1>&2 ;; "") echo " yes" 1>&2 echo "#define CPUSUPPORT_${ARCH}_${FEATURE} 1" ;; *) echo " yes, via ${CPU_CFLAGS}" 1>&2 echo "#define CPUSUPPORT_${ARCH}_${FEATURE} 1" echo "#ifdef cpusupport_dummy" echo "export CFLAGS_${ARCH}_${FEATURE}=\"${CPU_CFLAGS}\"" echo "#endif" ;; esac } if [ "$2" = "--all" ]; then feature() { ARCH=$1 FEATURE=$2 echo "#define CPUSUPPORT_${ARCH}_${FEATURE} 1" } fi # Detect CPU-detection features feature HWCAP ELF_AUX_INFO "" feature HWCAP GETAUXVAL "" feature X86 CPUID "" feature X86 CPUID_COUNT "" # Detect specific features feature X86 AESNI "" "-maes" \ "-maes -Wno-cast-align" \ "-maes -Wno-missing-prototypes -Wno-cast-qual" \ "-maes -Wno-missing-prototypes -Wno-cast-qual -Wno-cast-align" \ "-maes -Wno-missing-prototypes -Wno-cast-qual -Wno-cast-align \ -DBROKEN_MM_LOADU_SI64" feature X86 RDRAND "" "-mrdrnd" feature X86 SHANI "" "-msse2 -msha" \ "-msse2 -msha -Wno-cast-align" feature X86 SSE2 "" \ "-Wno-cast-align" \ "-msse2" \ "-msse2 -Wno-cast-align" feature X86 SSE42 "" "-msse4.2" \ "-msse4.2 -Wno-cast-align" \ "-msse4.2 -Wno-cast-align -fno-strict-aliasing" \ "-msse4.2 -Wno-cast-align -fno-strict-aliasing -Wno-cast-qual" feature X86 SSE42_64 "" "-msse4.2" \ "-msse4.2 -Wno-cast-align" \ "-msse4.2 -Wno-cast-align -fno-strict-aliasing" \ "-msse4.2 -Wno-cast-align -fno-strict-aliasing -Wno-cast-qual" feature X86 SSSE3 "" "-mssse3" \ "-mssse3 -Wno-cast-align" # Detect specific ARM features feature ARM AES "-march=armv8.1-a+crypto" \ "-march=armv8.1-a+crypto -D__ARM_ACLE=200" feature ARM CRC32_64 "-march=armv8.1-a" \ "-march=armv8.1-a+crc" \ "-march=armv8.1-a+crc -Wno-cast-align" \ "-march=armv8.1-a -D__ARM_ACLE=200" feature ARM SHA256 "-march=armv8.1-a+crypto" \ "-march=armv8.1-a+crypto -Wno-cast-align" \ "-march=armv8.1-a+crypto -D__ARM_ACLE=200" tarsnap-1.0.41/libcperciva/cpusupport/cpusupport.h000066400000000000000000000153361476705112600223720ustar00rootroot00000000000000#ifndef CPUSUPPORT_H_ #define CPUSUPPORT_H_ /* * To enable support for non-portable CPU features at compile time, one or * more CPUSUPPORT_ARCH_FEATURE macros should be defined. This can be done * directly on the compiler command line via -D CPUSUPPORT_ARCH_FEATURE or * -D CPUSUPPORT_ARCH_FEATURE=1; or a file can be created with the * necessary #define lines and then -D CPUSUPPORT_CONFIG_FILE=cpuconfig.h * (or similar) can be provided to include that file here. */ #ifdef CPUSUPPORT_CONFIG_FILE #include CPUSUPPORT_CONFIG_FILE #endif /** * The CPUSUPPORT_FEATURE macro declares the necessary variables and * functions for detecting CPU feature support at run time. The function * defined in the macro acts to cache the result of the ..._detect function * using the ..._present and ..._init variables. The _detect function and the * _present and _init variables are turn defined by CPUSUPPORT_FEATURE_DECL in * appropriate cpusupport_foo_bar.c file. * * In order to allow CPUSUPPORT_FEATURE to be used for features which do not * have corresponding CPUSUPPORT_FEATURE_DECL blocks in another source file, * we abuse the C preprocessor: If CPUSUPPORT_${enabler} is defined to 1, then * we access _present_1, _init_1, and _detect_1; but if it is not defined, we * access _present_CPUSUPPORT_${enabler} etc., which we define as static, thus * preventing the compiler from emitting a reference to an external symbol. * * In this way, it becomes possible to issue CPUSUPPORT_FEATURE invocations * for nonexistent features without running afoul of the requirement that * "If an identifier declared with external linkage is used... in the entire * program there shall be exactly one external definition" (C99 standard, 6.9 * paragraph 5). In practice, this means that users of the cpusupport code * can omit build and runtime detection files without changing the framework * code. */ #define CPUSUPPORT_FEATURE__(arch_feature, enabler, enabled) \ static int cpusupport_ ## arch_feature ## _present ## _CPUSUPPORT_ ## enabler; \ static int cpusupport_ ## arch_feature ## _init ## _CPUSUPPORT_ ## enabler; \ static inline int cpusupport_ ## arch_feature ## _detect ## _CPUSUPPORT_ ## enabler(void) { return (0); } \ extern int cpusupport_ ## arch_feature ## _present_ ## enabled; \ extern int cpusupport_ ## arch_feature ## _init_ ## enabled; \ int cpusupport_ ## arch_feature ## _detect_ ## enabled(void); \ \ static inline int \ cpusupport_ ## arch_feature(void) \ { \ \ if (cpusupport_ ## arch_feature ## _present_ ## enabled) \ return (1); \ else if (cpusupport_ ## arch_feature ## _init_ ## enabled) \ return (0); \ cpusupport_ ## arch_feature ## _present_ ## enabled = \ cpusupport_ ## arch_feature ## _detect_ ## enabled(); \ cpusupport_ ## arch_feature ## _init_ ## enabled = 1; \ return (cpusupport_ ## arch_feature ## _present_ ## enabled); \ } \ static void (* cpusupport_ ## arch_feature ## _dummyptr)(void); \ static inline void \ cpusupport_ ## arch_feature ## _dummyfunc(void) \ { \ \ (void)cpusupport_ ## arch_feature ## _present ## _CPUSUPPORT_ ## enabler; \ (void)cpusupport_ ## arch_feature ## _init ## _CPUSUPPORT_ ## enabler; \ (void)cpusupport_ ## arch_feature ## _detect ## _CPUSUPPORT_ ## enabler; \ (void)cpusupport_ ## arch_feature ## _present_ ## enabled; \ (void)cpusupport_ ## arch_feature ## _init_ ## enabled; \ (void)cpusupport_ ## arch_feature ## _detect_ ## enabled; \ (void)cpusupport_ ## arch_feature ## _dummyptr; \ } \ static void (* cpusupport_ ## arch_feature ## _dummyptr)(void) = cpusupport_ ## arch_feature ## _dummyfunc; \ struct cpusupport_ ## arch_feature ## _dummy #define CPUSUPPORT_FEATURE_(arch_feature, enabler, enabled) \ CPUSUPPORT_FEATURE__(arch_feature, enabler, enabled) #define CPUSUPPORT_FEATURE(arch, feature, enabler) \ CPUSUPPORT_FEATURE_(arch ## _ ## feature, enabler, CPUSUPPORT_ ## enabler) /** * CPUSUPPORT_FEATURE_DECL(arch, feature): * Macro which defines variables and provides a function declaration for * detecting the presence of "feature" on the "arch" architecture. The * function body following this macro expansion must return nonzero if the * feature is present, or zero if the feature is not present or the detection * fails for any reason. */ #define CPUSUPPORT_FEATURE_DECL(arch, feature) \ extern int cpusupport_ ## arch ## _ ## feature ## _present_1; \ extern int cpusupport_ ## arch ## _ ## feature ## _init_1; \ int cpusupport_ ## arch ## _ ## feature ## _present_1 = 0; \ int cpusupport_ ## arch ## _ ## feature ## _init_1 = 0; \ int cpusupport_ ## arch ## _ ## feature ## _detect_1(void); \ int \ cpusupport_ ## arch ## _ ## feature ## _detect_1(void) /** * CPUSUPPORT_VALIDATE(hwvar, success_value, cpusupport_checks, check): * Check if we can enable ${success_value}, given the ${cpusupport_checks} and * ${check}; if so, write to ${hwvar}. If the ${cpusupport_checks} pass but * the ${check} is non-zero, produce a warning which includes a stringified * ${success_value}, then fallthrough. */ #define CPUSUPPORT_VALIDATE(hwvar, success_value, cpusupport_checks, \ check) do { \ if ((cpusupport_checks)) { \ if ((check) == 0) { \ (hwvar) = (success_value); \ return; \ } else { \ warn0("Disabling " #success_value \ " due to failed self-test"); \ } \ } \ } while (0) /** * List of features. If a feature here is not enabled by the appropriate * CPUSUPPORT_ARCH_FEATURE macro being defined, it has no effect; but if the * relevant macro may be defined (e.g., by Build/cpusupport.sh successfully * compiling Build/cpusupport-ARCH-FEATURE.c) then the C file containing the * corresponding run-time detection code (cpusupport_arch_feature.c) must be * compiled and linked in. * * There are a few features for which we do not have run-time checks: * - X86_CPUID: compile-time is enough; if __get_cpuid() fails, then all the * x86 detection features will fail, but there's nothing we can * do about that. * - X86_CPUID_COUNT: ditto. * - X86_SSE42_64: the cpuid check tells us if the CPU supports SSE4.2, but * that says nothing about whether it's in 64-bit mode. */ CPUSUPPORT_FEATURE(x86, aesni, X86_AESNI); CPUSUPPORT_FEATURE(x86, rdrand, X86_RDRAND); CPUSUPPORT_FEATURE(x86, shani, X86_SHANI); CPUSUPPORT_FEATURE(x86, sse2, X86_SSE2); CPUSUPPORT_FEATURE(x86, sse42, X86_SSE42); CPUSUPPORT_FEATURE(x86, ssse3, X86_SSSE3); CPUSUPPORT_FEATURE(arm, aes, ARM_AES); CPUSUPPORT_FEATURE(arm, crc32_64, ARM_CRC32_64); CPUSUPPORT_FEATURE(arm, sha256, ARM_SHA256); #endif /* !CPUSUPPORT_H_ */ tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_arm_aes.c000066400000000000000000000027111476705112600240450ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_HWCAP_GETAUXVAL #include #if defined(__arm__) /** * Workaround for a glibc bug: contains a comment saying: * The following must match the kernel's . * However, it does not contain any of the HWCAP2_* entries from . */ #ifndef HWCAP2_AES #include #endif #endif /* __arm__ */ #endif /* CPUSUPPORT_HWCAP_GETAUXVAL */ #if defined(CPUSUPPORT_HWCAP_ELF_AUX_INFO) #include #endif /* CPUSUPPORT_HWCAP_ELF_AUX_INFO */ CPUSUPPORT_FEATURE_DECL(arm, aes) { int supported = 0; #if defined(CPUSUPPORT_ARM_AES) #if defined(CPUSUPPORT_HWCAP_GETAUXVAL) unsigned long capabilities; #if defined(__aarch64__) capabilities = getauxval(AT_HWCAP); supported = (capabilities & HWCAP_AES) ? 1 : 0; #elif defined(__arm__) capabilities = getauxval(AT_HWCAP2); supported = (capabilities & HWCAP2_AES) ? 1 : 0; #endif #endif /* CPUSUPPORT_HWCAP_GETAUXVAL */ #if defined(CPUSUPPORT_HWCAP_ELF_AUX_INFO) unsigned long capabilities; #if defined(__aarch64__) if (elf_aux_info(AT_HWCAP, &capabilities, sizeof(unsigned long))) return (0); supported = (capabilities & HWCAP_AES) ? 1 : 0; #else if (elf_aux_info(AT_HWCAP2, &capabilities, sizeof(unsigned long))) return (0); supported = (capabilities & HWCAP2_AES) ? 1 : 0; #endif #endif /* CPUSUPPORT_HWCAP_ELF_AUX_INFO */ #endif /* CPUSUPPORT_ARM_AES */ /* Return the supported status. */ return (supported); } tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_arm_sha256.c000066400000000000000000000027301476705112600243060ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_HWCAP_GETAUXVAL #include #if defined(__arm__) /** * Workaround for a glibc bug: contains a comment saying: * The following must match the kernel's . * However, it does not contain any of the HWCAP2_* entries from . */ #ifndef HWCAP2_CRC32 #include #endif #endif /* __arm__ */ #endif /* CPUSUPPORT_HWCAP_GETAUXVAL */ #if defined(CPUSUPPORT_HWCAP_ELF_AUX_INFO) #include #endif /* CPUSUPPORT_HWCAP_ELF_AUX_INFO */ CPUSUPPORT_FEATURE_DECL(arm, sha256) { int supported = 0; #if defined(CPUSUPPORT_ARM_SHA256) #if defined(CPUSUPPORT_HWCAP_GETAUXVAL) unsigned long capabilities; #if defined(__aarch64__) capabilities = getauxval(AT_HWCAP); supported = (capabilities & HWCAP_SHA2) ? 1 : 0; #elif defined(__arm__) capabilities = getauxval(AT_HWCAP2); supported = (capabilities & HWCAP2_SHA2) ? 1 : 0; #endif #endif /* CPUSUPPORT_HWCAP_GETAUXVAL */ #if defined(CPUSUPPORT_HWCAP_ELF_AUX_INFO) unsigned long capabilities; #if defined(__aarch64__) if (elf_aux_info(AT_HWCAP, &capabilities, sizeof(unsigned long))) return (0); supported = (capabilities & HWCAP_SHA2) ? 1 : 0; #else if (elf_aux_info(AT_HWCAP2, &capabilities, sizeof(unsigned long))) return (0); supported = (capabilities & HWCAP2_SHA2) ? 1 : 0; #endif #endif /* CPUSUPPORT_HWCAP_ELF_AUX_INFO */ #endif /* CPUSUPPORT_ARM_SHA256 */ /* Return the supported status. */ return (supported); } tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_x86_aesni.c000066400000000000000000000011371476705112600242430ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_CPUID #include #define CPUID_AESNI_BIT (1 << 25) #endif CPUSUPPORT_FEATURE_DECL(x86, aesni) { #ifdef CPUSUPPORT_X86_CPUID unsigned int eax, ebx, ecx, edx; /* Check if CPUID supports the level we need. */ if (!__get_cpuid(0, &eax, &ebx, &ecx, &edx)) goto unsupported; if (eax < 1) goto unsupported; /* Ask about CPU features. */ if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) goto unsupported; /* Return the relevant feature bit. */ return ((ecx & CPUID_AESNI_BIT) ? 1 : 0); unsupported: #endif /* Not supported. */ return (0); } tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_x86_rdrand.c000066400000000000000000000011421476705112600244120ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_CPUID #include #define CPUID_RDRAND_BIT (1 << 30) #endif CPUSUPPORT_FEATURE_DECL(x86, rdrand) { #ifdef CPUSUPPORT_X86_CPUID unsigned int eax, ebx, ecx, edx; /* Check if CPUID supports the level we need. */ if (!__get_cpuid(0, &eax, &ebx, &ecx, &edx)) goto unsupported; if (eax < 1) goto unsupported; /* Ask about CPU features. */ if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) goto unsupported; /* Return the relevant feature bit. */ return ((ecx & CPUID_RDRAND_BIT) ? 1 : 0); unsupported: #endif /* Not supported. */ return (0); } tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_x86_shani.c000066400000000000000000000015331476705112600242460ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_CPUID_COUNT #include #define CPUID_SHANI_BIT (1 << 29) #endif CPUSUPPORT_FEATURE_DECL(x86, shani) { #ifdef CPUSUPPORT_X86_CPUID_COUNT unsigned int eax, ebx, ecx, edx; /* Check if CPUID supports the level we need. */ if (!__get_cpuid(0, &eax, &ebx, &ecx, &edx)) goto unsupported; if (eax < 7) goto unsupported; /* * Ask about extended CPU features. Note that this macro violates * the principle of being "function-like" by taking the variables * used for holding output registers as named parameters rather than * as pointers (which would be necessary if __cpuid_count were a * function). */ __cpuid_count(7, 0, eax, ebx, ecx, edx); /* Return the relevant feature bit. */ return ((ebx & CPUID_SHANI_BIT) ? 1 : 0); unsupported: #endif /* Not supported. */ return (0); } tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_x86_sse2.c000066400000000000000000000011341476705112600240150ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_CPUID #include #define CPUID_SSE2_BIT (1 << 26) #endif CPUSUPPORT_FEATURE_DECL(x86, sse2) { #ifdef CPUSUPPORT_X86_CPUID unsigned int eax, ebx, ecx, edx; /* Check if CPUID supports the level we need. */ if (!__get_cpuid(0, &eax, &ebx, &ecx, &edx)) goto unsupported; if (eax < 1) goto unsupported; /* Ask about CPU features. */ if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) goto unsupported; /* Return the relevant feature bit. */ return ((edx & CPUID_SSE2_BIT) ? 1 : 0); unsupported: #endif /* Not supported. */ return (0); } tarsnap-1.0.41/libcperciva/cpusupport/cpusupport_x86_ssse3.c000066400000000000000000000011361476705112600242030ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_CPUID #include #define CPUID_SSSE3_BIT (1 << 9) #endif CPUSUPPORT_FEATURE_DECL(x86, ssse3) { #ifdef CPUSUPPORT_X86_CPUID unsigned int eax, ebx, ecx, edx; /* Check if CPUID supports the level we need. */ if (!__get_cpuid(0, &eax, &ebx, &ecx, &edx)) goto unsupported; if (eax < 1) goto unsupported; /* Ask about CPU features. */ if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) goto unsupported; /* Return the relevant feature bit. */ return ((ecx & CPUID_SSSE3_BIT) ? 1 : 0); unsupported: #endif /* Not supported. */ return (0); } tarsnap-1.0.41/libcperciva/crypto/000077500000000000000000000000001476705112600170615ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/crypto/crypto_aes.c000066400000000000000000000171531476705112600214040ustar00rootroot00000000000000/** * APISUPPORT CFLAGS: LIBCRYPTO_LOW_LEVEL_AES */ #include #include #include #include #include #include "cpusupport.h" #include "crypto_aes_aesni.h" #include "crypto_aes_arm.h" #include "insecure_memzero.h" #include "warnp.h" #include "crypto_aes.h" #if defined(CPUSUPPORT_X86_AESNI) || defined(CPUSUPPORT_ARM_AES) #define HWACCEL static enum { HW_SOFTWARE = 0, #if defined(CPUSUPPORT_X86_AESNI) HW_X86_AESNI, #endif #if defined(CPUSUPPORT_ARM_AES) HW_ARM_AES, #endif HW_UNSET } hwaccel = HW_UNSET; #endif /** * This represents either an AES_KEY or a struct crypto_aes_key_aesni; we * know which it is based on whether we're using AESNI code or not. As such, * it's just an opaque pointer; but declaring it as a named structure type * prevents type-mismatch bugs in upstream code. */ struct crypto_aes_key; #ifdef HWACCEL static struct aes_test { const uint8_t key[32]; const size_t len; const uint8_t ptext[16]; const uint8_t ctext[16]; } testcases[] = { { /* NIST FIPS 179, Appendix C - Example Vectors, AES-128, p. 35. */ .key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, .len = 16, .ptext = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, .ctext = { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a } }, { /* NIST FIPS 179, Appendix C - Example Vectors, AES-256, p. 42. */ .key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, }, .len = 32, .ptext = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, .ctext = { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 } } }; /* Test a function against test vectors. */ static int functest(int (* func)(const uint8_t *, size_t, const uint8_t[16], uint8_t[16])) { struct aes_test * knowngood; uint8_t ctext[16]; size_t i; for (i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) { knowngood = &testcases[i]; /* Sanity-check. */ assert((knowngood->len == 16) || (knowngood->len == 32)); /* Expand the key and encrypt with the provided function. */ if (func(knowngood->key, knowngood->len, knowngood->ptext, ctext)) goto err0; /* Does the output match the known good value? */ if (memcmp(knowngood->ctext, ctext, 16)) goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } #if defined(CPUSUPPORT_X86_AESNI) static int x86_aesni_oneshot(const uint8_t * key_unexpanded, size_t len, const uint8_t ptext[16], uint8_t ctext[16]) { void * kexp_hw; /* Expand the key and encrypt with hardware intrinsics. */ if ((kexp_hw = crypto_aes_key_expand_aesni(key_unexpanded, len)) == NULL) goto err0; crypto_aes_encrypt_block_aesni(ptext, ctext, kexp_hw); crypto_aes_key_free_aesni(kexp_hw); /* Success! */ return (0); err0: /* Failure! */ return (-1); } #endif #if defined(CPUSUPPORT_ARM_AES) static int arm_aes_oneshot(const uint8_t * key_unexpanded, size_t len, const uint8_t ptext[16], uint8_t * ctext) { void * kexp_hw; if ((kexp_hw = crypto_aes_key_expand_arm(key_unexpanded, len)) == NULL) goto err0; crypto_aes_encrypt_block_arm(ptext, ctext, kexp_hw); crypto_aes_key_free_arm(kexp_hw); /* Success! */ return (0); err0: /* Failure! */ return (-1); } #endif static int openssl_oneshot(const uint8_t * key_unexpanded, size_t len, const uint8_t ptext[16], uint8_t * ctext) { AES_KEY kexp_actual; AES_KEY * kexp = &kexp_actual; /* Expand the key, encrypt, and clean up. */ if (AES_set_encrypt_key(key_unexpanded, (int)(len * 8), kexp) != 0) goto err0; AES_encrypt(ptext, ctext, kexp); insecure_memzero(kexp, sizeof(AES_KEY)); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Which type of hardware acceleration should we use, if any? */ static void hwaccel_init(void) { /* If we've already set hwaccel, we're finished. */ if (hwaccel != HW_UNSET) return; /* Default to software. */ hwaccel = HW_SOFTWARE; #if defined(CPUSUPPORT_X86_AESNI) CPUSUPPORT_VALIDATE(hwaccel, HW_X86_AESNI, cpusupport_x86_aesni(), functest(x86_aesni_oneshot)); #endif #if defined(CPUSUPPORT_ARM_AES) CPUSUPPORT_VALIDATE(hwaccel, HW_ARM_AES, cpusupport_arm_aes(), functest(arm_aes_oneshot)); #endif /* * If we're here, we're not using any intrinsics. Test OpenSSL; if * there's an error, print a warning and abort. */ if (functest(openssl_oneshot)) { warn0("OpenSSL gives incorrect AES values."); abort(); } } #endif /* HWACCEL */ /** * crypto_aes_can_use_intrinsics(void): * Test whether hardware intrinsics are safe to use. Return 1 if x86 AESNI * operations are available, 2 if ARM-AES operations are available, or 0 if * none are available. */ int crypto_aes_can_use_intrinsics(void) { #ifdef HWACCEL /* Ensure that we've chosen the type of hardware acceleration. */ hwaccel_init(); #if defined(CPUSUPPORT_X86_AESNI) if (hwaccel == HW_X86_AESNI) return (1); #endif #if defined(CPUSUPPORT_ARM_AES) if (hwaccel == HW_ARM_AES) return (2); #endif #endif /* HWACCEL */ /* Software only. */ return (0); } /** * crypto_aes_key_expand(key_unexpanded, len): * Expand the ${len}-byte unexpanded AES key ${key_unexpanded} into a * structure which can be passed to crypto_aes_encrypt_block(). The length * must be 16 or 32. */ struct crypto_aes_key * crypto_aes_key_expand(const uint8_t * key_unexpanded, size_t len) { AES_KEY * kexp; /* Sanity-check. */ assert((len == 16) || (len == 32)); #ifdef HWACCEL /* Ensure that we've chosen the type of hardware acceleration. */ hwaccel_init(); #ifdef CPUSUPPORT_X86_AESNI if (hwaccel == HW_X86_AESNI) return (crypto_aes_key_expand_aesni(key_unexpanded, len)); #endif #ifdef CPUSUPPORT_ARM_AES if (hwaccel == HW_ARM_AES) return (crypto_aes_key_expand_arm(key_unexpanded, len)); #endif #endif /* HWACCEL */ /* Allocate structure. */ if ((kexp = malloc(sizeof(AES_KEY))) == NULL) goto err0; /* Expand the key. */ if (AES_set_encrypt_key(key_unexpanded, (int)(len * 8), kexp) != 0) goto err1; /* Success! */ return ((void *)kexp); err1: free(kexp); err0: /* Failure! */ return (NULL); } /** * crypto_aes_encrypt_block(in, out, key): * Using the expanded AES key ${key}, encrypt the block ${in} and write the * resulting ciphertext to ${out}. ${in} and ${out} can overlap. */ void crypto_aes_encrypt_block(const uint8_t in[16], uint8_t out[16], const struct crypto_aes_key * key) { #ifdef HWACCEL #ifdef CPUSUPPORT_X86_AESNI if (hwaccel == HW_X86_AESNI) { crypto_aes_encrypt_block_aesni(in, out, (const void *)key); return; } #endif #ifdef CPUSUPPORT_ARM_AES if (hwaccel == HW_ARM_AES) { crypto_aes_encrypt_block_arm(in, out, (const void *)key); return; } #endif #endif /* HWACCEL */ /* Get AES to do the work. */ AES_encrypt(in, out, (const void *)key); } /** * crypto_aes_key_free(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free(struct crypto_aes_key * key) { #ifdef HWACCEL #ifdef CPUSUPPORT_X86_AESNI if (hwaccel == HW_X86_AESNI) { crypto_aes_key_free_aesni((void *)key); return; } #endif #ifdef CPUSUPPORT_ARM_AES if (hwaccel == HW_ARM_AES) { crypto_aes_key_free_arm((void *)key); return; } #endif #endif /* HWACCEL */ /* Behave consistently with free(NULL). */ if (key == NULL) return; /* Attempt to zero the expanded key. */ insecure_memzero(key, sizeof(AES_KEY)); /* Free the key. */ free(key); } tarsnap-1.0.41/libcperciva/crypto/crypto_aes.h000066400000000000000000000021641476705112600214050ustar00rootroot00000000000000#ifndef CRYPTO_AES_H_ #define CRYPTO_AES_H_ #include #include /* Opaque structure. */ struct crypto_aes_key; /** * crypto_aes_can_use_intrinsics(void): * Test whether hardware intrinsics are safe to use. Return 1 if x86 AESNI * operations are available, 2 if ARM-AES operations are available, or 0 if * none are available. */ int crypto_aes_can_use_intrinsics(void); /** * crypto_aes_key_expand(key_unexpanded, len): * Expand the ${len}-byte unexpanded AES key ${key_unexpanded} into a * structure which can be passed to crypto_aes_encrypt_block(). The length * must be 16 or 32. */ struct crypto_aes_key * crypto_aes_key_expand(const uint8_t *, size_t); /** * crypto_aes_encrypt_block(in, out, key): * Using the expanded AES key ${key}, encrypt the block ${in} and write the * resulting ciphertext to ${out}. ${in} and ${out} can overlap. */ void crypto_aes_encrypt_block(const uint8_t[16], uint8_t[16], const struct crypto_aes_key *); /** * crypto_aes_key_free(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free(struct crypto_aes_key *); #endif /* !CRYPTO_AES_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aes_aesni.c000066400000000000000000000205351476705112600225610ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_AESNI /** * CPUSUPPORT CFLAGS: X86_AESNI */ #include #include #include #include "align_ptr.h" #include "insecure_memzero.h" #include "warnp.h" #include "crypto_aes_aesni.h" #include "crypto_aes_aesni_m128i.h" /* Expanded-key structure. */ struct crypto_aes_key_aesni { ALIGN_PTR_DECL(__m128i, rkeys, 15, sizeof(__m128i)); size_t nr; }; /* Compute an AES-128 round key. */ #define MKRKEY128(rkeys, i, rcon) do { \ __m128i _s = rkeys[i - 1]; \ __m128i _t = rkeys[i - 1]; \ _s = _mm_xor_si128(_s, _mm_slli_si128(_s, 4)); \ _s = _mm_xor_si128(_s, _mm_slli_si128(_s, 8)); \ _t = _mm_aeskeygenassist_si128(_t, rcon); \ _t = _mm_shuffle_epi32(_t, 0xff); \ rkeys[i] = _mm_xor_si128(_s, _t); \ } while (0) /** * crypto_aes_key_expand_128_aesni(key_unexpanded, rkeys): * Expand the 128-bit AES unexpanded key ${key_unexpanded} into the 11 round * keys ${rkeys}. This implementation uses x86 AESNI instructions, and should * only be used if CPUSUPPORT_X86_AESNI is defined and cpusupport_x86_aesni() * returns nonzero. */ static void crypto_aes_key_expand_128_aesni(const uint8_t key_unexpanded[16], __m128i rkeys[11]) { /* The first round key is just the key. */ /*- * XXX Compiler breakage: * The intrinsic defined by Intel for _mm_loadu_si128 defines it as * taking a (const __m128i *) parameter. This forces us to write a * bug: The cast to (const __m128i *) is invalid since it increases * the alignment requirement of the pointer. Alas, until compilers * get fixed intrinsics, all we can do is code the bug and require * that alignment-requirement-increasing compiler warnings get * disabled. */ rkeys[0] = _mm_loadu_si128((const __m128i *)&key_unexpanded[0]); /* * Each of the remaining round keys are computed from the preceding * round key: rotword+subword+rcon (provided as aeskeygenassist) to * compute the 'temp' value, then xor with 1, 2, 3, or all 4 of the * 32-bit words from the preceding round key. Unfortunately, 'rcon' * is encoded as an immediate value, so we need to write the loop out * ourselves rather than allowing the compiler to expand it. */ MKRKEY128(rkeys, 1, 0x01); MKRKEY128(rkeys, 2, 0x02); MKRKEY128(rkeys, 3, 0x04); MKRKEY128(rkeys, 4, 0x08); MKRKEY128(rkeys, 5, 0x10); MKRKEY128(rkeys, 6, 0x20); MKRKEY128(rkeys, 7, 0x40); MKRKEY128(rkeys, 8, 0x80); MKRKEY128(rkeys, 9, 0x1b); MKRKEY128(rkeys, 10, 0x36); } /* Compute an AES-256 round key. */ #define MKRKEY256(rkeys, i, shuffle, rcon) do { \ __m128i _s = rkeys[i - 2]; \ __m128i _t = rkeys[i - 1]; \ _s = _mm_xor_si128(_s, _mm_slli_si128(_s, 4)); \ _s = _mm_xor_si128(_s, _mm_slli_si128(_s, 8)); \ _t = _mm_aeskeygenassist_si128(_t, rcon); \ _t = _mm_shuffle_epi32(_t, shuffle); \ rkeys[i] = _mm_xor_si128(_s, _t); \ } while (0) /** * crypto_aes_key_expand_256_aesni(key_unexpanded, rkeys): * Expand the 256-bit unexpanded AES key ${key_unexpanded} into the 15 round * keys ${rkeys}. This implementation uses x86 AESNI instructions, and should * only be used if CPUSUPPORT_X86_AESNI is defined and cpusupport_x86_aesni() * returns nonzero. */ static void crypto_aes_key_expand_256_aesni(const uint8_t key_unexpanded[32], __m128i rkeys[15]) { /* The first two round keys are just the key. */ /*- * XXX Compiler breakage: * The intrinsic defined by Intel for _mm_loadu_si128 defines it as * taking a (const __m128i *) parameter. This forces us to write a * bug: The cast to (const __m128i *) is invalid since it increases * the alignment requirement of the pointer. Alas, until compilers * get fixed intrinsics, all we can do is code the bug and require * that alignment-requirement-increasing compiler warnings get * disabled. */ rkeys[0] = _mm_loadu_si128((const __m128i *)&key_unexpanded[0]); rkeys[1] = _mm_loadu_si128((const __m128i *)&key_unexpanded[16]); /* * Each of the remaining round keys are computed from the preceding * pair of keys. Even rounds use rotword+subword+rcon, while odd * rounds just use subword; the aeskeygenassist instruction computes * both, and we use 0xff or 0xaa to select the one we need. The rcon * value used is irrelevant for odd rounds since we ignore the value * which it feeds into. Unfortunately, the 'shuffle' and 'rcon' * values are encoded into the instructions as immediates, so we need * to write the loop out ourselves rather than allowing the compiler * to expand it. */ MKRKEY256(rkeys, 2, 0xff, 0x01); MKRKEY256(rkeys, 3, 0xaa, 0x00); MKRKEY256(rkeys, 4, 0xff, 0x02); MKRKEY256(rkeys, 5, 0xaa, 0x00); MKRKEY256(rkeys, 6, 0xff, 0x04); MKRKEY256(rkeys, 7, 0xaa, 0x00); MKRKEY256(rkeys, 8, 0xff, 0x08); MKRKEY256(rkeys, 9, 0xaa, 0x00); MKRKEY256(rkeys, 10, 0xff, 0x10); MKRKEY256(rkeys, 11, 0xaa, 0x00); MKRKEY256(rkeys, 12, 0xff, 0x20); MKRKEY256(rkeys, 13, 0xaa, 0x00); MKRKEY256(rkeys, 14, 0xff, 0x40); } /** * crypto_aes_key_expand_aesni(key_unexpanded, len): * Expand the ${len}-byte unexpanded AES key ${key_unexpanded} into a * structure which can be passed to crypto_aes_encrypt_block_aesni(). The * length must be 16 or 32. This implementation uses x86 AESNI instructions, * and should only be used if CPUSUPPORT_X86_AESNI is defined and * cpusupport_x86_aesni() returns nonzero. */ void * crypto_aes_key_expand_aesni(const uint8_t * key_unexpanded, size_t len) { struct crypto_aes_key_aesni * kexp; /* Allocate structure. */ if ((kexp = malloc(sizeof(struct crypto_aes_key_aesni))) == NULL) goto err0; /* Figure out where to put the round keys. */ ALIGN_PTR_INIT(kexp->rkeys, sizeof(__m128i)); /* Compute round keys. */ if (len == 16) { kexp->nr = 10; crypto_aes_key_expand_128_aesni(key_unexpanded, kexp->rkeys); } else if (len == 32) { kexp->nr = 14; crypto_aes_key_expand_256_aesni(key_unexpanded, kexp->rkeys); } else { warn0("Unsupported AES key length: %zu bytes", len); goto err1; } /* Success! */ return (kexp); err1: free(kexp); err0: /* Failure! */ return (NULL); } /** * crypto_aes_encrypt_block_aesni_m128i(in, key): * Using the expanded AES key ${key}, encrypt the block ${in} and return the * resulting ciphertext. This implementation uses x86 AESNI instructions, * and should only be used if CPUSUPPORT_X86_AESNI is defined and * cpusupport_x86_aesni() returns nonzero. */ __m128i crypto_aes_encrypt_block_aesni_m128i(__m128i in, const void * key) { const struct crypto_aes_key_aesni * _key = key; const __m128i * aes_key = _key->rkeys; __m128i aes_state = in; size_t nr = _key->nr; aes_state = _mm_xor_si128(aes_state, aes_key[0]); aes_state = _mm_aesenc_si128(aes_state, aes_key[1]); aes_state = _mm_aesenc_si128(aes_state, aes_key[2]); aes_state = _mm_aesenc_si128(aes_state, aes_key[3]); aes_state = _mm_aesenc_si128(aes_state, aes_key[4]); aes_state = _mm_aesenc_si128(aes_state, aes_key[5]); aes_state = _mm_aesenc_si128(aes_state, aes_key[6]); aes_state = _mm_aesenc_si128(aes_state, aes_key[7]); aes_state = _mm_aesenc_si128(aes_state, aes_key[8]); aes_state = _mm_aesenc_si128(aes_state, aes_key[9]); if (nr > 10) { aes_state = _mm_aesenc_si128(aes_state, aes_key[10]); aes_state = _mm_aesenc_si128(aes_state, aes_key[11]); aes_state = _mm_aesenc_si128(aes_state, aes_key[12]); aes_state = _mm_aesenc_si128(aes_state, aes_key[13]); } aes_state = _mm_aesenclast_si128(aes_state, aes_key[nr]); return (aes_state); } /** * crypto_aes_encrypt_block_aesni(in, out, key): * Using the expanded AES key ${key}, encrypt the block ${in} and write the * resulting ciphertext to ${out}. ${in} and ${out} can overlap. This * implementation uses x86 AESNI instructions, and should only be used if * CPUSUPPORT_X86_AESNI is defined and cpusupport_x86_aesni() returns nonzero. */ void crypto_aes_encrypt_block_aesni(const uint8_t in[16], uint8_t out[16], const void * key) { __m128i aes_state; aes_state = _mm_loadu_si128((const __m128i *)in); aes_state = crypto_aes_encrypt_block_aesni_m128i(aes_state, key); _mm_storeu_si128((__m128i *)out, aes_state); } /** * crypto_aes_key_free_aesni(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free_aesni(void * key) { /* Behave consistently with free(NULL). */ if (key == NULL) return; /* Attempt to zero the expanded key. */ insecure_memzero(key, sizeof(struct crypto_aes_key_aesni)); /* Free the key. */ free(key); } #endif /* CPUSUPPORT_X86_AESNI */ tarsnap-1.0.41/libcperciva/crypto/crypto_aes_aesni.h000066400000000000000000000022021476705112600225550ustar00rootroot00000000000000#ifndef CRYPTO_AES_AESNI_H_ #define CRYPTO_AES_AESNI_H_ #include #include /** * crypto_aes_key_expand_aesni(key_unexpanded, len): * Expand the ${len}-byte unexpanded AES key ${key_unexpanded} into a * structure which can be passed to crypto_aes_encrypt_block_aesni(). The * length must be 16 or 32. This implementation uses x86 AESNI instructions, * and should only be used if CPUSUPPORT_X86_AESNI is defined and * cpusupport_x86_aesni() returns nonzero. */ void * crypto_aes_key_expand_aesni(const uint8_t *, size_t); /** * crypto_aes_encrypt_block_aesni(in, out, key): * Using the expanded AES key ${key}, encrypt the block ${in} and write the * resulting ciphertext to ${out}. ${in} and ${out} can overlap. This * implementation uses x86 AESNI instructions, and should only be used if * CPUSUPPORT_X86_AESNI is defined and cpusupport_x86_aesni() returns nonzero. */ void crypto_aes_encrypt_block_aesni(const uint8_t[16], uint8_t[16], const void *); /** * crypto_aes_key_free_aesni(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free_aesni(void *); #endif /* !CRYPTO_AES_AESNI_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aes_aesni_m128i.h000066400000000000000000000010121476705112600234730ustar00rootroot00000000000000#ifndef CRYPTO_AES_AESNI_M128I_H_ #define CRYPTO_AES_AESNI_M128I_H_ #include /** * crypto_aes_encrypt_block_aesni_m128i(in, key): * Using the expanded AES key ${key}, encrypt the block ${in} and return the * resulting ciphertext. This implementation uses x86 AESNI instructions, * and should only be used if CPUSUPPORT_X86_AESNI is defined and * cpusupport_x86_aesni() returns nonzero. */ __m128i crypto_aes_encrypt_block_aesni_m128i(__m128i, const void *); #endif /* !CRYPTO_AES_AESNI_M128I_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aes_arm.c000066400000000000000000000221621476705112600222370ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_ARM_AES /** * CPUSUPPORT CFLAGS: ARM_AES */ #include #include #ifdef __ARM_NEON #include #endif #include "align_ptr.h" #include "insecure_memzero.h" #include "warnp.h" #include "crypto_aes_arm.h" #include "crypto_aes_arm_u8.h" /* Expanded-key structure. */ struct crypto_aes_key_arm { ALIGN_PTR_DECL(uint8x16_t, rkeys, 15, sizeof(uint8x16_t)); size_t nr; }; /** * vdupq_laneq_u32_u8(a, lane): * Set all 32-bit vector lanes to the same value. Exactly the same as * vdupq_laneq_u32(), except that accepts (and returns) uint8x16_t. */ #define vdupq_laneq_u32_u8(a, lane) \ vreinterpretq_u8_u32(vdupq_laneq_u32(vreinterpretq_u32_u8(a), lane)) /** * vshlq_n_u128(a, n): * Shift left (immediate), applied to the whole vector at once. * * Implementation note: this concatenates ${a} with a vector containing zeros, * then extracts a new vector from the pair (similar to a sliding window). * For example, vshlq_n_u128(a, 3) would do: * 0xaaaaaaaaaaaaaaaa0000000000000000 * return: ~~~~~~~~~~~~~~~~ * This is the recommended method of shifting an entire vector with Neon * intrinsics; all of the built-in shift instructions operate on multiple * values (such as a pair of 64-bit values). */ #define vshlq_n_u128(a, n) vextq_u8(vdupq_n_u8(0), a, 16 - n) /** * SubWord_duplicate(a): * Perform the AES SubWord operation on the final 32-bit word (bits 96..127) * of ${a}, and return a vector consisting of that value copied to all lanes. */ static inline uint8x16_t SubWord_duplicate(uint8x16_t a) { /* * Duplicate the final 32-bit word in all other lanes. By having four * copies of the same uint32_t, we cause the ShiftRows in the upcoming * AESE to have no effect. */ a = vdupq_laneq_u32_u8(a, 3); /* AESE does AddRoundKey (nop), ShiftRows (nop), and SubBytes. */ a = vaeseq_u8(a, vdupq_n_u8(0)); return (a); } /** * SubWord_RotWord_XOR_duplicate(a, rcon): * Perform the AES key schedule operations of SubWord, RotWord, and XOR with * ${rcon}, acting on the final 32-bit word (bits 96..127) of ${a}, and return * a vector consisting of that value copied to all lanes. */ static inline uint8x16_t SubWord_RotWord_XOR_duplicate(uint8x16_t a, const uint32_t rcon) { uint32_t x3; /* Perform SubWord on the final 32-bit word and copy it to all lanes. */ a = SubWord_duplicate(a); /* We'll use non-neon for the rest. */ x3 = vgetq_lane_u32(vreinterpretq_u32_u8(a), 0); /*- * x3 gets RotWord. Note that * RotWord(SubWord(a)) == SubWord(RotWord(a)) */ x3 = (x3 >> 8) | (x3 << (32 - 8)); /* x3 gets XOR'd with rcon. */ x3 = x3 ^ rcon; /* Copy x3 to all 128 bits, and convert it to a uint8x16_t. */ return (vreinterpretq_u8_u32(vdupq_n_u32(x3))); } /* Compute an AES-128 round key. */ #define MKRKEY128(rkeys, i, rcon) do { \ uint8x16_t _s = rkeys[i - 1]; \ uint8x16_t _t = rkeys[i - 1]; \ _s = veorq_u8(_s, vshlq_n_u128(_s, 4)); \ _s = veorq_u8(_s, vshlq_n_u128(_s, 8)); \ _t = SubWord_RotWord_XOR_duplicate(_t, rcon); \ rkeys[i] = veorq_u8(_s, _t); \ } while (0) /** * crypto_aes_key_expand_128_arm(key_unexpanded, rkeys): * Expand the 128-bit unexpanded AES key ${key_unexpanded} into the 11 round * keys ${rkeys}. This implementation uses ARM AES instructions, and should * only be used if CPUSUPPORT_ARM_AES is defined and cpusupport_arm_aes() * returns nonzero. */ static void crypto_aes_key_expand_128_arm(const uint8_t key_unexpanded[16], uint8x16_t rkeys[11]) { /* The first round key is just the key. */ rkeys[0] = vld1q_u8(&key_unexpanded[0]); /* * Each of the remaining round keys are computed from the preceding * round key: rotword+subword+rcon (provided as aeskeygenassist) to * compute the 'temp' value, then xor with 1, 2, 3, or all 4 of the * 32-bit words from the preceding round key. */ MKRKEY128(rkeys, 1, 0x01); MKRKEY128(rkeys, 2, 0x02); MKRKEY128(rkeys, 3, 0x04); MKRKEY128(rkeys, 4, 0x08); MKRKEY128(rkeys, 5, 0x10); MKRKEY128(rkeys, 6, 0x20); MKRKEY128(rkeys, 7, 0x40); MKRKEY128(rkeys, 8, 0x80); MKRKEY128(rkeys, 9, 0x1b); MKRKEY128(rkeys, 10, 0x36); } /* Compute an AES-256 round key. */ #define MKRKEY256(rkeys, i, rcon) do { \ uint8x16_t _s = rkeys[i - 2]; \ uint8x16_t _t = rkeys[i - 1]; \ _s = veorq_u8(_s, vshlq_n_u128(_s, 4)); \ _s = veorq_u8(_s, vshlq_n_u128(_s, 8)); \ _t = (i % 2 == 1) ? \ SubWord_duplicate(_t) : \ SubWord_RotWord_XOR_duplicate(_t, rcon); \ rkeys[i] = veorq_u8(_s, _t); \ } while (0) /** * crypto_aes_key_expand_256_arm(key_unexpanded, rkeys): * Expand the 256-bit unexpanded AES key ${key_unexpanded} into the 15 round * keys ${rkeys}. This implementation uses ARM AES instructions, and should * only be used if CPUSUPPORT_ARM_AES is defined and cpusupport_arm_aes() * returns nonzero. */ static void crypto_aes_key_expand_256_arm(const uint8_t key_unexpanded[32], uint8x16_t rkeys[15]) { /* The first two round keys are just the key. */ rkeys[0] = vld1q_u8(&key_unexpanded[0]); rkeys[1] = vld1q_u8(&key_unexpanded[16]); /* * Each of the remaining round keys are computed from the preceding * pair of keys. Even rounds use rotword+subword+rcon, while odd * rounds just use subword. The rcon value used is irrelevant for odd * rounds since we ignore the value which it feeds into. */ MKRKEY256(rkeys, 2, 0x01); MKRKEY256(rkeys, 3, 0x00); MKRKEY256(rkeys, 4, 0x02); MKRKEY256(rkeys, 5, 0x00); MKRKEY256(rkeys, 6, 0x04); MKRKEY256(rkeys, 7, 0x00); MKRKEY256(rkeys, 8, 0x08); MKRKEY256(rkeys, 9, 0x00); MKRKEY256(rkeys, 10, 0x10); MKRKEY256(rkeys, 11, 0x00); MKRKEY256(rkeys, 12, 0x20); MKRKEY256(rkeys, 13, 0x00); MKRKEY256(rkeys, 14, 0x40); } /** * crypto_aes_key_expand_arm(key_unexpanded, len): * Expand the ${len}-byte unexpanded AES key ${key_unexpanded} into a * structure which can be passed to crypto_aes_encrypt_block_arm(). The * length must be 16 or 32. This implementation uses ARM AES instructions, * and should only be used if CPUSUPPORT_ARM_AES is defined and * cpusupport_arm_aes() returns nonzero. */ void * crypto_aes_key_expand_arm(const uint8_t * key_unexpanded, size_t len) { struct crypto_aes_key_arm * kexp; /* Allocate structure. */ if ((kexp = malloc(sizeof(struct crypto_aes_key_arm))) == NULL) goto err0; /* Figure out where to put the round keys. */ ALIGN_PTR_INIT(kexp->rkeys, sizeof(uint8x16_t)); /* Compute round keys. */ if (len == 16) { kexp->nr = 10; crypto_aes_key_expand_128_arm(key_unexpanded, kexp->rkeys); } else if (len == 32) { kexp->nr = 14; crypto_aes_key_expand_256_arm(key_unexpanded, kexp->rkeys); } else { warn0("Unsupported AES key length: %zu bytes", len); goto err1; } /* Success! */ return (kexp); err1: free(kexp); err0: /* Failure! */ return (NULL); } /** * crypto_aes_encrypt_block_arm_u8(in, key): * Using the expanded AES key ${key}, encrypt the block ${in} and return the * resulting ciphertext. This implementation uses ARM AES instructions, * and should only be used if CPUSUPPORT_ARM_AES is defined and * cpusupport_arm_aes() returns nonzero. */ uint8x16_t crypto_aes_encrypt_block_arm_u8(uint8x16_t in, const void * key) { const struct crypto_aes_key_arm * _key = key; const uint8x16_t * aes_key = _key->rkeys; uint8x16_t aes_state = in; size_t nr = _key->nr; aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[0])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[1])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[2])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[3])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[4])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[5])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[6])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[7])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[8])); if (nr > 10) { aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[9])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[10])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[11])); aes_state = vaesmcq_u8(vaeseq_u8(aes_state, aes_key[12])); } /* Last round. */ aes_state = vaeseq_u8(aes_state, aes_key[nr - 1]); aes_state = veorq_u8(aes_state, aes_key[nr]); return (aes_state); } /** * crypto_aes_encrypt_block_arm(in, out, key): * Using the expanded AES key ${key}, encrypt the block ${in} and write the * resulting ciphertext to ${out}. ${in} and ${out} can overlap. This * implementation uses ARM AES instructions, and should only be used if * CPUSUPPORT_ARM_AES is defined and cpusupport_arm_aes() returns nonzero. */ void crypto_aes_encrypt_block_arm(const uint8_t in[16], uint8_t out[16], const void * key) { uint8x16_t aes_state; aes_state = vld1q_u8(in); aes_state = crypto_aes_encrypt_block_arm_u8(aes_state, key); vst1q_u8(out, aes_state); } /** * crypto_aes_key_free_arm(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free_arm(void * key) { /* Behave consistently with free(NULL). */ if (key == NULL) return; /* Attempt to zero the expanded key. */ insecure_memzero(key, sizeof(struct crypto_aes_key_arm)); /* Free the key. */ free(key); } #endif /* CPUSUPPORT_ARM_AES */ tarsnap-1.0.41/libcperciva/crypto/crypto_aes_arm.h000066400000000000000000000021421476705112600222400ustar00rootroot00000000000000#ifndef CRYPTO_AES_ARM_H_ #define CRYPTO_AES_ARM_H_ #include #include /** * crypto_aes_key_expand_arm(key_unexpanded, len): * Expand the ${len}-byte unexpanded AES key ${key_unexpanded} into a * structure which can be passed to crypto_aes_encrypt_block_arm(). The * length must be 16 or 32. This implementation uses ARM AES instructions, * and should only be used if CPUSUPPORT_ARM_AES is defined and * cpusupport_arm_aes() returns nonzero. */ void * crypto_aes_key_expand_arm(const uint8_t *, size_t); /** * crypto_aes_encrypt_block_arm(in, out, key): * Using the expanded AES key ${key}, encrypt the block ${in} and write the * resulting ciphertext to ${out}. ${in} and ${out} can overlap. This * implementation uses ARM AES instructions, and should only be used if * CPUSUPPORT_ARM_AES is defined and cpusupport_arm_aes() returns nonzero. */ void crypto_aes_encrypt_block_arm(const uint8_t[16], uint8_t[16], const void *); /** * crypto_aes_key_free_arm(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free_arm(void *); #endif /* !CRYPTO_AES_ARM_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aes_arm_u8.h000066400000000000000000000010111476705112600226460ustar00rootroot00000000000000#ifndef CRYPTO_AES_ARM_U8_H_ #define CRYPTO_AES_ARM_U8_H_ #ifdef __ARM_NEON #include #endif /** * crypto_aes_encrypt_block_arm_u8(in, key): * Using the expanded AES key ${key}, encrypt the block ${in} and return the * resulting ciphertext. This implementation uses ARM AES instructions, * and should only be used if CPUSUPPORT_ARM_AES is defined and * cpusupport_arm_aes() returns nonzero. */ uint8x16_t crypto_aes_encrypt_block_arm_u8(uint8x16_t, const void *); #endif /* !CRYPTO_AES_ARM_U8_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr.c000066400000000000000000000135321476705112600221120ustar00rootroot00000000000000#include #include #include #include "cpusupport.h" #include "crypto_aes.h" #include "crypto_aesctr_aesni.h" #include "crypto_aesctr_arm.h" #include "insecure_memzero.h" #include "sysendian.h" #include "crypto_aesctr.h" /** * In order to optimize AES-CTR, it is desirable to separate out the handling * of individual bytes of data vs. the handling of complete (16 byte) blocks. * The handling of blocks in turn can be optimized further using CPU * intrinsics, e.g. SSE2 on x86 CPUs; however while the byte-at-once code * remains the same across platforms it should be inlined into the same (CPU * feature specific) routines for performance reasons. * * In order to allow those generic functions to be inlined into multiple * functions in separate translation units, we place them into a "shared" C * file which is included in each of the platform-specific variants. */ #include "crypto_aesctr_shared.c" #if defined(CPUSUPPORT_X86_AESNI) || defined(CPUSUPPORT_ARM_AES) #define HWACCEL static enum { HW_SOFTWARE = 0, #if defined(CPUSUPPORT_X86_AESNI) HW_X86_AESNI, #endif #if defined(CPUSUPPORT_ARM_AES) HW_ARM_AES, #endif HW_UNSET } hwaccel = HW_UNSET; #endif #ifdef HWACCEL /* Which type of hardware acceleration should we use, if any? */ static void hwaccel_init(void) { /* If we've already set hwaccel, we're finished. */ if (hwaccel != HW_UNSET) return; /* Default to software. */ hwaccel = HW_SOFTWARE; /* Can we use AESNI? */ switch (crypto_aes_can_use_intrinsics()) { #ifdef CPUSUPPORT_X86_AESNI case 1: hwaccel = HW_X86_AESNI; break; #endif #ifdef CPUSUPPORT_ARM_AES case 2: hwaccel = HW_ARM_AES; break; #endif case 0: break; default: /* Should never happen. */ assert(0); } } #endif /* HWACCEL */ /** * crypto_aesctr_alloc(void): * Allocate an object for performing AES in CTR code. This must be followed * by calling _init2(). */ struct crypto_aesctr * crypto_aesctr_alloc(void) { struct crypto_aesctr * stream; /* Allocate memory. */ if ((stream = malloc(sizeof(struct crypto_aesctr))) == NULL) goto err0; /* Success! */ return (stream); err0: /* Failure! */ return (NULL); } /** * crypto_aesctr_init2(stream, key, nonce): * Reset the AES-CTR stream ${stream}, using the ${key} and ${nonce}. If ${key} * is NULL, retain the previous AES key. */ void crypto_aesctr_init2(struct crypto_aesctr * stream, const struct crypto_aes_key * key, uint64_t nonce) { /* If the key is NULL, retain the previous AES key. */ if (key != NULL) stream->key = key; /* Set nonce as provided and reset bytectr. */ be64enc(stream->pblk, nonce); stream->bytectr = 0; /* * Set the counter such that the least significant byte will wrap once * incremented. */ stream->pblk[15] = 0xff; #ifdef HWACCEL hwaccel_init(); #endif /* Sanity check. */ assert(stream->key != NULL); } /** * crypto_aesctr_init(key, nonce): * Prepare to encrypt/decrypt data with AES in CTR mode, using the provided * expanded ${key} and ${nonce}. The key provided must remain valid for the * lifetime of the stream. This is the same as calling _alloc() followed by * _init2(). */ struct crypto_aesctr * crypto_aesctr_init(const struct crypto_aes_key * key, uint64_t nonce) { struct crypto_aesctr * stream; /* Sanity check. */ assert(key != NULL); /* Allocate memory. */ if ((stream = crypto_aesctr_alloc()) == NULL) goto err0; /* Initialize values. */ crypto_aesctr_init2(stream, key, nonce); /* Success! */ return (stream); err0: /* Failure! */ return (NULL); } /** * crypto_aesctr_stream(stream, inbuf, outbuf, buflen): * Generate the next ${buflen} bytes of the AES-CTR stream ${stream} and xor * them with bytes from ${inbuf}, writing the result into ${outbuf}. If the * buffers ${inbuf} and ${outbuf} overlap, they must be identical. */ void crypto_aesctr_stream(struct crypto_aesctr * stream, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { #if defined(HWACCEL) #if defined(CPUSUPPORT_X86_AESNI) if ((buflen >= 16) && (hwaccel == HW_X86_AESNI)) { crypto_aesctr_aesni_stream(stream, inbuf, outbuf, buflen); return; } #endif #if defined(CPUSUPPORT_ARM_AES) if ((buflen >= 16) && (hwaccel == HW_ARM_AES)) { crypto_aesctr_arm_stream(stream, inbuf, outbuf, buflen); return; } #endif #endif /* HWACCEL */ /* Process any bytes before we can process a whole block. */ if (crypto_aesctr_stream_pre_wholeblock(stream, &inbuf, &outbuf, &buflen)) return; /* Process whole blocks of 16 bytes. */ while (buflen >= 16) { /* Generate a block of cipherstream. */ crypto_aesctr_stream_cipherblock_generate(stream); /* Encrypt the bytes and update the positions. */ crypto_aesctr_stream_cipherblock_use(stream, &inbuf, &outbuf, &buflen, 16, 0); } /* Process any final bytes after finishing all whole blocks. */ crypto_aesctr_stream_post_wholeblock(stream, &inbuf, &outbuf, &buflen); } /** * crypto_aesctr_free(stream): * Free the AES-CTR stream ${stream}. */ void crypto_aesctr_free(struct crypto_aesctr * stream) { /* Behave consistently with free(NULL). */ if (stream == NULL) return; /* Zero potentially sensitive information. */ insecure_memzero(stream, sizeof(struct crypto_aesctr)); /* Free the stream. */ free(stream); } /** * crypto_aesctr_buf(key, nonce, inbuf, outbuf, buflen): * Equivalent to _init(key, nonce); _stream(inbuf, outbuf, buflen); _free(). */ void crypto_aesctr_buf(const struct crypto_aes_key * key, uint64_t nonce, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { struct crypto_aesctr stream_rec; struct crypto_aesctr * stream = &stream_rec; /* Sanity check. */ assert(key != NULL); /* Initialize values. */ crypto_aesctr_init2(stream, key, nonce); /* Perform the encryption. */ crypto_aesctr_stream(stream, inbuf, outbuf, buflen); /* Zero potentially sensitive information. */ insecure_memzero(stream, sizeof(struct crypto_aesctr)); } tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr.h000066400000000000000000000033601476705112600221150ustar00rootroot00000000000000#ifndef CRYPTO_AESCTR_H_ #define CRYPTO_AESCTR_H_ #include #include /* Opaque types. */ struct crypto_aes_key; struct crypto_aesctr; /** * crypto_aesctr_init(key, nonce): * Prepare to encrypt/decrypt data with AES in CTR mode, using the provided * expanded ${key} and ${nonce}. The key provided must remain valid for the * lifetime of the stream. This is the same as calling _alloc() followed by * _init2(). */ struct crypto_aesctr * crypto_aesctr_init(const struct crypto_aes_key *, uint64_t); /** * crypto_aesctr_alloc(void): * Allocate an object for performing AES in CTR code. This must be followed * by calling _init2(). */ struct crypto_aesctr * crypto_aesctr_alloc(void); /** * crypto_aesctr_init2(stream, key, nonce): * Reset the AES-CTR stream ${stream}, using the ${key} and ${nonce}. If ${key} * is NULL, retain the previous AES key. */ void crypto_aesctr_init2(struct crypto_aesctr *, const struct crypto_aes_key *, uint64_t); /** * crypto_aesctr_stream(stream, inbuf, outbuf, buflen): * Generate the next ${buflen} bytes of the AES-CTR stream ${stream} and xor * them with bytes from ${inbuf}, writing the result into ${outbuf}. If the * buffers ${inbuf} and ${outbuf} overlap, they must be identical. */ void crypto_aesctr_stream(struct crypto_aesctr *, const uint8_t *, uint8_t *, size_t); /** * crypto_aesctr_free(stream): * Free the AES-CTR stream ${stream}. */ void crypto_aesctr_free(struct crypto_aesctr *); /** * crypto_aesctr_buf(key, nonce, inbuf, outbuf, buflen): * Equivalent to _init(key, nonce); _stream(inbuf, outbuf, buflen); _free(). */ void crypto_aesctr_buf(const struct crypto_aes_key *, uint64_t, const uint8_t *, uint8_t *, size_t); #endif /* !CRYPTO_AESCTR_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr_aesni.c000066400000000000000000000075051476705112600232740ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_AESNI /** * CPUSUPPORT CFLAGS: X86_AESNI */ #include #include #include #include #include "crypto_aes.h" #include "crypto_aes_aesni_m128i.h" #include "sysendian.h" #include "crypto_aesctr_aesni.h" /** * In order to optimize AES-CTR, it is desirable to separate out the handling * of individual bytes of data vs. the handling of complete (16 byte) blocks. * The handling of blocks in turn can be optimized further using CPU * intrinsics, e.g. SSE2 on x86 CPUs; however while the byte-at-once code * remains the same across platforms it should be inlined into the same (CPU * feature specific) routines for performance reasons. * * In order to allow those generic functions to be inlined into multiple * functions in separate translation units, we place them into a "shared" C * file which is included in each of the platform-specific variants. */ #include "crypto_aesctr_shared.c" #ifdef BROKEN_MM_LOADU_SI64 #warning Working around compiler bug: _mm_loadu_si64 is missing #warning Updating to a newer compiler may improve performance #endif /** * load_si64(mem): * Load an unaligned 64-bit integer from memory into the lowest 64 bits of the * returned value. The contents of the upper 64 bits is not defined. */ static inline __m128i load_si64(const void * mem) { #ifdef BROKEN_MM_LOADU_SI64 return (_mm_castpd_si128(_mm_load_sd(mem))); #else return (_mm_loadu_si64(mem)); #endif } /* Process multiple whole blocks by generating & using a cipherblock. */ static void crypto_aesctr_aesni_stream_wholeblocks(struct crypto_aesctr * stream, const uint8_t ** inbuf, uint8_t ** outbuf, size_t * buflen) { __m128i bufsse; __m128i inbufsse; __m128i nonce_be; uint8_t block_counter_be_arr[8]; uint64_t block_counter; size_t num_blocks; size_t i; /* Load local variables from stream. */ nonce_be = load_si64(stream->pblk); block_counter = stream->bytectr / 16; /* How many blocks should we process? */ num_blocks = (*buflen) / 16; /* * This is 'for (i = num_blocks; i > 0; i--)', but ensuring that the * compiler knows that we will execute the loop at least once. */ i = num_blocks; do { /* Prepare counter. */ be64enc(block_counter_be_arr, block_counter); /* Encrypt the cipherblock. */ bufsse = load_si64(block_counter_be_arr); bufsse = _mm_unpacklo_epi64(nonce_be, bufsse); bufsse = crypto_aes_encrypt_block_aesni_m128i(bufsse, stream->key); /* Encrypt the byte(s). */ inbufsse = _mm_loadu_si128((const __m128i *)(*inbuf)); bufsse = _mm_xor_si128(inbufsse, bufsse); _mm_storeu_si128((__m128i *)(*outbuf), bufsse); /* Update the positions. */ block_counter++; *inbuf += 16; *outbuf += 16; /* Update the counter. */ i--; } while (i > 0); /* Update the overall buffer length. */ *buflen -= 16 * num_blocks; /* Update variables in stream. */ memcpy(stream->pblk + 8, block_counter_be_arr, 8); stream->bytectr += 16 * num_blocks; } /** * crypto_aesctr_aesni_stream(stream, inbuf, outbuf, buflen): * Generate the next ${buflen} bytes of the AES-CTR stream ${stream} and xor * them with bytes from ${inbuf}, writing the result into ${outbuf}. If the * buffers ${inbuf} and ${outbuf} overlap, they must be identical. */ void crypto_aesctr_aesni_stream(struct crypto_aesctr * stream, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { /* Process any bytes before we can process a whole block. */ if (crypto_aesctr_stream_pre_wholeblock(stream, &inbuf, &outbuf, &buflen)) return; /* Process whole blocks of 16 bytes. */ if (buflen >= 16) crypto_aesctr_aesni_stream_wholeblocks(stream, &inbuf, &outbuf, &buflen); /* Process any final bytes after finishing all whole blocks. */ crypto_aesctr_stream_post_wholeblock(stream, &inbuf, &outbuf, &buflen); } #endif /* CPUSUPPORT_X86_AESNI */ tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr_aesni.h000066400000000000000000000010741476705112600232740ustar00rootroot00000000000000#ifndef CRYPTO_AESCTR_AESNI_H_ #define CRYPTO_AESCTR_AESNI_H_ #include #include /* Opaque type. */ struct crypto_aesctr; /** * crypto_aesctr_aesni_stream(stream, inbuf, outbuf, buflen): * Generate the next ${buflen} bytes of the AES-CTR stream ${stream} and xor * them with bytes from ${inbuf}, writing the result into ${outbuf}. If the * buffers ${inbuf} and ${outbuf} overlap, they must be identical. */ void crypto_aesctr_aesni_stream(struct crypto_aesctr *, const uint8_t *, uint8_t *, size_t); #endif /* !CRYPTO_AESCTR_AESNI_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr_arm.c000066400000000000000000000064671476705112600227620ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_ARM_AES /** * CPUSUPPORT CFLAGS: ARM_AES */ #include #include #include #ifdef __ARM_NEON #include #endif #include "crypto_aes.h" #include "crypto_aes_arm_u8.h" #include "sysendian.h" #include "crypto_aesctr_arm.h" /** * In order to optimize AES-CTR, it is desirable to separate out the handling * of individual bytes of data vs. the handling of complete (16 byte) blocks. * The handling of blocks in turn can be optimized further using CPU * intrinsics, e.g. SSE2 on x86 CPUs; however while the byte-at-once code * remains the same across platforms it should be inlined into the same (CPU * feature specific) routines for performance reasons. * * In order to allow those generic functions to be inlined into multiple * functions in separate translation units, we place them into a "shared" C * file which is included in each of the platform-specific variants. */ #include "crypto_aesctr_shared.c" /* Process multiple whole blocks by generating & using a cipherblock. */ static void crypto_aesctr_arm_stream_wholeblocks(struct crypto_aesctr * stream, const uint8_t ** inbuf, uint8_t ** outbuf, size_t * buflen) { uint8x16_t bufarm; uint8x16_t inbufarm; uint8x8_t nonce_be; uint8x8_t block_counter_be; uint8_t block_counter_be_arr[8]; uint64_t block_counter; size_t num_blocks; size_t i; /* Load local variables from stream. */ nonce_be = vld1_u8(stream->pblk); block_counter = stream->bytectr / 16; /* How many blocks should we process? */ num_blocks = (*buflen) / 16; /* * This is 'for (i = num_blocks; i > 0; i--)', but ensuring that the * compiler knows that we will execute the loop at least once. */ i = num_blocks; do { /* Prepare counter. */ be64enc(block_counter_be_arr, block_counter); /* Encrypt the cipherblock. */ block_counter_be = vld1_u8(block_counter_be_arr); bufarm = vcombine_u8(nonce_be, block_counter_be); bufarm = crypto_aes_encrypt_block_arm_u8(bufarm, stream->key); /* Encrypt the byte(s). */ inbufarm = vld1q_u8(*inbuf); bufarm = veorq_u8(inbufarm, bufarm); vst1q_u8(*outbuf, bufarm); /* Update the positions. */ block_counter++; *inbuf += 16; *outbuf += 16; /* Update the counter. */ i--; } while (i > 0); /* Update the overall buffer length. */ *buflen -= 16 * num_blocks; /* Update variables in stream. */ memcpy(stream->pblk + 8, block_counter_be_arr, 8); stream->bytectr += 16 * num_blocks; } /** * crypto_aesctr_arm_stream(stream, inbuf, outbuf, buflen): * Generate the next ${buflen} bytes of the AES-CTR stream ${stream} and xor * them with bytes from ${inbuf}, writing the result into ${outbuf}. If the * buffers ${inbuf} and ${outbuf} overlap, they must be identical. */ void crypto_aesctr_arm_stream(struct crypto_aesctr * stream, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { /* Process any bytes before we can process a whole block. */ if (crypto_aesctr_stream_pre_wholeblock(stream, &inbuf, &outbuf, &buflen)) return; /* Process whole blocks of 16 bytes. */ if (buflen >= 16) crypto_aesctr_arm_stream_wholeblocks(stream, &inbuf, &outbuf, &buflen); /* Process any final bytes after finishing all whole blocks. */ crypto_aesctr_stream_post_wholeblock(stream, &inbuf, &outbuf, &buflen); } #endif /* CPUSUPPORT_ARM_AES */ tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr_arm.h000066400000000000000000000010621476705112600227510ustar00rootroot00000000000000#ifndef CRYPTO_AESCTR_ARM_H_ #define CRYPTO_AESCTR_ARM_H_ #include #include /* Opaque type. */ struct crypto_aesctr; /** * crypto_aesctr_arm_stream(stream, inbuf, outbuf, buflen): * Generate the next ${buflen} bytes of the AES-CTR stream ${stream} and xor * them with bytes from ${inbuf}, writing the result into ${outbuf}. If the * buffers ${inbuf} and ${outbuf} overlap, they must be identical. */ void crypto_aesctr_arm_stream(struct crypto_aesctr *, const uint8_t *, uint8_t *, size_t); #endif /* !CRYPTO_AESCTR_ARM_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_aesctr_shared.c000066400000000000000000000054521476705112600234420ustar00rootroot00000000000000/* * This code is shared between crypto_aesctr*.c files, and should not be * compiled as a separate translation unit. For details, see the comments in * those files. */ /* AES-CTR state. */ struct crypto_aesctr { const struct crypto_aes_key * key; uint64_t bytectr; uint8_t buf[16]; uint8_t pblk[16]; }; /* Generate a block of cipherstream. */ static inline void crypto_aesctr_stream_cipherblock_generate(struct crypto_aesctr * stream) { /* Sanity check. */ assert(stream->bytectr % 16 == 0); /* Prepare counter. */ stream->pblk[15]++; if (stream->pblk[15] == 0) { /* * If incrementing the least significant byte resulted in it * wrapping, re-encode the complete 64-bit value. */ be64enc(stream->pblk + 8, stream->bytectr / 16); } /* Encrypt the cipherblock. */ crypto_aes_encrypt_block(stream->pblk, stream->buf, stream->key); } /* Encrypt ${nbytes} bytes, then update ${inbuf}, ${outbuf}, and ${buflen}. */ static inline void crypto_aesctr_stream_cipherblock_use(struct crypto_aesctr * stream, const uint8_t ** inbuf, uint8_t ** outbuf, size_t * buflen, size_t nbytes, size_t bytemod) { size_t i; /* Encrypt the byte(s). */ for (i = 0; i < nbytes; i++) (*outbuf)[i] = (*inbuf)[i] ^ stream->buf[bytemod + i]; /* Move to the next byte(s) of cipherstream. */ stream->bytectr += nbytes; /* Update the positions. */ *inbuf += nbytes; *outbuf += nbytes; *buflen -= nbytes; } /* * Process any bytes before we can process a whole block. Return 1 if there * are no bytes left to process after calling this function. */ static inline int crypto_aesctr_stream_pre_wholeblock(struct crypto_aesctr * stream, const uint8_t ** inbuf, uint8_t ** outbuf, size_t * buflen_p) { size_t bytemod; /* Do we have any bytes left in the current cipherblock? */ bytemod = stream->bytectr % 16; if (bytemod != 0) { /* Do we have enough to complete the request? */ if (bytemod + *buflen_p <= 16) { /* Process only buflen bytes, then return. */ crypto_aesctr_stream_cipherblock_use(stream, inbuf, outbuf, buflen_p, *buflen_p, bytemod); return (1); } /* Encrypt the byte(s) and update the positions. */ crypto_aesctr_stream_cipherblock_use(stream, inbuf, outbuf, buflen_p, 16 - bytemod, bytemod); } /* Success! */ return (0); } /* Process any final bytes after finishing all whole blocks. */ static inline void crypto_aesctr_stream_post_wholeblock(struct crypto_aesctr * stream, const uint8_t ** inbuf, uint8_t ** outbuf, size_t * buflen_p) { /* Process any final bytes; we need a new cipherblock. */ if (*buflen_p > 0) { /* Generate a block of cipherstream. */ crypto_aesctr_stream_cipherblock_generate(stream); /* Encrypt the byte(s) and update the positions. */ crypto_aesctr_stream_cipherblock_use(stream, inbuf, outbuf, buflen_p, *buflen_p, 0); } } tarsnap-1.0.41/libcperciva/crypto/crypto_dh.c000066400000000000000000000156171476705112600212320ustar00rootroot00000000000000#include #include #include #include #include "warnp.h" #include "crypto_dh_group14.h" #include "crypto_entropy.h" #include "crypto_dh.h" static int blinded_modexp(uint8_t r[CRYPTO_DH_PUBLEN], BIGNUM * a, const uint8_t priv[CRYPTO_DH_PRIVLEN]); /* Big-endian representation of 2^256. */ static const uint8_t two_exp_256[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /** * blinded_modexp(r, a, priv): * Compute ${r} = ${a}^(2^258 + ${priv}), where ${r} and ${priv} are treated * as big-endian integers; and avoid leaking timing data in this process. */ static int blinded_modexp(uint8_t r[CRYPTO_DH_PUBLEN], BIGNUM * a, const uint8_t priv[CRYPTO_DH_PRIVLEN]) { BIGNUM * two_exp_256_bn; BIGNUM * priv_bn; uint8_t blinding[CRYPTO_DH_PRIVLEN]; BIGNUM * blinding_bn; BIGNUM * priv_blinded; BIGNUM * m_bn; BN_CTX * ctx; BIGNUM * r1; BIGNUM * r2; int rlen; /* Construct 2^256 in BN representation. */ if ((two_exp_256_bn = BN_bin2bn(two_exp_256, 33, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Construct 2^258 + ${priv} in BN representation. */ if ((priv_bn = BN_bin2bn(priv, CRYPTO_DH_PRIVLEN, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err1; } if ((!BN_add(priv_bn, priv_bn, two_exp_256_bn)) || (!BN_add(priv_bn, priv_bn, two_exp_256_bn)) || (!BN_add(priv_bn, priv_bn, two_exp_256_bn)) || (!BN_add(priv_bn, priv_bn, two_exp_256_bn))) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err2; } /* Generate blinding exponent. */ if (crypto_entropy_read(blinding, CRYPTO_DH_PRIVLEN)) goto err2; if ((blinding_bn = BN_bin2bn(blinding, CRYPTO_DH_PRIVLEN, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err2; } if (!BN_add(blinding_bn, blinding_bn, two_exp_256_bn)) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err3; } /* Generate blinded exponent. */ if ((priv_blinded = BN_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err3; } if (!BN_sub(priv_blinded, priv_bn, blinding_bn)) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err4; } /* Construct group #14 modulus in BN representation. */ if ((m_bn = BN_bin2bn(crypto_dh_group14, 256, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err4; } /* Allocate BN context. */ if ((ctx = BN_CTX_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err5; } /* Allocate space for storing results of exponentiations. */ if ((r1 = BN_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err6; } if ((r2 = BN_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err7; } /* Perform modular exponentiations. */ if (!BN_mod_exp(r1, a, blinding_bn, m_bn, ctx)) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err8; } if (!BN_mod_exp(r2, a, priv_blinded, m_bn, ctx)) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err8; } /* Compute final result and export to big-endian integer format. */ if (!BN_mod_mul(r1, r1, r2, m_bn, ctx)) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err8; } rlen = BN_num_bytes(r1); if (rlen < 0) { warn0("Unexpected error in OpenSSL"); goto err8; } if (rlen > CRYPTO_DH_PUBLEN) { warn0("Exponent result too large!"); goto err8; } memset(r, 0, CRYPTO_DH_PUBLEN - (size_t)rlen); BN_bn2bin(r1, &r[CRYPTO_DH_PUBLEN - (size_t)rlen]); /* Free space allocated by BN_new. */ BN_clear_free(r2); BN_clear_free(r1); /* Free context allocated by BN_CTX_new. */ BN_CTX_free(ctx); /* Free space allocated by BN_bin2bn. */ BN_free(m_bn); /* Free space allocated by BN_new. */ BN_clear_free(priv_blinded); /* Free space allocated by BN_bin2bn. */ BN_clear_free(blinding_bn); BN_clear_free(priv_bn); BN_free(two_exp_256_bn); /* Success! */ return (0); err8: BN_clear_free(r2); err7: BN_clear_free(r1); err6: BN_CTX_free(ctx); err5: BN_free(m_bn); err4: BN_clear_free(priv_blinded); err3: BN_clear_free(blinding_bn); err2: BN_clear_free(priv_bn); err1: BN_free(two_exp_256_bn); err0: /* Failure! */ return (-1); } /** * crypto_dh_generate_pub(pub, priv): * Compute ${pub} equal to 2^(2^258 + ${priv}) in Diffie-Hellman group #14. */ int crypto_dh_generate_pub(uint8_t pub[CRYPTO_DH_PUBLEN], const uint8_t priv[CRYPTO_DH_PRIVLEN]) { BIGNUM * two; /* Generate BN representation for 2. */ if ((two = BN_new()) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } if (!BN_set_word(two, 2)) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err1; } /* Compute pub = two^(2^258 + priv). */ if (blinded_modexp(pub, two, priv)) goto err1; /* Free storage allocated by BN_new. */ BN_free(two); /* Success! */ return (0); err1: BN_free(two); err0: /* Failure! */ return (-1); } /** * crypto_dh_generate(pub, priv): * Generate a 256-bit private key ${priv}, and compute ${pub} equal to * 2^(2^258 + ${priv}) mod p where p is the Diffie-Hellman group #14 modulus. * Both values are stored as big-endian integers. */ int crypto_dh_generate(uint8_t pub[CRYPTO_DH_PUBLEN], uint8_t priv[CRYPTO_DH_PRIVLEN]) { /* Generate a random private key. */ if (crypto_entropy_read(priv, CRYPTO_DH_PRIVLEN)) goto err0; /* Compute the public key. */ if (crypto_dh_generate_pub(pub, priv)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * crypto_dh_compute(pub, priv, key): * In the Diffie-Hellman group #14, compute ${pub}^(2^258 + ${priv}) and * write the result into ${key}. All values are big-endian. Note that the * value ${pub} is the public key produced by the call to crypto_dh_generate() * made by the *other* participant in the key exchange. */ int crypto_dh_compute(const uint8_t pub[CRYPTO_DH_PUBLEN], const uint8_t priv[CRYPTO_DH_PRIVLEN], uint8_t key[CRYPTO_DH_KEYLEN]) { BIGNUM * a; /* Convert ${pub} into BN representation. */ if ((a = BN_bin2bn(pub, CRYPTO_DH_PUBLEN, NULL)) == NULL) { warn0("%s", ERR_error_string(ERR_get_error(), NULL)); goto err0; } /* Compute key = pub^(2^258 + priv). */ if (blinded_modexp(key, a, priv)) goto err1; /* Free storage allocated by BN_bin2bn. */ BN_free(a); /* Success! */ return (0); err1: BN_free(a); err0: /* Failure! */ return (-1); } /** * crypto_dh_sanitycheck(pub): * Sanity-check the Diffie-Hellman public value ${pub} by checking that it * is less than the group #14 modulus. Return 0 if sane, -1 if insane. */ int crypto_dh_sanitycheck(const uint8_t pub[CRYPTO_DH_PUBLEN]) { if (memcmp(pub, crypto_dh_group14, 256) >= 0) return (-1); /* Value is sane. */ return (0); } tarsnap-1.0.41/libcperciva/crypto/crypto_dh.h000066400000000000000000000027121476705112600212270ustar00rootroot00000000000000#ifndef CRYPTO_DH_H_ #define CRYPTO_DH_H_ #include /* Sizes of Diffie-Hellman private, public, and exchanged keys. */ #define CRYPTO_DH_PRIVLEN 32 #define CRYPTO_DH_PUBLEN 256 #define CRYPTO_DH_KEYLEN 256 /** * crypto_dh_generate_pub(pub, priv): * Compute ${pub} equal to 2^(2^258 + ${priv}) in Diffie-Hellman group #14. */ int crypto_dh_generate_pub(uint8_t[CRYPTO_DH_PUBLEN], const uint8_t[CRYPTO_DH_PRIVLEN]); /** * crypto_dh_generate(pub, priv): * Generate a 256-bit private key ${priv}, and compute ${pub} equal to * 2^(2^258 + ${priv}) mod p where p is the Diffie-Hellman group #14 modulus. * Both values are stored as big-endian integers. */ int crypto_dh_generate(uint8_t[CRYPTO_DH_PUBLEN], uint8_t[CRYPTO_DH_PRIVLEN]); /** * crypto_dh_compute(pub, priv, key): * In the Diffie-Hellman group #14, compute ${pub}^(2^258 + ${priv}) and * write the result into ${key}. All values are big-endian. Note that the * value ${pub} is the public key produced by the call to crypto_dh_generate() * made by the *other* participant in the key exchange. */ int crypto_dh_compute(const uint8_t[CRYPTO_DH_PUBLEN], const uint8_t[CRYPTO_DH_PRIVLEN], uint8_t[CRYPTO_DH_KEYLEN]); /** * crypto_dh_sanitycheck(pub): * Sanity-check the Diffie-Hellman public value ${pub} by checking that it * is less than the group #14 modulus. Return 0 if sane, -1 if insane. */ int crypto_dh_sanitycheck(const uint8_t[CRYPTO_DH_PUBLEN]); #endif /* !CRYPTO_DH_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_dh_group14.c000066400000000000000000000037531476705112600226110ustar00rootroot00000000000000#include #include "crypto_dh_group14.h" /** * This is the big-endian representation of * p = 2^2048 - 2^1984 + 2^64 * floor(2^1918 pi + 124476) - 1 * where the value 124476 is chosen to be the least positive integer such * that both p and (p - 1)/2 are prime. Diffie-Hellman operations are done * in the group of quadratic residues modulo p, and the integer 2 is a * generator of this group. */ const uint8_t crypto_dh_group14[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xac, 0xaa, 0x68, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; tarsnap-1.0.41/libcperciva/crypto/crypto_dh_group14.h000066400000000000000000000003151476705112600226050ustar00rootroot00000000000000#ifndef CRYPTO_DH_GROUP14_H_ #define CRYPTO_DH_GROUP14_H_ #include /* Diffie-Hellman group #14, from RFC 3526. */ extern const uint8_t crypto_dh_group14[]; #endif /* !CRYPTO_DH_GROUP14_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_entropy.c000066400000000000000000000125541476705112600223340ustar00rootroot00000000000000#include #include #include #include "cpusupport.h" #include "crypto_entropy_rdrand.h" #include "entropy.h" #include "insecure_memzero.h" #include "sha256.h" #include "crypto_entropy.h" /** * This system implements the HMAC_DRBG pseudo-random number generator as * specified in section 10.1.2 of the NIST SP 800-90 standard. In this * implementation, the optional personalization_string and additional_input * specified in the standard are not implemented. */ /* Internal HMAC_DRBG state. */ static struct { uint8_t Key[32]; uint8_t V[32]; uint32_t reseed_counter; } drbg; /* Set to non-zero once the PRNG has been instantiated. */ static int instantiated = 0; /* Could be as high as 2^48 if we wanted... */ #define RESEED_INTERVAL 256 /* Limited to 2^16 by specification. */ #define GENERATE_MAXLEN 65536 static int instantiate(void); static void update(const uint8_t *, size_t); static int reseed(void); static void generate(uint8_t *, size_t); #ifdef CPUSUPPORT_X86_RDRAND static void update_from_rdrand(void) { unsigned int buf[8]; /* This is only *extra* entropy, so it's ok if it fails. */ if (generate_seed_rdrand(buf, 8)) return; update((uint8_t *)buf, sizeof(buf)); /* Clean up. */ insecure_memzero(buf, sizeof(buf)); } #endif /** * instantiate(void): * Initialize the DRBG state. (Section 10.1.2.3) */ static int instantiate(void) { uint8_t seed_material[48]; /* Obtain random seed_material = (entropy_input || nonce). */ if (entropy_read(seed_material, 48)) return (-1); /* Initialize Key, V, and reseed_counter. */ memset(drbg.Key, 0x00, 32); memset(drbg.V, 0x01, 32); drbg.reseed_counter = 1; /* Mix the random seed into the state. */ update(seed_material, 48); #ifdef CPUSUPPORT_X86_RDRAND /* Add output of RDRAND into the state. */ if (cpusupport_x86_rdrand()) update_from_rdrand(); #endif /* Clean the stack. */ insecure_memzero(seed_material, 48); /* Success! */ return (0); } /** * update(data, datalen): * Update the DRBG state using the provided data. (Section 10.1.2.2) */ static void update(const uint8_t * data, size_t datalen) { HMAC_SHA256_CTX ctx; uint8_t K[32]; uint8_t Vx[33]; /* Load (Key, V) into (K, Vx). */ memcpy(K, drbg.Key, 32); memcpy(Vx, drbg.V, 32); /* K <- HMAC(K, V || 0x00 || data). */ Vx[32] = 0x00; HMAC_SHA256_Init(&ctx, K, 32); HMAC_SHA256_Update(&ctx, Vx, 33); HMAC_SHA256_Update(&ctx, data, datalen); HMAC_SHA256_Final(K, &ctx); /* V <- HMAC(K, V). */ HMAC_SHA256_Buf(K, 32, Vx, 32, Vx); /* If the provided data is non-Null, perform another mixing stage. */ if (datalen != 0) { /* K <- HMAC(K, V || 0x01 || data). */ Vx[32] = 0x01; HMAC_SHA256_Init(&ctx, K, 32); HMAC_SHA256_Update(&ctx, Vx, 33); HMAC_SHA256_Update(&ctx, data, datalen); HMAC_SHA256_Final(K, &ctx); /* V <- HMAC(K, V). */ HMAC_SHA256_Buf(K, 32, Vx, 32, Vx); } /* Copy (K, Vx) back to (Key, V). */ memcpy(drbg.Key, K, 32); memcpy(drbg.V, Vx, 32); /* Clean the stack. */ insecure_memzero(K, 32); insecure_memzero(Vx, 33); } /** * reseed(void): * Reseed the DRBG state (mix in new entropy). (Section 10.1.2.4) */ static int reseed(void) { uint8_t seed_material[32]; /* Obtain random seed_material = entropy_input. */ if (entropy_read(seed_material, 32)) return (-1); /* Mix the random seed into the state. */ update(seed_material, 32); #ifdef CPUSUPPORT_X86_RDRAND /* Add output of RDRAND into the state. */ if (cpusupport_x86_rdrand()) update_from_rdrand(); #endif /* Reset the reseed_counter. */ drbg.reseed_counter = 1; /* Clean the stack. */ insecure_memzero(seed_material, 32); /* Success! */ return (0); } /** * generate(buf, buflen): * Fill the provided buffer with random bits, assuming that reseed_counter * is less than RESEED_INTERVAL (the caller is responsible for calling * reseed() as needed) and ${buflen} is less than 2^16 (the caller is * responsible for splitting up larger requests). (Section 10.1.2.5) */ static void generate(uint8_t * buf, size_t buflen) { size_t bufpos; assert(buflen <= GENERATE_MAXLEN); assert(drbg.reseed_counter <= RESEED_INTERVAL); /* Iterate until we've filled the buffer. */ for (bufpos = 0; bufpos < buflen; bufpos += 32) { HMAC_SHA256_Buf(drbg.Key, 32, drbg.V, 32, drbg.V); if (buflen - bufpos >= 32) memcpy(&buf[bufpos], drbg.V, 32); else memcpy(&buf[bufpos], drbg.V, buflen - bufpos); } /* Mix up state. */ update(NULL, 0); /* We're one data-generation step closer to needing a reseed. */ drbg.reseed_counter += 1; } /** * crypto_entropy_read(buf, buflen): * Fill the buffer with unpredictable bits. */ int crypto_entropy_read(uint8_t * buf, size_t buflen) { size_t bytes_to_provide; /* Instantiate if needed. */ if (instantiated == 0) { /* Try to instantiate the PRNG. */ if (instantiate()) return (-1); /* We have instantiated the PRNG. */ instantiated = 1; } /* Loop until we've filled the buffer. */ while (buflen > 0) { /* Do we need to reseed? */ if (drbg.reseed_counter > RESEED_INTERVAL) { if (reseed()) return (-1); } /* How much data are we generating in this step? */ if (buflen > GENERATE_MAXLEN) bytes_to_provide = GENERATE_MAXLEN; else bytes_to_provide = buflen; /* Generate bytes. */ generate(buf, bytes_to_provide); /* We've done part of the buffer. */ buf += bytes_to_provide; buflen -= bytes_to_provide; } /* Success! */ return (0); } tarsnap-1.0.41/libcperciva/crypto/crypto_entropy.h000066400000000000000000000004041476705112600223300ustar00rootroot00000000000000#ifndef CRYPTO_ENTROPY_H_ #define CRYPTO_ENTROPY_H_ #include #include /** * crypto_entropy_read(buf, buflen): * Fill the buffer with unpredictable bits. */ int crypto_entropy_read(uint8_t *, size_t); #endif /* !CRYPTO_ENTROPY_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_entropy_rdrand.c000066400000000000000000000012761476705112600236650ustar00rootroot00000000000000#include "cpusupport.h" #ifdef CPUSUPPORT_X86_RDRAND /** * CPUSUPPORT CFLAGS: X86_RDRAND */ #include #include #include "crypto_entropy_rdrand.h" /** * generate_seed_rdrand(buf, len): * Fill the ${buf} buffer with values from RDRAND. This implementation uses * the RDRAND instruction, and should only be used if CPUSUPPORT_X86_RDRAND is * defined and cpusupport_x86_rdrand() returns nonzero. */ int generate_seed_rdrand(unsigned int * buf, size_t len) { size_t i; /* Fill buffer. */ for (i = 0; i < len; i++) { if (!_rdrand32_step(&buf[i])) goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (1); } #endif /* CPUSUPPORT_X86_RDRAND */ tarsnap-1.0.41/libcperciva/crypto/crypto_entropy_rdrand.h000066400000000000000000000006611476705112600236670ustar00rootroot00000000000000#ifndef CRYPTO_ENTROPY_RDRAND_H_ #define CRYPTO_ENTROPY_RDRAND_H_ #include /** * generate_seed_rdrand(buf, len): * Fill the ${buf} buffer with values from RDRAND. This implementation uses * the RDRAND instruction, and should only be used if CPUSUPPORT_X86_RDRAND is * defined and cpusupport_x86_rdrand() returns nonzero. */ int generate_seed_rdrand(unsigned int *, size_t); #endif /* !CRYPTO_ENTROPY_RDRAND_H_ */ tarsnap-1.0.41/libcperciva/crypto/crypto_verify_bytes.c000066400000000000000000000007231476705112600233410ustar00rootroot00000000000000#include #include #include "crypto_verify_bytes.h" /** * crypto_verify_bytes(buf0, buf1, len): * Return zero if and only if ${buf0[0 .. len - 1]} and ${buf1[0 .. len - 1]} * are identical. Do not leak any information via timing side channels. */ uint8_t crypto_verify_bytes(const uint8_t * buf0, const uint8_t * buf1, size_t len) { uint8_t rc = 0; size_t i; for (i = 0; i < len; i++) rc = rc | (buf0[i] ^ buf1[i]); return (rc); } tarsnap-1.0.41/libcperciva/crypto/crypto_verify_bytes.h000066400000000000000000000006351476705112600233500ustar00rootroot00000000000000#ifndef CRYPTO_VERIFY_BYTES_H_ #define CRYPTO_VERIFY_BYTES_H_ #include #include /** * crypto_verify_bytes(buf0, buf1, len): * Return zero if and only if ${buf0[0 .. len - 1]} and ${buf1[0 .. len - 1]} * are identical. Do not leak any information via timing side channels. */ uint8_t crypto_verify_bytes(const uint8_t *, const uint8_t *, size_t); #endif /* !CRYPTO_VERIFY_BYTES_H_ */ tarsnap-1.0.41/libcperciva/datastruct/000077500000000000000000000000001476705112600177175ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/datastruct/elasticarray.c000066400000000000000000000176161476705112600225610ustar00rootroot00000000000000#include #include #include #include #include #include "elasticarray.h" struct elasticarray { size_t size; size_t alloc; void * buf; }; /** * resize(EA, nsize): * Resize the virtual buffer for ${EA} to length ${nsize} bytes. The actual * buffer may or may not need to be resized. On failure, the buffer will be * unmodified. */ static int resize(struct elasticarray * EA, size_t nsize) { size_t nalloc; void * nbuf; /* Figure out how large an allocation we want. */ if (EA->alloc < nsize) { /* We need to enlarge the buffer. */ nalloc = EA->alloc * 2; /* * Handle if nalloc is not large enough to hold nsize, which can * happen when: 1) EA->alloc is small enough that EA->alloc * 2 * is still smaller than nsize, or 2) EA->alloc is large enough * that EA->alloc * 2 wraps around, becoming smaller than nsize. */ if (nalloc < nsize) nalloc = nsize; } else if (EA->alloc / 4 > nsize) { /* We need to shrink the buffer. */ nalloc = nsize * 2; } else { nalloc = EA->alloc; } /* * Resizing to zero needs special handling due to some strange * interpretations of what realloc(p, 0) should do if it fails * to allocate zero bytes of memory. */ if (nalloc == 0) { /* We can only get here if we set nsize to 0. */ assert(nsize == 0); free(EA->buf); EA->buf = NULL; EA->alloc = 0; } else if (nalloc != EA->alloc) { /* Reallocate to a nonzero size if necessary. */ if ((nbuf = realloc(EA->buf, nalloc)) == NULL) goto err0; EA->buf = nbuf; EA->alloc = nalloc; } /* Record the new array size. */ EA->size = nsize; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_init(nrec, reclen): * Create and return an elastic array holding ${nrec} (uninitialized) records * of length ${reclen}. Takes O(nrec * reclen) time. The value ${reclen} * must be positive. */ struct elasticarray * elasticarray_init(size_t nrec, size_t reclen) { struct elasticarray * EA; /* Sanity check. */ assert(reclen > 0); /* Allocate structure. */ if ((EA = malloc(sizeof(struct elasticarray))) == NULL) goto err0; /* The array is empty for now. */ EA->size = EA->alloc = 0; EA->buf = NULL; /* Reallocate to the requested length. */ if (elasticarray_resize(EA, nrec, reclen)) goto err1; /* Success! */ return (EA); err1: elasticarray_free(EA); err0: /* Failure! */ return (NULL); } /** * elasticarray_resize(EA, nrec, reclen): * Resize the elastic array pointed to by ${EA} to hold ${nrec} records of * length ${reclen}. If ${nrec} exceeds the number of records previously * held by the array, the additional records will be uninitialized. Takes * O(nrec * reclen) time. The value ${reclen} must be positive. */ int elasticarray_resize(struct elasticarray * EA, size_t nrec, size_t reclen) { /* Check for overflow. */ if (nrec > SIZE_MAX / reclen) { errno = ENOMEM; goto err0; } /* Resize the buffer. */ if (resize(EA, nrec * reclen)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_getsize(EA, reclen): * Return the number of length-${reclen} records in the array, rounding down * if there is a partial record (which can only occur if elasticarray_* * functions have been called with different values of reclen). The value * ${reclen} must be positive. */ size_t elasticarray_getsize(const struct elasticarray * EA, size_t reclen) { return (EA->size / reclen); } /** * elasticarray_append(EA, buf, nrec, reclen): * Append to the elastic array ${EA} the ${nrec} records of length ${reclen} * stored in ${buf}. Takes O(nrec * reclen) amortized time. The value * ${reclen} must be positive. */ int elasticarray_append(struct elasticarray * EA, const void * buf, size_t nrec, size_t reclen) { size_t bufpos = EA->size; size_t nsize; /* Check for overflow. */ if ((nrec > SIZE_MAX / reclen) || (nrec * reclen > SIZE_MAX - EA->size)) { errno = ENOMEM; goto err0; } /* Resize the buffer. */ nsize = EA->size + nrec * reclen; if (resize(EA, nsize)) goto err0; /* Copy bytes in. */ if (nrec > 0) { /* We shouldn't have requested a 0-size array. */ assert(nsize > 0); /* Copy bytes in. */ memcpy((uint8_t *)(EA->buf) + bufpos, buf, nrec * reclen); } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_shrink(EA, nrec, reclen): * Delete the final ${nrec} records of length ${reclen} from the elastic * array ${EA}. If there are fewer than ${nrec} records, all records * present will be deleted. The value ${reclen} must be positive. * * As an exception to the normal rule, an elastic array may occupy more than * 4 times the optimal storage immediately following an elasticarray_shrink() * call; but only if realloc(3) failed to shrink a memory allocation. */ void elasticarray_shrink(struct elasticarray * EA, size_t nrec, size_t reclen) { size_t nsize; /* Figure out how much to keep. */ if ((nrec > SIZE_MAX / reclen) || (nrec * reclen > EA->size)) nsize = 0; else nsize = EA->size - nrec * reclen; /* Resize the buffer... */ if (resize(EA, nsize)) { /* * ... and if we fail to reallocate, just record the new * length and continue using the old buffer. */ EA->size = nsize; } } /** * elasticarray_truncate(EA): * Release any spare space in the elastic array ${EA}. */ int elasticarray_truncate(struct elasticarray * EA) { void * nbuf; /* * Truncating down to zero needs special handling due to some strange * interpretations of what realloc(p, 0) should do if it fails to * allocate zero bytes of memory. */ if (EA->size == 0) { free(EA->buf); EA->buf = NULL; EA->alloc = 0; } else if (EA->alloc > EA->size) { /* Reallocate to eliminate spare space if necessary. */ if ((nbuf = realloc(EA->buf, EA->size)) == NULL) goto err0; EA->buf = nbuf; EA->alloc = EA->size; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_get(EA, pos, reclen): * Return a pointer to record number ${pos} of length ${reclen} in the * elastic array ${EA}. Takes O(1) time. */ void * elasticarray_get(struct elasticarray * EA, size_t pos, size_t reclen) { /* Calculate the pointer address. */ return ((uint8_t *)(EA->buf) + pos * reclen); } /** * elasticarray_free(EA): * Free the elastic array ${EA}. Takes O(1) time. */ void elasticarray_free(struct elasticarray * EA) { /* Behave consistently with free(NULL). */ if (EA == NULL) return; free(EA->buf); free(EA); } /** * elasticarray_export(EA, buf, nrec, reclen): * Return the data in the elastic array ${EA} as a buffer ${buf} containing * ${nrec} records of length ${reclen}. Free the elastic array ${EA}. * The value ${reclen} must be positive. */ int elasticarray_export(struct elasticarray * EA, void ** buf, size_t * nrec, size_t reclen) { /* Remove any spare space. */ if (elasticarray_truncate(EA)) goto err0; /* Return the buffer and number of records. */ *buf = EA->buf; *nrec = elasticarray_getsize(EA, reclen); /* * Free the elastic array structure -- but not its buffer, since we've * passed the buffer out to the caller. */ free(EA); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_exportdup(EA, buf, nrec, reclen): * Duplicate the data in the elastic array ${EA} into a buffer ${buf} * containing ${nrec} records of length ${reclen}. (Same as _export, except * that the elastic array remains intact.) The value ${reclen} must be * positive. */ int elasticarray_exportdup(const struct elasticarray * EA, void ** buf, size_t * nrec, size_t reclen) { /* Allocate buffer for the caller's copy of the data. */ if ((*buf = malloc(EA->size)) == NULL) goto err0; /* Copy the data in. */ memcpy(*buf, EA->buf, EA->size); /* Tell the caller how many records we have. */ *nrec = elasticarray_getsize(EA, reclen); /* Success! */ return (0); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/libcperciva/datastruct/elasticarray.h000066400000000000000000000166431476705112600225650ustar00rootroot00000000000000#ifndef ELASTICARRAY_H_ #define ELASTICARRAY_H_ #include /** * Elastic Arrays are dynamically resizing arrays which remain within a * factor of 4 of the optimal size for the data they contain and have (within * a constant factor) amortized optimal running time providing that all of * the allocated space is accessed at some point. Functions return NULL or * (int)(-1) on error and set errno; other return types indicate that failure * is not possible. On error, the array will be unmodified. * * The ELASTICARRAY_DECL(type, prefix, rectype) macro can be used to create a * more friendly interface, at the expense of restricting the array to only * holding a single data type. */ /* Opaque elastic array type. */ struct elasticarray; /** * elasticarray_init(nrec, reclen): * Create and return an elastic array holding ${nrec} (uninitialized) records * of length ${reclen}. Takes O(nrec * reclen) time. The value ${reclen} * must be positive. */ struct elasticarray * elasticarray_init(size_t, size_t); /** * elasticarray_resize(EA, nrec, reclen): * Resize the elastic array pointed to by ${EA} to hold ${nrec} records of * length ${reclen}. If ${nrec} exceeds the number of records previously * held by the array, the additional records will be uninitialized. Takes * O(nrec * reclen) time. The value ${reclen} must be positive. */ int elasticarray_resize(struct elasticarray *, size_t, size_t); /** * elasticarray_getsize(EA, reclen): * Return the number of length-${reclen} records in the array, rounding down * if there is a partial record (which can only occur if elasticarray_* * functions have been called with different values of reclen). The value * ${reclen} must be positive. */ size_t elasticarray_getsize(const struct elasticarray *, size_t); /** * elasticarray_append(EA, buf, nrec, reclen): * Append to the elastic array ${EA} the ${nrec} records of length ${reclen} * stored in ${buf}. Takes O(nrec * reclen) amortized time. The value * ${reclen} must be positive. */ int elasticarray_append(struct elasticarray *, const void *, size_t, size_t); /** * elasticarray_shrink(EA, nrec, reclen): * Delete the final ${nrec} records of length ${reclen} from the elastic * array ${EA}. If there are fewer than ${nrec} records, all records * present will be deleted. The value ${reclen} must be positive. * * As an exception to the normal rule, an elastic array may occupy more than * 4 times the optimal storage immediately following an elasticarray_shrink() * call; but only if realloc(3) failed to shrink a memory allocation. */ void elasticarray_shrink(struct elasticarray *, size_t, size_t); /** * elasticarray_truncate(EA): * Release any spare space in the elastic array ${EA}. */ int elasticarray_truncate(struct elasticarray *); /** * elasticarray_get(EA, pos, reclen): * Return a pointer to record number ${pos} of length ${reclen} in the * elastic array ${EA}. Takes O(1) time. */ void * elasticarray_get(struct elasticarray *, size_t, size_t); /** * elasticarray_free(EA): * Free the elastic array ${EA}. Takes O(1) time. */ void elasticarray_free(struct elasticarray *); /** * elasticarray_export(EA, buf, nrec, reclen): * Return the data in the elastic array ${EA} as a buffer ${buf} containing * ${nrec} records of length ${reclen}. Free the elastic array ${EA}. * The value ${reclen} must be positive. */ int elasticarray_export(struct elasticarray *, void **, size_t *, size_t); /** * elasticarray_exportdup(EA, buf, nrec, reclen): * Duplicate the data in the elastic array ${EA} into a buffer ${buf} * containing ${nrec} records of length ${reclen}. (Same as _export, except * that the elastic array remains intact.) The value ${reclen} must be * positive. */ int elasticarray_exportdup(const struct elasticarray *, void **, size_t *, size_t); /** * ELASTICARRAY_DECL(type, prefix, rectype): * Declare the type ${type} and the following functions: * ${type} ${prefix}_init(size_t nrec); * int ${prefix}_resize(${type} EA, size_t nrec); * size_t ${prefix}_getsize(const ${type} EA); * int ${prefix}_append(${type} EA, const void * buf, size_t nrec); * void ${prefix}_shrink(${type} EA, size_t nrec); * int ${prefix}_truncate(${type} EA); * ${rectype} * ${prefix}_get(${type} EA, size_t pos); * void ${prefix}_iter(${type} EA, void (*)(void *)); * void ${prefix}_free(${type} EA); */ #define ELASTICARRAY_DECL(type, prefix, rectype) \ static inline struct prefix##_struct * \ prefix##_init(size_t nrec) \ { \ struct elasticarray * EA; \ \ EA = elasticarray_init(nrec, sizeof(rectype)); \ return ((struct prefix##_struct *)EA); \ } \ static inline int \ prefix##_resize(struct prefix##_struct * EA, size_t nrec) \ { \ return (elasticarray_resize((struct elasticarray *)EA, \ nrec, sizeof(rectype))); \ } \ static inline size_t \ prefix##_getsize(const struct prefix##_struct * EA) \ { \ return (elasticarray_getsize( \ (const struct elasticarray *)EA, \ sizeof(rectype))); \ } \ static inline int \ prefix##_append(struct prefix##_struct * EA, \ rectype const * buf, size_t nrec) \ { \ return (elasticarray_append((struct elasticarray *)EA, \ buf, nrec, sizeof(rectype))); \ } \ static inline void \ prefix##_shrink(struct prefix##_struct * EA, size_t nrec) \ { \ elasticarray_shrink((struct elasticarray *)EA, \ nrec, sizeof(rectype)); \ } \ static inline int \ prefix##_truncate(struct prefix##_struct * EA) \ { \ return (elasticarray_truncate( \ (struct elasticarray *)EA)); \ } \ static inline rectype * \ prefix##_get(struct prefix##_struct * EA, size_t pos) \ { \ rectype * rec; \ \ rec = elasticarray_get((struct elasticarray *)EA, \ pos, sizeof(rectype)); \ return (rec); \ } \ static inline void \ prefix##_iter(struct prefix##_struct * EA, \ void (* fp)(rectype *)) \ { \ size_t i; \ \ for (i = 0; i < prefix##_getsize(EA); i++) \ fp(prefix##_get(EA, i)); \ } \ static inline void \ prefix##_free(struct prefix##_struct * EA) \ { \ elasticarray_free((struct elasticarray *)EA); \ } \ static inline int \ prefix##_export(struct prefix##_struct * EA, rectype ** buf, \ size_t * nrec) \ { \ return (elasticarray_export((struct elasticarray *)EA, \ (void **)buf, nrec, sizeof(rectype))); \ } \ static inline int \ prefix##_exportdup(const struct prefix##_struct * EA, \ rectype ** buf, size_t * nrec) \ { \ return (elasticarray_exportdup( \ (const struct elasticarray *)EA, \ (void **)buf, nrec, sizeof(rectype))); \ } \ static void (* prefix##_dummyptr)(void); \ static inline void \ prefix##_dummy(void) \ { \ \ (void)prefix##_init; \ (void)prefix##_resize; \ (void)prefix##_getsize; \ (void)prefix##_append; \ (void)prefix##_shrink; \ (void)prefix##_truncate; \ (void)prefix##_get; \ (void)prefix##_iter; \ (void)prefix##_free; \ (void)prefix##_export; \ (void)prefix##_exportdup; \ (void)prefix##_dummyptr; \ } \ static void (* prefix##_dummyptr)(void) = prefix##_dummy; \ typedef struct prefix##_struct * type #endif /* !ELASTICARRAY_H_ */ tarsnap-1.0.41/libcperciva/datastruct/mpool.h000066400000000000000000000101201476705112600212100ustar00rootroot00000000000000#ifndef MPOOL_H_ #define MPOOL_H_ #include #include #include #include #include "ctassert.h" /** * Memory allocator cache. Memory allocations can be returned to the pool * and reused by a subsequent allocation without returning all the way to * free/malloc. In effect, this is an optimization for the case where we * know we will want another allocation of the same size soon, at the expense * of keeping memory allocated (and thus preventing any other code from * allocating the same memory). */ /* Internal data. */ struct mpool { size_t stacklen; size_t allocsize; void ** allocs; uint64_t nallocs; uint64_t nempties; int state; void ** allocs_static; void (* atexitfunc)(void); }; static inline void mpool_atexit(struct mpool * M) { /* Free all items on the stack. */ while (M->stacklen) free(M->allocs[--M->stacklen]); /* If we allocated a stack, free it. */ if (M->allocs != M->allocs_static) free(M->allocs); } static inline void * mpool_malloc(struct mpool * M, size_t len) { /* Count the total number of allocation requests. */ M->nallocs++; /* If we have an object on the stack, use that. */ if (M->stacklen) return (M->allocs[--(M->stacklen)]); /* Count allocation requests where the pool was already empty. */ M->nempties++; /* Initialize the atexit function (the first time we reach here). */ if (M->state == 0) { atexit(M->atexitfunc); M->state = 1; } /* Allocate a new object. */ return (malloc(len)); } static inline void mpool_free(struct mpool * M, void * p) { void ** allocs_new; /* Behave consistently with free(NULL). */ if (p == NULL) return; /* If we have space in the stack, cache the object. */ if (M->stacklen < M->allocsize) { M->allocs[M->stacklen++] = p; return; } /* * Autotuning: If more than 1/256 of mpool_malloc() calls resulted in * a malloc(), double the stack. */ if (M->nempties > (M->nallocs >> 8)) { /* Sanity check. */ assert(M->allocsize > 0); /* Allocate new stack and copy pointers into it. */ allocs_new = (void **)malloc(M->allocsize * 2 * sizeof(void *)); if (allocs_new) { memcpy(allocs_new, M->allocs, M->allocsize * sizeof(void *)); if (M->allocs != M->allocs_static) free(M->allocs); M->allocs = allocs_new; M->allocsize = M->allocsize * 2; M->allocs[M->stacklen++] = p; } else free(p); } else free(p); /* Reset statistics. */ M->nempties = 0; M->nallocs = 0; } /** * MPOOL(name, type, size): * Define the functions * * ${type} * mpool_${name}_malloc(void); * void mpool_${name}_free(${type} *); * * which allocate and free structures of type ${type}. A minimum of ${size} * such structures are kept cached after _free is called in order to allow * future _malloc calls to be rapidly serviced; this limit will be autotuned * upwards depending on the allocation/free pattern. * * Cached structures will be freed at program exit time in order to aid * in the detection of memory leaks. */ #define MPOOL(name, type, size) \ static void mpool_##name##_atexit(void); \ static void * mpool_##name##_static[size]; \ static struct mpool mpool_##name##_rec = \ {0, size, mpool_##name##_static, 0, 0, 0, \ mpool_##name##_static, mpool_##name##_atexit}; \ \ CTASSERT(size > 0); \ \ static void \ mpool_##name##_atexit(void) \ { \ \ mpool_atexit(&mpool_##name##_rec); \ } \ \ static inline type * \ mpool_##name##_malloc(void) \ { \ \ return (mpool_malloc(&mpool_##name##_rec, sizeof(type))); \ } \ \ static inline void \ mpool_##name##_free(type * p) \ { \ \ mpool_free(&mpool_##name##_rec, p); \ } \ \ static void (* mpool_##name##_dummyptr)(void); \ static inline void \ mpool_##name##_dummyfunc(void) \ { \ \ (void)mpool_##name##_malloc; \ (void)mpool_##name##_free; \ (void)mpool_##name##_dummyptr; \ } \ static void (* mpool_##name##_dummyptr)(void) = mpool_##name##_dummyfunc; \ struct mpool_##name##_dummy #endif /* !MPOOL_H_ */ tarsnap-1.0.41/libcperciva/datastruct/ptrheap.c000066400000000000000000000213401476705112600215260ustar00rootroot00000000000000#include #include "elasticarray.h" #include "ptrheap.h" ELASTICARRAY_DECL(PTRLIST, ptrlist, void *); struct ptrheap { int (* compar)(void *, const void *, const void *); void (* setreccookie)(void *, void *, size_t); void * cookie; PTRLIST elems; size_t nelems; }; /** * swap(elems, i, j, setreccookie, cookie): * Swap elements ${i} and ${j} in ${elems}. If ${setreccookie} is non-NULL, * invoke ${setreccookie}(${cookie}, elem, pos) for each of the elements and * their new positions in the tree. */ static void swap(PTRLIST elems, size_t i, size_t j, void (* setreccookie)(void *, void *, size_t), void * cookie) { void * tmp; /* Swap the elements. */ tmp = *ptrlist_get(elems, i); *ptrlist_get(elems, i) = *ptrlist_get(elems, j); *ptrlist_get(elems, j) = tmp; /* Notify about the moved elements. */ if (setreccookie != NULL) { setreccookie(cookie, *ptrlist_get(elems, i), i); setreccookie(cookie, *ptrlist_get(elems, j), j); } } /** * heapifyup(elems, i, compar, setreccookie, cookie): * Sift up element ${i} of the elements ${elems}, using the comparison * function ${compar} and the cookie ${cookie}. If elements move and * ${setreccookie} is non-NULL, use it to notify about the updated position * of elements in the heap. */ static void heapifyup(PTRLIST elems, size_t i, int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie) { /* Iterate up the tree. */ do { /* If we're at the root, we have nothing to do. */ if (i == 0) break; /* If this is >= its parent, we're done. */ if (compar(cookie, *ptrlist_get(elems, i), *ptrlist_get(elems, (i - 1) / 2)) >= 0) break; /* Swap with the parent. */ swap(elems, i, (i - 1) / 2, setreccookie, cookie); /* Move up the tree. */ i = (i - 1) / 2; } while (1); } /** * heapify(elems, i, N, compar, setreccookie, cookie): * Sift down element number ${i} out of ${N} of the elements ${elems}, using * the comparison function ${compar} and the cookie ${cookie}. If elements * move and ${setreccookie} is non-NULL, use it to notify about the updated * position of elements in the heap. */ static void heapify(PTRLIST elems, size_t i, size_t N, int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie) { size_t min; /* Iterate down the tree. */ do { /* Look for the minimum element out of {i, 2i+1, 2i+2}. */ min = i; /* Is this bigger than element 2i+1? */ if ((2 * i + 1 < N) && (compar(cookie, *ptrlist_get(elems, min), *ptrlist_get(elems, 2 * i + 1)) > 0)) min = 2 * i + 1; /* Is this bigger than element 2i+2? */ if ((2 * i + 2 < N) && (compar(cookie, *ptrlist_get(elems, min), *ptrlist_get(elems, 2 * i + 2)) > 0)) min = 2 * i + 2; /* If the minimum is i, we have heap-property. */ if (min == i) break; /* Move the minimum into position i. */ swap(elems, min, i, setreccookie, cookie); /* Move down the tree. */ i = min; } while (1); } /** * ptrheap_init(compar, setreccookie, cookie): * Create and return an empty heap. The function ${compar}(${cookie}, x, y) * should return less than, equal to, or greater than 0 depending on whether * x is less than, equal to, or greater than y; and if ${setreccookie} is * non-zero it will be called as ${setreccookie}(${cookie}, ${ptr}, ${rc}) to * indicate that the value ${rc} is the current record cookie for the pointer * ${ptr}. The function ${setreccookie} may not make any ptrheap_* calls. */ struct ptrheap * ptrheap_init(int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie) { /* Let ptrheap_create handle this. */ return (ptrheap_create(compar, setreccookie, cookie, 0, NULL)); } /** * ptrheap_create(compar, setreccookie, cookie, N, ptrs): * Create and return a heap, as in ptrheap_init(), but with the ${N} pointers * in ${ptrs} as heap elements. This is faster than creating an empty heap * and adding the elements individually. */ struct ptrheap * ptrheap_create(int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie, size_t N, void ** ptrs) { struct ptrheap * H; size_t i; /* Allocate structure. */ if ((H = malloc(sizeof(struct ptrheap))) == NULL) goto err0; /* Store parameters. */ H->compar = compar; H->setreccookie = setreccookie; H->cookie = cookie; /* We will have N elements. */ H->nelems = N; /* Allocate space for N heap elements. */ if ((H->elems = ptrlist_init(N)) == NULL) goto err1; /* Copy the heap elements in. */ for (i = 0; i < N; i++) *ptrlist_get(H->elems, i) = ptrs[i]; /* Turn this into a heap. */ for (i = N - 1; i < N; i--) heapify(H->elems, i, N, H->compar, NULL, H->cookie); /* Advise the caller about the record cookies. */ if (H->setreccookie != NULL) for (i = 0; i < N; i++) (H->setreccookie)(H->cookie, *ptrlist_get(H->elems, i), i); /* Success! */ return (H); err1: free(H); err0: /* Failure! */ return (NULL); } /** * ptrheap_add(H, ptr): * Add the pointer ${ptr} to the heap ${H}. */ int ptrheap_add(struct ptrheap * H, void * ptr) { /* Add the element to the end of the heap. */ if (ptrlist_append(H->elems, &ptr, 1)) goto err0; H->nelems += 1; /* Advise the caller about the current location of this record. */ if (H->setreccookie != NULL) (H->setreccookie)(H->cookie, ptr, H->nelems - 1); /* Move the new element up in the tree if necessary. */ heapifyup(H->elems, H->nelems - 1, H->compar, H->setreccookie, H->cookie); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * ptrheap_getmin(H): * Return the minimum pointer in the heap ${H}. If the heap is empty, NULL * is returned. */ void * ptrheap_getmin(struct ptrheap * H) { /* If we have any elements, the minimum is in position 0. */ if (H->nelems) return (*ptrlist_get(H->elems, 0)); else return (NULL); } /** * ptrheap_delete(H, rc): * Delete from the heap ${H} the element ptr for which the function call * setreccookie(cookie, ptr, ${rc}) was most recently made. */ void ptrheap_delete(struct ptrheap * H, size_t rc) { /* * If the element we're deleting is not at the end of the heap, * replace it with the element which is currently at the end. */ if (rc != H->nelems - 1) { /* Move ptr from position H->nelems - 1 into position rc. */ *ptrlist_get(H->elems, rc) = *ptrlist_get(H->elems, H->nelems - 1); if (H->setreccookie != NULL) (H->setreccookie)(H->cookie, *ptrlist_get(H->elems, rc), rc); /* Is this too small to be in position ${rc}? */ if ((rc > 0) && (H->compar(H->cookie, *ptrlist_get(H->elems, rc), *ptrlist_get(H->elems, (rc - 1) / 2)) < 0)) { /* Swap with the parent, and keep moving up. */ swap(H->elems, rc, (rc - 1) / 2, H->setreccookie, H->cookie); heapifyup(H->elems, (rc - 1) / 2, H->compar, H->setreccookie, H->cookie); } else { /* Maybe we need to move it down instead? */ heapify(H->elems, rc, H->nelems, H->compar, H->setreccookie, H->cookie); } } /* * We've got everything we want to keep in positions 0 .. nelems - 2, * and we have heap-nature, so all we need to do is strip off the * final pointer. */ ptrlist_shrink(H->elems, 1); H->nelems--; } /** * ptrheap_deletemin(H): * Delete the minimum element in the heap ${H}. The heap must not be empty. */ void ptrheap_deletemin(struct ptrheap * H) { /* Let ptrheap_delete handle this. */ ptrheap_delete(H, 0); } /** * ptrheap_decrease(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has decreased. */ void ptrheap_decrease(struct ptrheap * H, size_t rc) { /* Move the element up if necessary. */ heapifyup(H->elems, rc, H->compar, H->setreccookie, H->cookie); } /** * ptrheap_increase(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has increased. */ void ptrheap_increase(struct ptrheap * H, size_t rc) { /* Move the element down if necessary. */ heapify(H->elems, rc, H->nelems, H->compar, H->setreccookie, H->cookie); } /** * ptrheap_increasemin(H): * Adjust the heap ${H} to account for the fact that the (formerly) minimum * element has increased. */ void ptrheap_increasemin(struct ptrheap * H) { /* Move the element down if necessary. */ heapify(H->elems, 0, H->nelems, H->compar, H->setreccookie, H->cookie); } /** * ptrheap_free(H): * Free the pointer heap ${H}. */ void ptrheap_free(struct ptrheap * H) { /* Behave consistently with free(NULL). */ if (H == NULL) return; ptrlist_free(H->elems); free(H); } tarsnap-1.0.41/libcperciva/datastruct/ptrheap.h000066400000000000000000000063331476705112600215400ustar00rootroot00000000000000#ifndef PTRHEAP_H_ #define PTRHEAP_H_ #include /** * Pointer-heap data structure. Arbitrary pointers can be inserted and are * compared using a provided callback; the usual heapy getmin / increasemin / * deletemin algorithms are supported. To use three additional functions, * ptrheap_{delete, increase, decrease}, a setreccookie callback needs to be * provided. These functions require a record cookie to identify the element * to operate upon; each time a record's record cookie changes, the * setreccookie callback will be called. Functions return NULL or (int)(-1) * on error and set errno; other return types indicate that failure is not * possible. On error, the heap will be unmodified. */ /* Opaque pointer-heap type. */ struct ptrheap; /** * ptrheap_init(compar, setreccookie, cookie): * Create and return an empty heap. The function ${compar}(${cookie}, x, y) * should return less than, equal to, or greater than 0 depending on whether * x is less than, equal to, or greater than y; and if ${setreccookie} is * non-zero it will be called as ${setreccookie}(${cookie}, ${ptr}, ${rc}) to * indicate that the value ${rc} is the current record cookie for the pointer * ${ptr}. The function ${setreccookie} may not make any ptrheap_* calls. */ struct ptrheap * ptrheap_init(int (*)(void *, const void *, const void *), void (*)(void *, void *, size_t), void *); /** * ptrheap_create(compar, setreccookie, cookie, N, ptrs): * Create and return a heap, as in ptrheap_init(), but with the ${N} pointers * in ${ptrs} as heap elements. This is faster than creating an empty heap * and adding the elements individually. */ struct ptrheap * ptrheap_create(int (*)(void *, const void *, const void *), void (*)(void *, void *, size_t), void *, size_t, void **); /** * ptrheap_add(H, ptr): * Add the pointer ${ptr} to the heap ${H}. */ int ptrheap_add(struct ptrheap *, void *); /** * ptrheap_getmin(H): * Return the minimum pointer in the heap ${H}. If the heap is empty, NULL * is returned. */ void * ptrheap_getmin(struct ptrheap *); /** * ptrheap_delete(H, rc): * Delete from the heap ${H} the element ptr for which the function call * setreccookie(cookie, ptr, ${rc}) was most recently made. */ void ptrheap_delete(struct ptrheap *, size_t); /** * ptrheap_deletemin(H): * Delete the minimum element in the heap ${H}. The heap must not be empty. */ void ptrheap_deletemin(struct ptrheap *); /** * ptrheap_decrease(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has decreased. */ void ptrheap_decrease(struct ptrheap *, size_t); /** * ptrheap_increase(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has increased. */ void ptrheap_increase(struct ptrheap *, size_t); /** * ptrheap_increasemin(H): * Adjust the heap ${H} to account for the fact that the (formerly) minimum * element has increased. */ void ptrheap_increasemin(struct ptrheap *); /** * ptrheap_free(H): * Free the pointer heap ${H}. */ void ptrheap_free(struct ptrheap *); #endif /* !PTRHEAP_H_ */ tarsnap-1.0.41/libcperciva/datastruct/timerqueue.c000066400000000000000000000115521476705112600222540ustar00rootroot00000000000000#include #include #include #include "ptrheap.h" #include "timerqueue.h" struct timerqueue { struct ptrheap * H; }; struct timerrec { struct timeval tv; size_t rc; void * ptr; }; /* Compare two timevals. */ static int tvcmp(const struct timeval * x, const struct timeval * y) { /* Does one have more seconds? */ if (x->tv_sec > y->tv_sec) return (1); if (x->tv_sec < y->tv_sec) return (-1); /* Does one have more microseconds? */ if (x->tv_usec > y->tv_usec) return (1); if (x->tv_usec < y->tv_usec) return (-1); /* They must be equal. */ return (0); } /* Record-comparison callback from ptrheap. */ static int compar(void * cookie, const void * x, const void * y) { const struct timerrec * _x = x; const struct timerrec * _y = y; (void)cookie; /* UNUSED */ /* Compare the times. */ return (tvcmp(&_x->tv, &_y->tv)); } /* Cookie-recording callback from ptrheap. */ static void setreccookie(void * cookie, void * ptr, size_t rc) { struct timerrec * rec = ptr; (void)cookie; /* UNUSED */ rec->rc = rc; } /** * timerqueue_init(void): * Create and return an empty timer priority queue. */ struct timerqueue * timerqueue_init(void) { struct timerqueue * Q; /* Allocate structure. */ if ((Q = malloc(sizeof(struct timerqueue))) == NULL) goto err0; /* Allocate heap. */ if ((Q->H = ptrheap_init(compar, setreccookie, Q)) == NULL) goto err1; /* Success! */ return (Q); err1: free(Q); err0: /* Failure! */ return (NULL); } /** * timerqueue_add(Q, tv, ptr): * Add the pair (${tv}, ${ptr}) to the priority queue ${Q}. Return a cookie * which can be passed to timerqueue_delete() or timerqueue_increase(). */ void * timerqueue_add(struct timerqueue * Q, const struct timeval * tv, void * ptr) { struct timerrec * r; /* Allocate (timeval, ptr) pair record. */ if ((r = malloc(sizeof(struct timerrec))) == NULL) goto err0; /* Fill in values. */ memcpy(&r->tv, tv, sizeof(struct timeval)); r->ptr = ptr; /* * Add the record to the heap. The value r->rc will be filled in * by setreccookie which will be called by ptrheap_add. */ if (ptrheap_add(Q->H, r)) goto err1; /* Success! */ return (r); err1: free(r); err0: /* Failure! */ return (NULL); } /** * timerqueue_delete(Q, cookie): * Delete the (timeval, ptr) pair associated with the cookie ${cookie} from * the priority queue ${Q}. */ void timerqueue_delete(struct timerqueue * Q, void * cookie) { struct timerrec * r = cookie; /* Remove the record from the heap. */ ptrheap_delete(Q->H, r->rc); /* Free the record. */ free(r); } /** * timerqueue_increase(Q, cookie, tv): * Increase the timer associated with the cookie ${cookie} in the priority * queue ${Q} to ${tv}. */ void timerqueue_increase(struct timerqueue * Q, void * cookie, const struct timeval * tv) { struct timerrec * r = cookie; /* Adjust timer value. */ memcpy(&r->tv, tv, sizeof(struct timeval)); /* Inform the heap that the record value has increased. */ ptrheap_increase(Q->H, r->rc); } /** * timerqueue_getmin(Q): * Return a pointer to the least timeval in ${Q}, or NULL if the priority * queue is empty. The pointer will remain valid until the next call to a * timerqueue_* function. This function cannot fail. */ const struct timeval * timerqueue_getmin(struct timerqueue * Q) { struct timerrec * r; /* Get the minimum element from the heap. */ r = ptrheap_getmin(Q->H); /* If we have an element, return its timeval; otherwise, NULL. */ if (r != NULL) return (&r->tv); else return (NULL); } /** * timerqueue_getptr(Q, tv): * If the least timeval in ${Q} is less than or equal to ${tv}, return the * associated pointer and remove the pair from the priority queue. If not, * return NULL. This function cannot fail. */ void * timerqueue_getptr(struct timerqueue * Q, const struct timeval * tv) { struct timerrec * r; void * ptr; /* * Get the minimum element from the heap. Return NULL if the heap * has no minimum element (i.e., is empty). */ if ((r = ptrheap_getmin(Q->H)) == NULL) return (NULL); /* If the minimum timeval is greater than ${tv}, return NULL. */ if (tvcmp(&r->tv, tv) > 0) return (NULL); /* Remove this record from the heap. */ ptrheap_deletemin(Q->H); /* Extract its pointer. */ ptr = r->ptr; /* Free the record. */ free(r); /* * And finally return the pointer which was associated with the * (formerly) minimum timeval in the heap. */ return (ptr); } /** * timerqueue_free(Q): * Free the timer priority queue ${Q}. */ void timerqueue_free(struct timerqueue * Q) { struct timerrec * r; /* Behave consistently with free(NULL). */ if (Q == NULL) return; /* Extract elements from the heap and free them one by one. */ while ((r = ptrheap_getmin(Q->H)) != NULL) { free(r); ptrheap_deletemin(Q->H); } /* Free the heap. */ ptrheap_free(Q->H); /* Free the timer priority queue structure. */ free(Q); } tarsnap-1.0.41/libcperciva/datastruct/timerqueue.h000066400000000000000000000033331476705112600222570ustar00rootroot00000000000000#ifndef TIMERQUEUE_H_ #define TIMERQUEUE_H_ #include /* Timer priority queue. Contains (timeval, ptr) pairs. */ /* Opaque timer priority queue type. */ struct timerqueue; /** * timerqueue_init(void): * Create and return an empty timer priority queue. */ struct timerqueue * timerqueue_init(void); /** * timerqueue_add(Q, tv, ptr): * Add the pair (${tv}, ${ptr}) to the priority queue ${Q}. Return a cookie * which can be passed to timerqueue_delete() or timerqueue_increase(). */ void * timerqueue_add(struct timerqueue *, const struct timeval *, void *); /** * timerqueue_delete(Q, cookie): * Delete the (timeval, ptr) pair associated with the cookie ${cookie} from * the priority queue ${Q}. */ void timerqueue_delete(struct timerqueue *, void *); /** * timerqueue_increase(Q, cookie, tv): * Increase the timer associated with the cookie ${cookie} in the priority * queue ${Q} to ${tv}. */ void timerqueue_increase(struct timerqueue *, void *, const struct timeval *); /** * timerqueue_getmin(Q): * Return a pointer to the least timeval in ${Q}, or NULL if the priority * queue is empty. The pointer will remain valid until the next call to a * timerqueue_* function. This function cannot fail. */ const struct timeval * timerqueue_getmin(struct timerqueue *); /** * timerqueue_getptr(Q, tv): * If the least timeval in ${Q} is less than or equal to ${tv}, return the * associated pointer and remove the pair from the priority queue. If not, * return NULL. This function cannot fail. */ void * timerqueue_getptr(struct timerqueue *, const struct timeval *); /** * timerqueue_free(Q): * Free the timer priority queue ${Q}. */ void timerqueue_free(struct timerqueue *); #endif /* !TIMERQUEUE_H_ */ tarsnap-1.0.41/libcperciva/events/000077500000000000000000000000001476705112600170455ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/events/events.c000066400000000000000000000123031476705112600205140ustar00rootroot00000000000000#include #include #include #include #include "mpool.h" #include "events.h" #include "events_internal.h" /* Event structure. */ struct eventrec { int (* func)(void *); void * cookie; }; MPOOL(eventrec, struct eventrec, 4096); /* Zero timeval, for use with non-blocking event runs. */ static const struct timeval tv_zero = {0, 0}; /* We want to interrupt a running event loop. */ static volatile sig_atomic_t interrupt_requested = 0; /** * events_mkrec(func, cookie): * Package ${func}, ${cookie} into a struct eventrec. */ struct eventrec * events_mkrec(int (* func)(void *), void * cookie) { struct eventrec * r; /* Allocate structure. */ if ((r = mpool_eventrec_malloc()) == NULL) goto err0; /* Initialize. */ r->func = func; r->cookie = cookie; /* Success! */ return (r); err0: /* Failure! */ return (NULL); } /** * events_freerec(r): * Free the eventrec ${r}. */ void events_freerec(struct eventrec * r) { mpool_eventrec_free(r); } /* Do an event. This makes events_run cleaner. */ static inline int doevent(struct eventrec * r) { int rc; /* Invoke the callback. */ rc = (r->func)(r->cookie); /* Free the event record. */ mpool_eventrec_free(r); /* Return the status code from the callback. */ return (rc); } /** * events_run(void): * Run events. Events registered via events_immediate_register() will be run * first, in order of increasing ${prio} values; then events associated with * ready sockets registered via events_network_register(); finally, events * associated with expired timers registered via events_timer_register() will * be run. If any event function returns a non-zero result, no further events * will be run and said non-zero result will be returned; on error, * -1 will be returned. May be interrupted by events_interrupt(), in which * case 0 will be returned. If there are runnable events, events_run() is * guaranteed to run at least one; but it may return while there are still * more runnable events. */ static int events_run_internal(void) { struct eventrec * r; struct timeval * tv; struct timeval tv2; int rc = 0; /* If we have any immediate events, process them and return. */ if ((r = events_immediate_get()) != NULL) { while (r != NULL) { /* Process the event. */ if ((rc = doevent(r)) != 0) goto done; /* Interrupt loop if requested. */ if (interrupt_requested) goto done; /* Get the next event. */ r = events_immediate_get(); } /* We've processed at least one event; time to return. */ goto done; } /* * Figure out the maximum duration to block, and wait up to that * duration for network events to become available. */ if (events_timer_min(&tv)) goto err0; if (events_network_select(tv, &interrupt_requested)) goto err1; free(tv); /* * Check for available immediate events, network events, and timer * events, in that order of priority; exit only when no more events * are available or when interrupted. */ do { /* Interrupt loop if requested. */ if (interrupt_requested) goto done; /* Run an immediate event, if one is available. */ if ((r = events_immediate_get()) != NULL) { if ((rc = doevent(r)) != 0) goto done; continue; } /* Run a network event, if one is available. */ if ((r = events_network_get()) != NULL) { if ((rc = doevent(r)) != 0) goto done; continue; } /* Check if any new network events are available. */ memcpy(&tv2, &tv_zero, sizeof(struct timeval)); if (events_network_select(&tv2, &interrupt_requested)) goto err0; if ((r = events_network_get()) != NULL) { if ((rc = doevent(r)) != 0) goto done; continue; } /* Run a timer event, if one is available. */ if (events_timer_get(&r)) goto err0; if (r != NULL) { if ((rc = doevent(r)) != 0) goto done; continue; } /* No events available. */ break; } while (1); done: /* Success! */ return (rc); err1: free(tv); err0: /* Failure! */ return (-1); } /* Wrapper function for events_run to reset interrupt_requested. */ int events_run(void) { int rc; /* Call the real function. */ rc = events_run_internal(); /* Reset interrupt_requested after quitting the loop. */ interrupt_requested = 0; /* Return status. */ return (rc); } /** * events_spin(done): * Call events_run() until ${done} is non-zero (and return 0), an error occurs * (and return -1), or a callback returns a non-zero status (and return the * status code from the callback). May be interrupted by events_interrupt() * (and return 0). */ int events_spin(const int * done) { int rc = 0; /* Loop until we're done or have a non-zero status. */ while ((done[0] == 0) && (rc == 0) && (interrupt_requested == 0)) { /* Run events. */ rc = events_run_internal(); } /* Reset interrupt_requested after quitting the loop. */ interrupt_requested = 0; /* Return status code. */ return (rc); } /** * events_interrupt(void): * Halt the event loop after finishing the current event. This function can * be safely called from within a signal handler. */ void events_interrupt(void) { /* Interrupt the event loop. */ interrupt_requested = 1; } /** * events_shutdown(void): * Deprecated function; does nothing. */ void events_shutdown(void) { } tarsnap-1.0.41/libcperciva/events/events.h000066400000000000000000000076331476705112600205330ustar00rootroot00000000000000#ifndef EVENTS_H_ #define EVENTS_H_ #include /** * events_immediate_register(func, cookie, prio): * Register ${func}(${cookie}) to be run the next time events_run() is * invoked, after immediate events with smaller ${prio} values and before * events with larger ${prio} values. The value ${prio} must be in the range * [0, 31]. Return a cookie which can be passed to events_immediate_cancel(). */ void * events_immediate_register(int (*)(void *), void *, int); /** * events_immediate_cancel(cookie): * Cancel the immediate event for which the cookie ${cookie} was returned by * events_immediate_register(). */ void events_immediate_cancel(void *); /* "op" parameter to events_network_register(). */ #define EVENTS_NETWORK_OP_READ 0 #define EVENTS_NETWORK_OP_WRITE 1 /** * events_network_register(func, cookie, s, op): * Register ${func}(${cookie}) to be run when socket ${s} is ready for * reading or writing depending on whether ${op} is EVENTS_NETWORK_OP_READ or * EVENTS_NETWORK_OP_WRITE. If there is already an event registration for * this ${s}/${op} pair, errno will be set to EEXIST and the function will * fail. */ int events_network_register(int (*)(void *), void *, int, int); /** * events_network_cancel(s, op): * Cancel the event registered for the socket/operation pair ${s}/${op}. If * there is no such registration, errno will be set to ENOENT and the * function will fail. */ int events_network_cancel(int, int); /** * events_network_selectstats(N, mu, va, max): * Return statistics on the inter-select durations since the last time this * function was called. */ void events_network_selectstats(double *, double *, double *, double *); /** * events_timer_register(func, cookie, timeo): * Register ${func}(${cookie}) to be run ${timeo} in the future. Return a * cookie which can be passed to events_timer_cancel() or events_timer_reset(). */ void * events_timer_register(int (*)(void *), void *, const struct timeval *); /** * events_timer_register_double(func, cookie, timeo): * As events_timer_register(), but ${timeo} is a double-precision * floating-point value specifying a number of seconds. */ void * events_timer_register_double(int (*)(void *), void *, double); /** * events_timer_cancel(cookie): * Cancel the timer for which the cookie ${cookie} was returned by * events_timer_register(). */ void events_timer_cancel(void *); /** * events_timer_reset(cookie): * Reset the timer for which the cookie ${cookie} was returned by * events_timer_register() to its initial value. */ int events_timer_reset(void *); /** * events_run(void): * Run events. Events registered via events_immediate_register() will be run * first, in order of increasing ${prio} values; then events associated with * ready sockets registered via events_network_register(); finally, events * associated with expired timers registered via events_timer_register() will * be run. If any event function returns a non-zero result, no further events * will be run and said non-zero result will be returned; on error, * -1 will be returned. May be interrupted by events_interrupt(), in which * case 0 will be returned. If there are runnable events, events_run() is * guaranteed to run at least one; but it may return while there are still * more runnable events. */ int events_run(void); /** * events_spin(done): * Call events_run() until ${done} is non-zero (and return 0), an error occurs * (and return -1), or a callback returns a non-zero status (and return the * status code from the callback). May be interrupted by events_interrupt() * (and return 0). */ int events_spin(const int *); /** * events_interrupt(void): * Halt the event loop after finishing the current event. This function can * be safely called from within a signal handler. */ void events_interrupt(void); /** * events_shutdown(void): * Deprecated function; does nothing. */ void events_shutdown(void); #endif /* !EVENTS_H_ */ tarsnap-1.0.41/libcperciva/events/events_immediate.c000066400000000000000000000073211476705112600225360ustar00rootroot00000000000000#include #include #include "mpool.h" #include "queue.h" #include "events.h" #include "events_internal.h" struct eventq { struct eventrec * r; TAILQ_ENTRY(eventq) entries; int prio; }; MPOOL(eventq, struct eventq, 4096); /* First nodes in the linked lists. */ static TAILQ_HEAD(tailhead, eventq) heads[32] = { TAILQ_HEAD_INITIALIZER(heads[0]), TAILQ_HEAD_INITIALIZER(heads[1]), TAILQ_HEAD_INITIALIZER(heads[2]), TAILQ_HEAD_INITIALIZER(heads[3]), TAILQ_HEAD_INITIALIZER(heads[4]), TAILQ_HEAD_INITIALIZER(heads[5]), TAILQ_HEAD_INITIALIZER(heads[6]), TAILQ_HEAD_INITIALIZER(heads[7]), TAILQ_HEAD_INITIALIZER(heads[8]), TAILQ_HEAD_INITIALIZER(heads[9]), TAILQ_HEAD_INITIALIZER(heads[10]), TAILQ_HEAD_INITIALIZER(heads[11]), TAILQ_HEAD_INITIALIZER(heads[12]), TAILQ_HEAD_INITIALIZER(heads[13]), TAILQ_HEAD_INITIALIZER(heads[14]), TAILQ_HEAD_INITIALIZER(heads[15]), TAILQ_HEAD_INITIALIZER(heads[16]), TAILQ_HEAD_INITIALIZER(heads[17]), TAILQ_HEAD_INITIALIZER(heads[18]), TAILQ_HEAD_INITIALIZER(heads[19]), TAILQ_HEAD_INITIALIZER(heads[20]), TAILQ_HEAD_INITIALIZER(heads[21]), TAILQ_HEAD_INITIALIZER(heads[22]), TAILQ_HEAD_INITIALIZER(heads[23]), TAILQ_HEAD_INITIALIZER(heads[24]), TAILQ_HEAD_INITIALIZER(heads[25]), TAILQ_HEAD_INITIALIZER(heads[26]), TAILQ_HEAD_INITIALIZER(heads[27]), TAILQ_HEAD_INITIALIZER(heads[28]), TAILQ_HEAD_INITIALIZER(heads[29]), TAILQ_HEAD_INITIALIZER(heads[30]), TAILQ_HEAD_INITIALIZER(heads[31]) }; /* For i < minq, heads[i] == NULL. */ static int minq = 32; /** * events_immediate_register(func, cookie, prio): * Register ${func}(${cookie}) to be run the next time events_run() is * invoked, after immediate events with smaller ${prio} values and before * events with larger ${prio} values. The value ${prio} must be in the range * [0, 31]. Return a cookie which can be passed to events_immediate_cancel(). */ void * events_immediate_register(int (* func)(void *), void * cookie, int prio) { struct eventrec * r; struct eventq * q; /* Sanity check. */ assert((prio >= 0) && (prio < 32)); /* Bundle into an eventrec record. */ if ((r = events_mkrec(func, cookie)) == NULL) goto err0; /* Create a linked list node. */ if ((q = mpool_eventq_malloc()) == NULL) goto err1; q->r = r; q->prio = prio; /* Add to the queue. */ TAILQ_INSERT_TAIL(&heads[prio], q, entries); /* Update minq if necessary. */ if (prio < minq) minq = prio; /* Success! */ return (q); err1: events_freerec(r); err0: /* Failure! */ return (NULL); } /** * events_immediate_cancel(cookie): * Cancel the immediate event for which the cookie ${cookie} was returned by * events_immediate_register(). */ void events_immediate_cancel(void * cookie) { struct eventq * q = cookie; int prio = q->prio; /* Remove it from the list. */ TAILQ_REMOVE(&heads[prio], q, entries); /* Free the eventrec. */ events_freerec(q->r); /* Return the node to the malloc pool. */ mpool_eventq_free(q); } /** * events_immediate_get(void): * Remove and return an eventrec structure from the immediate event queue, * or return NULL if there are no such events. The caller is responsible for * freeing the returned memory. */ struct eventrec * events_immediate_get(void) { struct eventq * q; struct eventrec * r; /* Advance past priorities which have no events. */ while ((minq < 32) && (TAILQ_EMPTY(&heads[minq]))) minq++; /* Are there any events? */ if (minq == 32) return (NULL); /* * Remove the first node from the highest priority non-empty linked * list. */ q = TAILQ_FIRST(&heads[minq]); TAILQ_REMOVE(&heads[minq], q, entries); /* Extract the eventrec. */ r = q->r; /* Return the node to the malloc pool. */ mpool_eventq_free(q); /* Return the eventrec. */ return (r); } tarsnap-1.0.41/libcperciva/events/events_internal.h000066400000000000000000000050221476705112600224150ustar00rootroot00000000000000#ifndef EVENTS_INTERNAL_H_ #define EVENTS_INTERNAL_H_ #include #include /* Opaque event structure. */ struct eventrec; /** * events_mkrec(func, cookie): * Package ${func}, ${cookie} into a struct eventrec. */ struct eventrec * events_mkrec(int (*)(void *), void *); /** * events_freerec(r): * Free the eventrec ${r}. */ void events_freerec(struct eventrec *); /** * events_immediate_get(void): * Remove and return an eventrec structure from the immediate event queue, * or return NULL if there are no such events. The caller is responsible for * freeing the returned memory. */ struct eventrec * events_immediate_get(void); /** * events_network_select(tv, interrupt_requested): * Check for socket readiness events, waiting up to ${tv} time if there are * no sockets immediately ready, or indefinitely if ${tv} is NULL. The value * stored in ${tv} may be modified. If ${*interrupt_requested} is non-zero * and a signal is received, exit. */ int events_network_select(const struct timeval *, const volatile sig_atomic_t *); /** * events_network_selectstats_startclock(void): * Start the inter-select duration clock: There is a selectable event. */ void events_network_selectstats_startclock(void); /** * events_network_selectstats_stopclock(void): * Stop the inter-select duration clock: There are no selectable events. */ void events_network_selectstats_stopclock(void); /** * events_network_selectstats_select(void): * Update inter-select duration statistics in relation to an upcoming * select(2) call. */ void events_network_selectstats_select(void); /** * events_network_get(void): * Find a socket readiness event which was identified by a previous call to * events_network_select, and return it as an eventrec structure; or return * NULL if there are no such events available. The caller is responsible for * freeing the returned memory. */ struct eventrec * events_network_get(void); /** * events_timer_min(timeo): * Return via ${timeo} a pointer to the minimum time which must be waited * before a timer will expire; or to NULL if there are no timers. The caller * is responsible for freeing the returned pointer. */ int events_timer_min(struct timeval **); /** * events_timer_get(r): * Return via ${r} a pointer to an eventrec structure corresponding to an * expired timer, and delete said timer; or to NULL if there are no expired * timers. The caller is responsible for freeing the returned pointer. */ int events_timer_get(struct eventrec **); #endif /* !EVENTS_INTERNAL_H_ */ tarsnap-1.0.41/libcperciva/events/events_network.c000066400000000000000000000277221476705112600223000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "ctassert.h" #include "elasticarray.h" #include "warnp.h" #include "events.h" #include "events_internal.h" /* * Sanity checks on the nfds_t type: POSIX simply says "an unsigned integer * type used for the number of file descriptors", but it doesn't make sense * for it to be larger than size_t unless there's an undocumented limit on * the number of descriptors which can be polled (since poll takes an array, * the size of which must fit into a size_t); and nfds_t should be able to * store the value INT_MAX + 1 (in case every possible file descriptor is in * use and being polled for). */ CTASSERT((nfds_t)(-1) <= (size_t)(-1)); CTASSERT((nfds_t)((size_t)(INT_MAX) + 1) == (size_t)(INT_MAX) + 1); /* Structure for holding readability and writability events for a socket. */ struct socketrec { struct eventrec * reader; struct eventrec * writer; size_t pollpos; }; /* List of sockets. */ ELASTICARRAY_DECL(SOCKETLIST, socketlist, struct socketrec); static SOCKETLIST S = NULL; /* Poll structures. */ static struct pollfd * fds; /* Number of poll structures allocated in array. */ static size_t fds_alloc; /* Number of poll structures initialized. */ static size_t nfds; /* Position to which events_network_get has scanned in *fds. */ static size_t fdscanpos; /** * Invariants: * 1. Initialized entries in S and fds point to each other: * S[i].pollpos < nfds ==> fds[S[i].pollpos].fd == i * j < nfds ==> S[fds[j].fd].pollpos == j * 2. Descriptors with events registered are in the right place: * S[i].reader != NULL ==> S[i].pollpos < nfds * S[i].writer != NULL ==> S[i].pollpos < nfds * 3. Descriptors without events registered aren't in the way: * (S[i].reader == NULL && S[i].writer == NULL) ==> S[i].pollpos == -1 * 4. Descriptors with events registered have the right masks: * S[i].reader != NULL <==> (fds[S[i].pollpos].events & POLLIN) != 0 * S[i].writer != NULL <==> (fds[S[i].pollpos].events & POLLOUT) != 0 * 5. We don't have events ready which we don't want: * (fds[j].revents & (POLLIN | POLLOUT) & (~fds[j].events)) == 0 * 6. Returned events are in position to be scanned later: * fds[j].revents != 0 ==> f < fdscanpos. */ static void events_network_shutdown(void); /* Initialize data structures if we haven't already done so. */ static int init(void) { /* If we're already initialized, do nothing. */ if (S != NULL) goto done; /* Initialize the socket list. */ if ((S = socketlist_init(0)) == NULL) goto err0; /* We have no poll structures allocated or initialized. */ fds = NULL; fds_alloc = nfds = fdscanpos = 0; /* Clean up the socket list at exit. */ if (atexit(events_network_shutdown)) goto err0; done: /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Grow the socket list and initialize new records. */ static int growsocketlist(size_t nrec) { size_t i; /* Get the old size. */ i = socketlist_getsize(S); /* Grow the list. */ if (socketlist_resize(S, nrec)) goto err0; /* Initialize new members. */ for (; i < nrec; i++) { socketlist_get(S, i)->reader = NULL; socketlist_get(S, i)->writer = NULL; socketlist_get(S, i)->pollpos = (size_t)(-1); } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Add a new descriptor to the pollfd array. */ static int growpollfd(size_t fd) { size_t new_fds_alloc; struct pollfd * new_fds; /* We should not be called if the descriptor is already listed. */ assert(socketlist_get(S, fd)->pollpos == (size_t)(-1)); /* Expand the pollfd allocation if needed. */ if (fds_alloc == nfds) { new_fds_alloc = fds_alloc == 0 ? 16 : fds_alloc * 2; if (new_fds_alloc > SIZE_MAX / sizeof(struct pollfd)) { errno = ENOMEM; goto err0; } if ((new_fds = realloc(fds, new_fds_alloc * sizeof(struct pollfd))) == NULL) goto err0; fds = new_fds; fds_alloc = new_fds_alloc; } /* Sanity-check. */ assert(nfds < fds_alloc); assert(fd < INT_MAX); /* Initialize pollfd structure. */ fds[nfds].fd = (int)fd; fds[nfds].events = 0; fds[nfds].revents = 0; /* Point at pollfd structure. */ socketlist_get(S, fd)->pollpos = nfds; /* We now have one more pollfd structure. */ nfds++; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Clear a bit from a pollfd and maintain invariants. */ static void clearbit(size_t pollpos, short bit) { /* Clear the bit. */ fds[pollpos].events &= (short)(~bit); fds[pollpos].revents &= (short)(~bit); /* Is this pollfd in the way? */ if (fds[pollpos].events == 0) { /* Clear the descriptor's pollpos pointer. */ socketlist_get(S, (size_t)fds[pollpos].fd)->pollpos = (size_t)(-1); /* If this wasn't the last pollfd, move another one up. */ if (pollpos != nfds - 1) { memcpy(&fds[pollpos], &fds[nfds-1], sizeof(struct pollfd)); socketlist_get(S, (size_t)fds[pollpos].fd)->pollpos = pollpos; } /* Shrink the pollfd array. */ nfds--; } } /** * events_network_register(func, cookie, s, op): * Register ${func}(${cookie}) to be run when socket ${s} is ready for * reading or writing depending on whether ${op} is EVENTS_NETWORK_OP_READ or * EVENTS_NETWORK_OP_WRITE. If there is already an event registration for * this ${s}/${op} pair, errno will be set to EEXIST and the function will * fail. */ int events_network_register(int (* func)(void *), void * cookie, int s, int op) { struct eventrec ** r; /* Initialize if necessary. */ if (init()) goto err0; /* Sanity-check socket number. */ if (s < 0) { warn0("Invalid file descriptor for network event: %d", s); goto err0; } /* Sanity-check operation. */ if ((op != EVENTS_NETWORK_OP_READ) && (op != EVENTS_NETWORK_OP_WRITE)) { warn0("Invalid operation for network event: %d", op); goto err0; } /* Grow the array if necessary. */ if (((size_t)(s) >= socketlist_getsize(S)) && (growsocketlist((size_t)s + 1) != 0)) goto err0; /* Look up the relevant event pointer. */ if (op == EVENTS_NETWORK_OP_READ) r = &socketlist_get(S, (size_t)s)->reader; else r = &socketlist_get(S, (size_t)s)->writer; /* Error out if we already have an event registered. */ if (*r != NULL) { errno = EEXIST; goto err0; } /* Register the new event. */ if ((*r = events_mkrec(func, cookie)) == NULL) goto err0; /* If we had no events registered, start a clock. */ if (nfds == 0) events_network_selectstats_startclock(); /* If this descriptor isn't in the pollfd array, add it. */ if (socketlist_get(S, (size_t)s)->pollpos == (size_t)(-1)) { if (growpollfd((size_t)s)) goto err1; } /* Set the appropriate event flag. */ if (op == EVENTS_NETWORK_OP_READ) fds[socketlist_get(S, (size_t)s)->pollpos].events |= POLLIN; else fds[socketlist_get(S, (size_t)s)->pollpos].events |= POLLOUT; /* Success! */ return (0); err1: events_freerec(*r); *r = NULL; err0: /* Failure! */ return (-1); } /** * events_network_cancel(s, op): * Cancel the event registered for the socket/operation pair ${s}/${op}. If * there is no such registration, errno will be set to ENOENT and the * function will fail. */ int events_network_cancel(int s, int op) { struct eventrec ** r; /* Initialize if necessary. */ if (init()) goto err0; /* Sanity-check socket number. */ if (s < 0) { warn0("Invalid file descriptor for network event: %d", s); goto err0; } /* Sanity-check operation. */ if ((op != EVENTS_NETWORK_OP_READ) && (op != EVENTS_NETWORK_OP_WRITE)) { warn0("Invalid operation for network event: %d", op); goto err0; } /* We have no events registered beyond the end of the array. */ if ((size_t)(s) >= socketlist_getsize(S)) { errno = ENOENT; goto err0; } /* Look up the relevant event pointer. */ if (op == EVENTS_NETWORK_OP_READ) r = &socketlist_get(S, (size_t)s)->reader; else r = &socketlist_get(S, (size_t)s)->writer; /* Check if we have an event. */ if (*r == NULL) { errno = ENOENT; goto err0; } /* Free the event. */ events_freerec(*r); *r = NULL; /* Clear the appropriate pollfd bit(s). */ if (op == EVENTS_NETWORK_OP_READ) clearbit(socketlist_get(S, (size_t)s)->pollpos, POLLIN); else clearbit(socketlist_get(S, (size_t)s)->pollpos, POLLOUT); /* If that was the last remaining event, stop the clock. */ if (nfds == 0) events_network_selectstats_stopclock(); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * events_network_select(tv, interrupt_requested): * Check for socket readiness events, waiting up to ${tv} time if there are * no sockets immediately ready, or indefinitely if ${tv} is NULL. The value * stored in ${tv} may be modified. If ${*interrupt_requested} is non-zero * and a signal is received, exit. */ int events_network_select(const struct timeval * tv, const volatile sig_atomic_t * interrupt_requested) { int timeout; /* Initialize if necessary. */ if (init()) goto err0; /* * Convert timeout to an integer number of ms. We round up in order * to avoid creating busy loops when 0 < ${tv} < 1 ms. */ if (tv == NULL) timeout = -1; else if (tv->tv_sec >= INT_MAX / 1000) timeout = INT_MAX; else timeout = (int)(tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000); /* We're about to call poll! */ events_network_selectstats_select(); /* Poll. */ while (poll(fds, (nfds_t)nfds, timeout) == -1) { /* EINTR is harmless, unless we've requested an interrupt. */ if (errno == EINTR) { if (*interrupt_requested) break; continue; } /* Anything else is an error. */ warnp("poll()"); goto err0; } /* If we have any events registered, start the clock again. */ if (nfds > 0) events_network_selectstats_startclock(); /* Start scanning at the last registered descriptor and work down. */ fdscanpos = nfds - 1; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * events_network_get(void): * Find a socket readiness event which was identified by a previous call to * events_network_select, and return it as an eventrec structure; or return * NULL if there are no such events available. The caller is responsible for * freeing the returned memory. */ struct eventrec * events_network_get(void) { struct eventrec * r; /* We haven't found any events yet. */ r = NULL; /* Scan through the pollfds looking for ready descriptors. */ for (; fdscanpos < nfds; fdscanpos--) { /* Did we poll on an invalid descriptor? */ assert((fds[fdscanpos].revents & POLLNVAL) == 0); /* * If either POLLERR ("an exceptional condition") or POLLHUP * ("has been disconnected") is set, then we should invoke * whatever callbacks we have available. */ if (fds[fdscanpos].revents & (POLLERR | POLLHUP)) { fds[fdscanpos].revents &= ~(POLLERR | POLLHUP); fds[fdscanpos].revents |= fds[fdscanpos].events; } /* Are we ready for reading? */ if (fds[fdscanpos].revents & POLLIN) { r = socketlist_get(S, (size_t)fds[fdscanpos].fd)->reader; socketlist_get(S, (size_t)fds[fdscanpos].fd)->reader = NULL; clearbit(fdscanpos, POLLIN); break; } /* Are we ready for writing? */ if (fds[fdscanpos].revents & POLLOUT) { r = socketlist_get(S, (size_t)fds[fdscanpos].fd)->writer; socketlist_get(S, (size_t)fds[fdscanpos].fd)->writer = NULL; clearbit(fdscanpos, POLLOUT); break; } } /* If we're returning the last registered event, stop the clock. */ if ((r != NULL) && (nfds == 0)) events_network_selectstats_stopclock(); /* Return the event we found, or NULL if we didn't find any. */ return (r); } /** * events_network_shutdown(void): * Clean up and free memory. This should run automatically via atexit. */ static void events_network_shutdown(void) { /* If we're not initialized, do nothing. */ if (S == NULL) return; /* If we have any registered events, do nothing. */ if (nfds > 0) return; /* Free the pollfd array. */ free(fds); fds = NULL; fds_alloc = 0; /* Free the socket list. */ socketlist_free(S); S = NULL; } tarsnap-1.0.41/libcperciva/events/events_network_selectstats.c000066400000000000000000000040711476705112600247060ustar00rootroot00000000000000#include #include "monoclock.h" #include "events.h" #include "events_internal.h" /* Time when inter-select duration clock started. */ static struct timeval st; static int running = 0; /* Statistics on inter-select durations. */ static double N = 0.0; static double mu = 0.0; static double M2 = 0.0; static double max = 0.0; /** * events_network_selectstats_startclock(void): * Start the inter-select duration clock: There is a selectable event. */ void events_network_selectstats_startclock(void) { /* If the clock is already running, return silently. */ if (running) return; /* Get the current time; return silently on error. */ if (monoclock_get(&st)) return; /* The clock is now running. */ running = 1; } /** * events_network_selectstats_stopclock(void): * Stop the inter-select duration clock: There are no selectable events. */ void events_network_selectstats_stopclock(void) { /* The clock is no longer running. */ running = 0; } /** * events_network_selectstats_select(void): * Update inter-select duration statistics in relation to an upcoming * select(2) call. */ void events_network_selectstats_select(void) { struct timeval tnow; double t, d; /* If the clock is not running, return silently. */ if (!running) return; /* If we can't get the current time, fail silently. */ if (monoclock_get(&tnow)) goto done; /* Compute inter-select duration in seconds. */ t = timeval_diff(st, tnow); /* Adjust statistics. We track running mean, variance * N, and max. */ N += 1.0; d = t - mu; mu += d / N; M2 += d * (t - mu); if (max < t) max = t; done: /* The clock is no longer running. */ running = 0; } /** * events_network_selectstats(N, mu, va, max): * Return statistics on the inter-select durations since the last time this * function was called. */ void events_network_selectstats(double * _n, double * _mu, double * _va, double * _max) { /* Copy statistics out. */ *_n = N; *_mu = mu; if (N > 1.0) *_va = M2 / (N - 1.0); else *_va = 0.0; *_max = max; /* Zero statistics. */ N = mu = M2 = max = 0.0; } tarsnap-1.0.41/libcperciva/events/events_timer.c000066400000000000000000000133271476705112600217230ustar00rootroot00000000000000#include #include #include #include "monoclock.h" #include "timerqueue.h" #include "events.h" #include "events_internal.h" struct timerrec { struct eventrec * r; void * cookie; struct timeval tv_orig; }; /* This also tracks whether we've initialized the atexit function. */ static struct timerqueue * Q = NULL; static void events_timer_shutdown(void); /* Set tv := + tdelta. */ static int gettimeout(struct timeval * tv, const struct timeval * tdelta) { if (monoclock_get(tv)) goto err0; tv->tv_sec += tdelta->tv_sec; if ((tv->tv_usec += tdelta->tv_usec) >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec += 1; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * events_timer_register(func, cookie, timeo): * Register ${func}(${cookie}) to be run ${timeo} in the future. Return a * cookie which can be passed to events_timer_cancel() or events_timer_reset(). */ void * events_timer_register(int (* func)(void *), void * cookie, const struct timeval * timeo) { struct eventrec * r; struct timerrec * t; struct timeval tv; /* Create the timer queue if it doesn't exist yet. */ if (Q == NULL) { if ((Q = timerqueue_init()) == NULL) goto err0; /* Clean up the timer queue at exit. */ if (atexit(events_timer_shutdown)) goto err0; } /* Bundle into an eventrec record. */ if ((r = events_mkrec(func, cookie)) == NULL) goto err0; /* Create a timer record. */ if ((t = malloc(sizeof(struct timerrec))) == NULL) goto err1; t->r = r; memcpy(&t->tv_orig, timeo, sizeof(struct timeval)); /* Compute the absolute timeout. */ if (gettimeout(&tv, &t->tv_orig)) goto err2; /* Add this to the timer queue. */ if ((t->cookie = timerqueue_add(Q, &tv, t)) == NULL) goto err2; /* Success! */ return (t); err2: free(t); err1: events_freerec(r); err0: /* Failure! */ return (NULL); } /** * events_timer_register_double(func, cookie, timeo): * As events_timer_register(), but ${timeo} is a double-precision * floating-point value specifying a number of seconds. */ void * events_timer_register_double(int (* func)(void *), void * cookie, double timeo) { struct timeval tv; /* Convert timeo to a struct timeval. */ tv.tv_sec = (time_t)timeo; tv.tv_usec = (suseconds_t)((timeo - (double)tv.tv_sec) * 1000000.0); /* Schedule the timeout. */ return (events_timer_register(func, cookie, &tv)); } /** * events_timer_cancel(cookie): * Cancel the timer for which the cookie ${cookie} was returned by * events_timer_register(). */ void events_timer_cancel(void * cookie) { struct timerrec * t = cookie; /* Remove from the timer queue. */ timerqueue_delete(Q, t->cookie); /* Free the eventrec and timer records. */ events_freerec(t->r); free(t); } /** * events_timer_reset(cookie): * Reset the timer for which the cookie ${cookie} was returned by * events_timer_register() to its initial value. */ int events_timer_reset(void * cookie) { struct timerrec * t = cookie; struct timeval tv; /* Compute the new timeout. */ if (gettimeout(&tv, &t->tv_orig)) goto err0; /* Adjust the timer. */ timerqueue_increase(Q, t->cookie, &tv); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * events_timer_min(timeo): * Return via ${timeo} a pointer to the minimum time which must be waited * before a timer will expire; or to NULL if there are no timers. The caller * is responsible for freeing the returned pointer. */ int events_timer_min(struct timeval ** timeo) { struct timeval tnow; const struct timeval * tv; /* If we have no queue, we have no timers; return NULL. */ if (Q == NULL) { *timeo = NULL; goto done; } /* Get the minimum timer from the queue. */ tv = timerqueue_getmin(Q); /* If there are no timers, return NULL. */ if (tv == NULL) { *timeo = NULL; goto done; } /* Allocate space for holding the returned timeval. */ if ((*timeo = malloc(sizeof(struct timeval))) == NULL) goto err0; /* Get the current time... */ if (monoclock_get(&tnow)) goto err1; /* ... and compare it to the minimum timer. */ if ((tnow.tv_sec > tv->tv_sec) || ((tnow.tv_sec == tv->tv_sec) && (tnow.tv_usec > tv->tv_usec))) { /* The timer has already expired, so return zero. */ (*timeo)->tv_sec = 0; (*timeo)->tv_usec = 0; } else { /* Compute the difference. */ (*timeo)->tv_sec = tv->tv_sec - tnow.tv_sec; (*timeo)->tv_usec = tv->tv_usec - tnow.tv_usec; if (tv->tv_usec < tnow.tv_usec) { (*timeo)->tv_usec += 1000000; (*timeo)->tv_sec -= 1; } } done: /* Success! */ return (0); err1: free(*timeo); err0: /* Failure! */ return (-1); } /** * events_timer_get(r): * Return via ${r} a pointer to an eventrec structure corresponding to an * expired timer, and delete said timer; or to NULL if there are no expired * timers. The caller is responsible for freeing the returned pointer. */ int events_timer_get(struct eventrec ** r) { struct timeval tnow; struct timerrec * t; /* If we have no queue, we have no timers; return NULL. */ if (Q == NULL) { *r = NULL; goto done; } /* Get current time. */ if (monoclock_get(&tnow)) goto err0; /* Get an expired timer, if there is one. */ t = timerqueue_getptr(Q, &tnow); /* If there is an expired timer... */ if (t != NULL) { /* ... pass back the eventrec and free the timer. */ *r = t->r; free(t); } else { /* Otherwise, return NULL. */ *r = NULL; } done: /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * events_timer_shutdown(void): * Clean up and free memory. This should run automatically via atexit. */ static void events_timer_shutdown(void) { /* If we have a queue and it is empty, free it. */ if ((Q != NULL) && (timerqueue_getmin(Q) == NULL)) { timerqueue_free(Q); Q = NULL; } } tarsnap-1.0.41/libcperciva/external/000077500000000000000000000000001476705112600173635ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/external/queue/000077500000000000000000000000001476705112600205075ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/external/queue/queue.h000066400000000000000000000547761476705112600220270ustar00rootroot00000000000000/* $NetBSD: queue.h,v 1.75.2.1 2021/04/03 22:29:03 thorpej Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef SYS_QUEUE_H_ #define SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * A singly-linked list is headed by a single forward pointer. The * elements are singly linked for minimum space and pointer manipulation * overhead at the expense of O(n) removal for arbitrary elements. New * elements can be added to the list after an existing element or at the * head of the list. Elements being removed from the head of the list * should use the explicit macro for this purpose for optimum * efficiency. A singly-linked list may only be traversed in the forward * direction. Singly-linked lists are ideal for applications with large * datasets and few or no removals or for implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * For details on the use of these macros, see the queue(3) manual page. */ /* * Include the definition of NULL only on NetBSD because sys/null.h * is not available elsewhere. This conditional makes the header * portable and it can simply be dropped verbatim into any system. * The caveat is that on other systems some other header * must provide NULL before the macros can be used. */ #ifdef __NetBSD__ #include #endif #if defined(_KERNEL) && defined(DIAGNOSTIC) #define QUEUEDEBUG 1 #endif #if defined(QUEUEDEBUG) # if defined(_KERNEL) # define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__) # else # include # define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__) # endif #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = (head)->slh_first; \ (var) != SLIST_END(head); \ (var) = (var)->field.sle_next) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST((head)); \ (var) != SLIST_END(head) && \ ((tvar) = SLIST_NEXT((var), field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) do { \ (head)->slh_first = SLIST_END(head); \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE_AFTER(slistelm, field) do { \ (slistelm)->field.sle_next = \ SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = (head)->slh_first; \ while(curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ } while (/*CONSTCOND*/0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods. */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for ((var) = ((head)->lh_first); \ (var) != LIST_END(head); \ (var) = ((var)->field.le_next)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST((head)); \ (var) != LIST_END(head) && \ ((tvar) = LIST_NEXT((var), field), 1); \ (var) = (tvar)) #define LIST_MOVE(head1, head2, field) do { \ LIST_INIT((head2)); \ if (!LIST_EMPTY((head1))) { \ (head2)->lh_first = (head1)->lh_first; \ (head2)->lh_first->field.le_prev = &(head2)->lh_first; \ LIST_INIT((head1)); \ } \ } while (/*CONSTCOND*/0) /* * List functions. */ #if defined(QUEUEDEBUG) #define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ if ((head)->lh_first && \ (head)->lh_first->field.le_prev != &(head)->lh_first) \ QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \ __FILE__, __LINE__); #define QUEUEDEBUG_LIST_OP(elm, field) \ if ((elm)->field.le_next && \ (elm)->field.le_next->field.le_prev != \ &(elm)->field.le_next) \ QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \ __FILE__, __LINE__); \ if (*(elm)->field.le_prev != (elm)) \ QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \ __FILE__, __LINE__); #define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ (elm)->field.le_next = (void *)1L; \ (elm)->field.le_prev = (void *)1L; #else #define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) #define QUEUEDEBUG_LIST_OP(elm, field) #define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) #endif #define LIST_INIT(head) do { \ (head)->lh_first = LIST_END(head); \ } while (/*CONSTCOND*/0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ QUEUEDEBUG_LIST_OP((listelm), field) \ if (((elm)->field.le_next = (listelm)->field.le_next) != \ LIST_END(head)) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ QUEUEDEBUG_LIST_OP((listelm), field) \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_HEAD(head, elm, field) do { \ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (/*CONSTCOND*/0) #define LIST_REMOVE(elm, field) do { \ QUEUEDEBUG_LIST_OP((elm), field) \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ } while (/*CONSTCOND*/0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ } while (/*CONSTCOND*/0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->sqh_first); \ (var) != SIMPLEQ_END(head); \ (var) = ((var)->field.sqe_next)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ for ((var) = ((head)->sqh_first); \ (var) != SIMPLEQ_END(head) && \ ((next = ((var)->field.sqe_next)), 1); \ (var) = (next)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE(head, elm, type, field) do { \ if ((head)->sqh_first == (elm)) { \ SIMPLEQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->sqh_first; \ while (curelm->field.sqe_next != (elm)) \ curelm = curelm->field.sqe_next; \ if ((curelm->field.sqe_next = \ curelm->field.sqe_next->field.sqe_next) == NULL) \ (head)->sqh_last = &(curelm)->field.sqe_next; \ } \ } while (/*CONSTCOND*/0) #define SIMPLEQ_CONCAT(head1, head2) do { \ if (!SIMPLEQ_EMPTY((head2))) { \ *(head1)->sqh_last = (head2)->sqh_first; \ (head1)->sqh_last = (head2)->sqh_last; \ SIMPLEQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) #define SIMPLEQ_LAST(head, type, field) \ (SIMPLEQ_EMPTY((head)) ? \ NULL : \ ((struct type *)(void *) \ ((char *)((head)->sqh_last) - offsetof(struct type, field)))) /* * Tail queue definitions. */ #define TAILQ_HEAD_(name, type, qual) \ struct name { \ qual type *tqh_first; /* first element */ \ qual type *qual *tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD(name, type) TAILQ_HEAD_(name, struct type,) #define TAILQ_HEAD_INITIALIZER(head) \ { TAILQ_END(head), &(head).tqh_first } #define TAILQ_ENTRY_(type, qual) \ struct { \ qual type *tqe_next; /* next element */ \ qual type *qual *tqe_prev; /* address of previous next element */\ } #define TAILQ_ENTRY(type) TAILQ_ENTRY_(struct type,) /* * Tail queue access methods. */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) (NULL) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)(void *)((head)->tqh_last))->tqh_last)) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)(void *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->tqh_first); \ (var) != TAILQ_END(head); \ (var) = ((var)->field.tqe_next)) #define TAILQ_FOREACH_SAFE(var, head, field, next) \ for ((var) = ((head)->tqh_first); \ (var) != TAILQ_END(head) && \ ((next) = TAILQ_NEXT(var, field), 1); (var) = (next)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = TAILQ_LAST((head), headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV((var), headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ for ((var) = TAILQ_LAST((head), headname); \ (var) != TAILQ_END(head) && \ ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev)) /* * Tail queue functions. */ #if defined(QUEUEDEBUG) #define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ if ((head)->tqh_first && \ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \ __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ if (*(head)->tqh_last != NULL) \ QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \ __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_OP(elm, field) \ if ((elm)->field.tqe_next && \ (elm)->field.tqe_next->field.tqe_prev != \ &(elm)->field.tqe_next) \ QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \ __FILE__, __LINE__); \ if (*(elm)->field.tqe_prev != (elm)) \ QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \ __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ if ((elm)->field.tqe_next == NULL && \ (head)->tqh_last != &(elm)->field.tqe_next) \ QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\ (head), (elm), __FILE__, __LINE__); #define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ (elm)->field.tqe_next = (void *)1L; \ (elm)->field.tqe_prev = (void *)1L; #else #define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) #define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) #define QUEUEDEBUG_TAILQ_OP(elm, field) #define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) #define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) #endif #define TAILQ_INIT(head) do { \ (head)->tqh_first = TAILQ_END(head); \ (head)->tqh_last = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ (elm)->field.tqe_next = TAILQ_END(head); \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ QUEUEDEBUG_TAILQ_OP((listelm), field) \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \ TAILQ_END(head)) \ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ QUEUEDEBUG_TAILQ_OP((listelm), field) \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_REMOVE(head, elm, field) do { \ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ QUEUEDEBUG_TAILQ_OP((elm), field) \ if (((elm)->field.tqe_next) != TAILQ_END(head)) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ } while (/*CONSTCOND*/0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \ TAILQ_END(head)) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ } while (/*CONSTCOND*/0) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first; /* first element */ \ struct type **stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue access methods. */ #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_END(head) NULL #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) /* * Singly-linked Tail queue functions. */ #define STAILQ_INIT(head) do { \ (head)->stqh_first = NULL; \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ (head)->stqh_last = &(elm)->field.stqe_next; \ (head)->stqh_first = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.stqe_next = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &(elm)->field.stqe_next; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ (head)->stqh_last = &(elm)->field.stqe_next; \ (listelm)->field.stqe_next = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if ((head)->stqh_first == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->stqh_first; \ while (curelm->field.stqe_next != (elm)) \ curelm = curelm->field.stqe_next; \ if ((curelm->field.stqe_next = \ curelm->field.stqe_next->field.stqe_next) == NULL) \ (head)->stqh_last = &(curelm)->field.stqe_next; \ } \ } while (/*CONSTCOND*/0) #define STAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->stqh_first); \ (var); \ (var) = ((var)->field.stqe_next)) #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = STAILQ_FIRST((head)); \ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY((head)) ? \ NULL : \ ((struct type *)(void *) \ ((char *)((head)->stqh_last) - offsetof(struct type, field)))) #endif /* !SYS_QUEUE_H_ */ tarsnap-1.0.41/libcperciva/network/000077500000000000000000000000001476705112600172325ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/network/network.h000066400000000000000000000102141476705112600210720ustar00rootroot00000000000000#ifndef NETWORK_H_ #define NETWORK_H_ #include #include #include #include /* Opaque address structure. */ struct sock_addr; /** * network_accept(fd, callback, cookie): * Asynchronously accept a connection on the socket ${fd}, which must be * already marked as listening and non-blocking. When a connection has been * accepted or an error occurs, invoke ${callback}(${cookie}, s) where s is * the accepted connection or -1 on error. Return a cookie which can be * passed to network_accept_cancel() in order to cancel the accept. */ void * network_accept(int, int (*)(void *, int), void *); /** * network_accept_cancel(cookie): * Cancel the connection accept for which the cookie ${cookie} was returned * by network_accept(). Do not invoke the callback associated with the accept. */ void network_accept_cancel(void *); /** * network_connect(sas, callback, cookie): * Iterate through the addresses in ${sas}, attempting to create and connect * a non-blocking socket. Once connected, invoke ${callback}(${cookie}, s) * where s is the connected socket; upon fatal error or if there are no * addresses remaining to attempt, invoke ${callback}(${cookie}, -1). Return * a cookie which can be passed to network_connect_cancel() in order to cancel * the connection attempt. */ void * network_connect(struct sock_addr * const *, int (*)(void *, int), void *); /** * network_connect_bind(sas, sa_b, callback, cookie): * Iterate through the addresses in ${sas}, attempting to create and connect * a non-blocking socket. If ${sa_b} is not NULL, bind the socket to ${sa_b} * immediately after creating it. Once connected, invoke * ${callback}(${cookie}, s) where s is the connected socket; upon fatal error * or if there are no addresses remaining to attempt, invoke * ${callback}(${cookie}, -1). Return a cookie which can be passed to * network_connect_cancel() in order to cancel the connection attempt. */ void * network_connect_bind(struct sock_addr * const *, const struct sock_addr *, int (*)(void *, int), void *); /** * network_connect_timeo(sas, timeo, callback, cookie): * Behave as network_connect(), but wait a duration of at most ${timeo} for * each address which is being attempted. */ void * network_connect_timeo(struct sock_addr * const *, const struct timeval *, int (*)(void *, int), void *); /** * network_connect_cancel(cookie): * Cancel the connection attempt for which ${cookie} was returned by * network_connect(). Do not invoke the associated callback. */ void network_connect_cancel(void *); /** * network_read(fd, buf, buflen, minread, callback, cookie): * Asynchronously read up to ${buflen} bytes of data from ${fd} into ${buf}. * When at least ${minread} bytes have been read or on error, invoke * ${callback}(${cookie}, lenread), where lenread is 0 on EOF or -1 on error, * and the number of bytes read (between ${minread} and ${buflen} inclusive) * otherwise. Return a cookie which can be passed to network_read_cancel() in * order to cancel the read. */ void * network_read(int, uint8_t *, size_t, size_t, int (*)(void *, ssize_t), void *); /** * network_read_cancel(cookie): * Cancel the buffer read for which the cookie ${cookie} was returned by * network_read(). Do not invoke the callback associated with the read. */ void network_read_cancel(void *); /** * network_write(fd, buf, buflen, minwrite, callback, cookie): * Asynchronously write up to ${buflen} bytes of data from ${buf} to ${fd}. * When at least ${minwrite} bytes have been written or on error, invoke * ${callback}(${cookie}, lenwrit), where lenwrit is -1 on error and the * number of bytes written (between ${minwrite} and ${buflen} inclusive) * otherwise. Return a cookie which can be passed to network_write_cancel() in * order to cancel the write. */ void * network_write(int, const uint8_t *, size_t, size_t, int (*)(void *, ssize_t), void *); /** * network_write_cancel(cookie): * Cancel the buffer write for which the cookie ${cookie} was returned by * network_write(). Do not invoke the callback associated with the write. */ void network_write_cancel(void *); #endif /* !NETWORK_H_ */ tarsnap-1.0.41/libcperciva/network/network_accept.c000066400000000000000000000045031476705112600224100ustar00rootroot00000000000000#include #include #include #include "events.h" #include "network.h" struct accept_cookie { int (* callback)(void *, int); void * cookie; int fd; }; /* Accept the connection and invoke the callback. */ static int callback_accept(void * cookie) { struct accept_cookie * C = cookie; int s; int rc; /* Attempt to accept a new connection. */ if ((s = accept(C->fd, NULL, NULL)) == -1) { /* If a connection isn't available, reset the callback. */ if ((errno == EAGAIN) || #if EAGAIN != EWOULDBLOCK (errno == EWOULDBLOCK) || #endif (errno == ECONNABORTED) || (errno == EINTR)) goto tryagain; } /* Call the upstream callback. */ rc = (C->callback)(C->cookie, s); /* Free the cookie. */ free(C); /* Return status from upstream callback. */ return (rc); tryagain: /* Reset the callback. */ return (events_network_register(callback_accept, C, C->fd, EVENTS_NETWORK_OP_READ)); } /** * network_accept(fd, callback, cookie): * Asynchronously accept a connection on the socket ${fd}, which must be * already marked as listening and non-blocking. When a connection has been * accepted or an error occurs, invoke ${callback}(${cookie}, s) where s is * the accepted connection or -1 on error. Return a cookie which can be * passed to network_accept_cancel() in order to cancel the accept. */ void * network_accept(int fd, int (* callback)(void *, int), void * cookie) { struct accept_cookie * C; /* Bake a cookie. */ if ((C = malloc(sizeof(struct accept_cookie))) == NULL) goto err0; C->callback = callback; C->cookie = cookie; C->fd = fd; /* * Register a network event. A connection arriving on a listening * socket is treated by select(2) as the socket becoming readable. */ if (events_network_register(callback_accept, C, C->fd, EVENTS_NETWORK_OP_READ)) goto err1; /* Success! */ return (C); err1: free(C); err0: /* Failure! */ return (NULL); } /** * network_accept_cancel(cookie): * Cancel the connection accept for which the cookie ${cookie} was returned * by network_accept(). Do not invoke the callback associated with the accept. */ void network_accept_cancel(void * cookie) { struct accept_cookie * C = cookie; /* Cancel the network event. */ events_network_cancel(C->fd, EVENTS_NETWORK_OP_READ); /* Free the cookie. */ free(C); } tarsnap-1.0.41/libcperciva/network/network_connect.c000066400000000000000000000170701476705112600226050ustar00rootroot00000000000000#include #include #include #include #include #include #include "events.h" #include "sock.h" #include "warnp.h" #include "network.h" struct connect_cookie { int (* callback)(void *, int); void * cookie; struct sock_addr * const * sas; const struct sock_addr * sa_b; struct timeval timeo; void * cookie_immediate; int s; int timeo_enabled; void * cookie_timeo; }; static int tryconnect(struct connect_cookie *); /* Invoke the upstream callback and clean up. */ static int docallback(void * cookie) { struct connect_cookie * C = cookie; int rc; /* Invoke the upstream callback. */ rc = (C->callback)(C->cookie, C->s); /* Free the cookie. */ free(C); /* Return status from upstream callback. */ return (rc); } /* An address failed to connect. */ static int dofailed(struct connect_cookie * C) { /* Close the socket which failed to connect. */ if (close(C->s)) warnp("close"); /* We don't have an open socket any more. */ C->s = -1; /* This address didn't work. */ C->sas++; /* Try other addresses until we run out of options. */ return (tryconnect(C)); } /* Callback when connect(2) succeeds or fails. */ static int callback_connect(void * cookie) { struct connect_cookie * C = cookie; int sockerr; socklen_t sockerrlen = sizeof(int); /* Stop waiting for the timer callback. */ if (C->cookie_timeo != NULL) { events_timer_cancel(C->cookie_timeo); C->cookie_timeo = NULL; } /* Did we succeed? */ if (getsockopt(C->s, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen)) goto err1; if (sockerr != 0) return (dofailed(C)); /* * Perform the callback (this can be done here rather than being * scheduled as an immediate callback, as we're already running from * callback context). */ return (docallback(C)); err1: if (close(C->s)) warnp("close"); free(C); /* Fatal error! */ return (-1); } /* Callback when a timer expires. */ static int callback_timeo(void * cookie) { struct connect_cookie * C = cookie; /* We're not waiting for a timer callback any more. */ C->cookie_timeo = NULL; /* Stop listening for this socket. */ events_network_cancel(C->s, EVENTS_NETWORK_OP_WRITE); /* This connect attempt failed. */ return (dofailed(C)); } /* Try to launch a connection. Free the cookie on fatal errors. */ static int tryconnect(struct connect_cookie * C) { /* Try addresses until we find one which doesn't fail immediately. */ for (; C->sas[0] != NULL; C->sas++) { /* Can we try to connect to this address? */ if ((C->s = sock_connect_bind_nb(C->sas[0], C->sa_b)) != -1) break; } /* Did we run out of addresses to try? */ if (C->sas[0] == NULL) goto failed; /* If we've been asked to have a timeout, set one. */ if (C->timeo_enabled) { if ((C->cookie_timeo = events_timer_register(callback_timeo, C, &C->timeo)) == NULL) goto err1; } else { C->cookie_timeo = NULL; } /* Wait until this socket connects or fails to do so. */ if (events_network_register(callback_connect, C, C->s, EVENTS_NETWORK_OP_WRITE)) goto err2; /* Success! */ return (0); failed: /* Schedule a callback. */ if ((C->cookie_immediate = events_immediate_register(docallback, C, 0)) == NULL) goto err1; /* Failure successfully handled. */ return (0); err2: if (C->cookie_timeo != NULL) events_timer_cancel(C->cookie_timeo); err1: if ((C->s != -1) && close(C->s)) warnp("close"); free(C); /* Fatal error. */ return (-1); } /** * network_connect_internal(sas, sa_b, timeo, callback, cookie): * Iterate through the addresses in ${sas}, attempting to create and connect * a non-blocking socket. If ${timeo} is not NULL, wait a duration of at * most ${timeo} for each address which is being attempted. If ${sa_b} is * not NULL, then bind the socket to ${sa_b}. * * Once connected, invoke ${callback}(${cookie}, s) where s is the connected * socket; upon fatal error or if there are no addresses remaining to * attempt, invoke ${callback}(${cookie}, -1). Return a cookie which can be * passed to network_connect_cancel() in order to cancel the connection * attempt. */ static void * network_connect_internal(struct sock_addr * const * sas, const struct sock_addr * sa_b, const struct timeval * timeo, int (* callback)(void *, int), void * cookie) { struct connect_cookie * C; /* Bake a cookie. */ if ((C = malloc(sizeof(struct connect_cookie))) == NULL) goto err0; C->callback = callback; C->cookie = cookie; C->sas = sas; C->sa_b = sa_b; C->cookie_immediate = NULL; C->cookie_timeo = NULL; C->s = -1; /* Do we have a timeout? */ if (timeo != NULL) { memcpy(&C->timeo, timeo, sizeof(struct timeval)); C->timeo_enabled = 1; } else { C->timeo_enabled = 0; } /* Try to connect to the first address. */ if (tryconnect(C)) goto err0; /* Success! */ return (C); err0: /* Failure! */ return (NULL); } /** * network_connect(sas, callback, cookie): * Iterate through the addresses in ${sas}, attempting to create and connect * a non-blocking socket. Once connected, invoke ${callback}(${cookie}, s) * where s is the connected socket; upon fatal error or if there are no * addresses remaining to attempt, invoke ${callback}(${cookie}, -1). Return * a cookie which can be passed to network_connect_cancel() in order to cancel * the connection attempt. */ void * network_connect(struct sock_addr * const * sas, int (* callback)(void *, int), void * cookie) { /* Let network_connect_internal handle this. */ return (network_connect_internal(sas, NULL, NULL, callback, cookie)); } /** * network_connect_bind(sas, sa_b, callback, cookie): * Iterate through the addresses in ${sas}, attempting to create and connect * a non-blocking socket. If ${sa_b} is not NULL, bind the socket to ${sa_b} * immediately after creating it. Once connected, invoke * ${callback}(${cookie}, s) where s is the connected socket; upon fatal error * or if there are no addresses remaining to attempt, invoke * ${callback}(${cookie}, -1). Return a cookie which can be passed to * network_connect_cancel() in order to cancel the connection attempt. */ void * network_connect_bind(struct sock_addr * const * sas, const struct sock_addr * sa_b, int (* callback)(void *, int), void * cookie) { /* Let network_connect_internal handle this. */ return (network_connect_internal(sas, sa_b, NULL, callback, cookie)); } /** * network_connect_timeo(sas, timeo, callback, cookie): * Behave as network_connect(), but wait a duration of at most ${timeo} for * each address which is being attempted. */ void * network_connect_timeo(struct sock_addr * const * sas, const struct timeval * timeo, int (* callback)(void *, int), void * cookie) { /* Let network_connect_internal handle this. */ return (network_connect_internal(sas, NULL, timeo, callback, cookie)); } /** * network_connect_cancel(cookie): * Cancel the connection attempt for which ${cookie} was returned by * network_connect(). Do not invoke the associated callback. */ void network_connect_cancel(void * cookie) { struct connect_cookie * C = cookie; /* We should have either an immediate callback or a socket. */ assert((C->cookie_immediate != NULL) || (C->s != -1)); assert((C->cookie_immediate == NULL) || (C->s == -1)); /* Cancel any timer. */ if (C->cookie_timeo != NULL) events_timer_cancel(C->cookie_timeo); /* Cancel any immediate callback. */ if (C->cookie_immediate != NULL) events_immediate_cancel(C->cookie_immediate); /* Close any socket. */ if (C->s != -1) { events_network_cancel(C->s, EVENTS_NETWORK_OP_WRITE); if (close(C->s)) warnp("close"); } /* Free the cookie. */ free(C); } tarsnap-1.0.41/libcperciva/network/network_read.c000066400000000000000000000072721476705112600220720ustar00rootroot00000000000000#include #include #include #include #include #include #include "events.h" #include "mpool.h" #include "network.h" struct network_read_cookie { int (* callback)(void *, ssize_t); void * cookie; int fd; uint8_t * buf; size_t buflen; size_t minlen; size_t bufpos; }; MPOOL(network_read_cookie, struct network_read_cookie, 16); /* Invoke the callback, clean up, and return the callback's status. */ static int docallback(struct network_read_cookie * C, ssize_t nbytes) { int rc; /* Invoke the callback. */ rc = (C->callback)(C->cookie, nbytes); /* Clean up. */ mpool_network_read_cookie_free(C); /* Return the callback's status. */ return (rc); } /* The socket is ready for reading/writing. */ static int callback_buf(void * cookie) { struct network_read_cookie * C = cookie; size_t oplen; ssize_t len; /* Attempt to read/write data to/from the buffer. */ oplen = C->buflen - C->bufpos; len = recv(C->fd, C->buf + C->bufpos, oplen, 0); /* Failure? */ if (len == -1) { /* Was it really an error, or just a try-again? */ if ((errno == EAGAIN) || #if EAGAIN != EWOULDBLOCK (errno == EWOULDBLOCK) || #endif (errno == EINTR)) goto tryagain; /* Something went wrong. */ goto failed; } else if (len == 0) { /* The socket was shut down by the remote host. */ goto eof; } /* We processed some data. */ C->bufpos += (size_t)len; /* Do we need to keep going? */ if (C->bufpos < C->minlen) goto tryagain; /* Sanity-check: buffer position must fit into a ssize_t. */ assert(C->bufpos <= SSIZE_MAX); /* Invoke the callback and return. */ return (docallback(C, (ssize_t)C->bufpos)); tryagain: /* Reset the event. */ if (events_network_register(callback_buf, C, C->fd, EVENTS_NETWORK_OP_READ)) goto failed; /* Callback was reset. */ return (0); eof: /* Invoke the callback with an EOF status and return. */ return (docallback(C, 0)); failed: /* Invoke the callback with a failure status and return. */ return (docallback(C, -1)); } /** * network_read(fd, buf, buflen, minread, callback, cookie): * Asynchronously read up to ${buflen} bytes of data from ${fd} into ${buf}. * When at least ${minread} bytes have been read or on error, invoke * ${callback}(${cookie}, lenread), where lenread is 0 on EOF or -1 on error, * and the number of bytes read (between ${minread} and ${buflen} inclusive) * otherwise. Return a cookie which can be passed to network_read_cancel() in * order to cancel the read. */ void * network_read(int fd, uint8_t * buf, size_t buflen, size_t minread, int (* callback)(void *, ssize_t), void * cookie) { struct network_read_cookie * C; /* Make sure buflen is non-zero. */ assert(buflen != 0); /* Sanity-check: # bytes must fit into a ssize_t. */ assert(buflen <= SSIZE_MAX); /* Bake a cookie. */ if ((C = mpool_network_read_cookie_malloc()) == NULL) goto err0; C->callback = callback; C->cookie = cookie; C->fd = fd; C->buf = buf; C->buflen = buflen; C->minlen = minread; C->bufpos = 0; /* Register a callback for network readiness. */ if (events_network_register(callback_buf, C, C->fd, EVENTS_NETWORK_OP_READ)) goto err1; /* Success! */ return (C); err1: mpool_network_read_cookie_free(C); err0: /* Failure! */ return (NULL); } /** * network_read_cancel(cookie): * Cancel the buffer read for which the cookie ${cookie} was returned by * network_read(). Do not invoke the callback associated with the read. */ void network_read_cancel(void * cookie) { struct network_read_cookie * C = cookie; /* Kill the network event. */ events_network_cancel(C->fd, EVENTS_NETWORK_OP_READ); /* Free the cookie. */ mpool_network_read_cookie_free(C); } tarsnap-1.0.41/libcperciva/network/network_write.c000066400000000000000000000115231476705112600223030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "events.h" #include "mpool.h" #include "warnp.h" #include "network.h" /** * POSIX.1-2008 requires that MSG_NOSIGNAL be defined as a flag for send(2) * which has the effect of preventing SIGPIPE from being raised when writing * to a descriptor which has been shut down. Unfortunately there are some * platforms which are not POSIX.1-2008 compliant; we provide a workaround * (-DPOSIXFAIL_MSG_NOSIGNAL) which instead blocks the SIGPIPE signal on such * platforms. * * (This workaround could be used automatically, but requiring that it be * explicitly enabled helps to get platforms fixed.) */ #ifdef POSIXFAIL_MSG_NOSIGNAL #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #endif struct network_write_cookie { int (* callback)(void *, ssize_t); void * cookie; int fd; const uint8_t * buf; size_t buflen; size_t minlen; size_t bufpos; }; MPOOL(network_write_cookie, struct network_write_cookie, 16); /* Invoke the callback, clean up, and return the callback's status. */ static int docallback(struct network_write_cookie * C, ssize_t nbytes) { int rc; /* Invoke the callback. */ rc = (C->callback)(C->cookie, nbytes); /* Clean up. */ mpool_network_write_cookie_free(C); /* Return the callback's status. */ return (rc); } /* The socket is ready for reading/writing. */ static int callback_buf(void * cookie) { struct network_write_cookie * C = cookie; size_t oplen; ssize_t len; #ifdef POSIXFAIL_MSG_NOSIGNAL void (* oldsig)(int); int saved_errno; #endif /* If we don't have MSG_NOSIGNAL, ignore SIGPIPE. */ #ifdef POSIXFAIL_MSG_NOSIGNAL if ((oldsig = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) { warnp("signal(SIGPIPE)"); goto failed; } #endif /* Attempt to read/write data to/from the buffer. */ oplen = C->buflen - C->bufpos; len = send(C->fd, C->buf + C->bufpos, oplen, MSG_NOSIGNAL); /* We should never see a send length of zero. */ assert(len != 0); /* If we ignored SIGPIPE, restore the old handler. */ #ifdef POSIXFAIL_MSG_NOSIGNAL /* Save errno in case it gets clobbered by signal(). */ saved_errno = errno; if (signal(SIGPIPE, oldsig) == SIG_ERR) { warnp("signal(SIGPIPE)"); goto failed; } /* Restore saved errno. */ errno = saved_errno; #endif /* Failure? */ if (len == -1) { /* Was it really an error, or just a try-again? */ if ((errno == EAGAIN) || #if EAGAIN != EWOULDBLOCK (errno == EWOULDBLOCK) || #endif (errno == EINTR)) goto tryagain; /* Something went wrong. */ goto failed; } /* We processed some data. */ C->bufpos += (size_t)len; /* Do we need to keep going? */ if (C->bufpos < C->minlen) goto tryagain; /* Sanity-check: buffer position must fit into a ssize_t. */ assert(C->bufpos <= SSIZE_MAX); /* Invoke the callback and return. */ return (docallback(C, (ssize_t)C->bufpos)); tryagain: /* Reset the event. */ if (events_network_register(callback_buf, C, C->fd, EVENTS_NETWORK_OP_WRITE)) goto failed; /* Callback was reset. */ return (0); failed: /* Invoke the callback with a failure status and return. */ return (docallback(C, -1)); } /** * network_write(fd, buf, buflen, minwrite, callback, cookie): * Asynchronously write up to ${buflen} bytes of data from ${buf} to ${fd}. * When at least ${minwrite} bytes have been written or on error, invoke * ${callback}(${cookie}, lenwrit), where lenwrit is -1 on error and the * number of bytes written (between ${minwrite} and ${buflen} inclusive) * otherwise. Return a cookie which can be passed to network_write_cancel() in * order to cancel the write. */ void * network_write(int fd, const uint8_t * buf, size_t buflen, size_t minwrite, int (* callback)(void *, ssize_t), void * cookie) { struct network_write_cookie * C; /* Make sure buflen is non-zero. */ assert(buflen != 0); /* Sanity-check: # bytes must fit into a ssize_t. */ assert(buflen <= SSIZE_MAX); /* Bake a cookie. */ if ((C = mpool_network_write_cookie_malloc()) == NULL) goto err0; C->callback = callback; C->cookie = cookie; C->fd = fd; C->buf = buf; C->buflen = buflen; C->minlen = minwrite; C->bufpos = 0; /* Register a callback for network readiness. */ if (events_network_register(callback_buf, C, C->fd, EVENTS_NETWORK_OP_WRITE)) goto err1; /* Success! */ return (C); err1: mpool_network_write_cookie_free(C); err0: /* Failure! */ return (NULL); } /** * network_write_cancel(cookie): * Cancel the buffer write for which the cookie ${cookie} was returned by * network_write(). Do not invoke the callback associated with the write. */ void network_write_cancel(void * cookie) { struct network_write_cookie * C = cookie; /* Kill the network event. */ events_network_cancel(C->fd, EVENTS_NETWORK_OP_WRITE); /* Free the cookie. */ mpool_network_write_cookie_free(C); } tarsnap-1.0.41/libcperciva/util/000077500000000000000000000000001476705112600165165ustar00rootroot00000000000000tarsnap-1.0.41/libcperciva/util/align_ptr.h000066400000000000000000000032411476705112600206460ustar00rootroot00000000000000#ifndef ALIGN_PTR_H_ #define ALIGN_PTR_H_ #include #include /** * ALIGN_PTR_DECL(type, name, num, alignsize): * Declare a pointer called ${name}, which points to an array large enough to * contain ${num} values of ${type} and is aligned to ${alignsize} bytes. The * pointer must not be used until ALIGN_PTR_INIT(${name}, ${alignsize}) has * been called. This macro may also create an additional variable called * "${name}_buf". */ #define ALIGN_PTR_DECL(type, name, num, alignsize) \ uint8_t name##_buf[num * sizeof(type) + (alignsize - 1)]; \ type * name /** * ALIGN_PTR_INIT(name, alignsize): * Initialize the variable called ${name} to point to an array which is * aligned to ${alignsize} bytes. They must have previously been declared * with ALIGN_PTR_DECL(${name}, ${alignsize}). This macro assumes that * casting a pointer to uintptr_t behaves naturally, i.e. yields a byte offset * relative to aligned memory. */ #define ALIGN_PTR_INIT(name, alignsize) \ name = align_ptr(name##_buf, alignsize) /** * align_ptr(arr, alignment): * Return a pointer to the first memory location within ${arr} which is * aligned to ${alignsize} bytes. (It is expected that this function will * only be called via the ALIGN_PTR_INIT macro). */ static inline void * align_ptr(uint8_t * arr, size_t alignment) { size_t offset; /* * This assumes that casting a pointer to uintptr_t behaves naturally, * i.e. yields a byte offset relative to aligned memory. */ offset = (uintptr_t)(&arr[0]) % alignment; offset = (alignment - offset) % alignment; /* Return the aligned pointer. */ return ((void *)&arr[offset]); } #endif /* !ALIGN_PTR_H_ */ tarsnap-1.0.41/libcperciva/util/asprintf.c000066400000000000000000000014261476705112600205130ustar00rootroot00000000000000#include #include #include #include "asprintf.h" /** * asprintf(ret, format, ...): * Do asprintf(3) like GNU and BSD do. */ int asprintf(char ** ret, const char * format, ...) { va_list ap; int len; size_t buflen; /* Figure out how long the string needs to be. */ va_start(ap, format); len = vsnprintf(NULL, 0, format, ap); va_end(ap); /* Did we fail? */ if (len < 0) goto err0; buflen = (size_t)(len) + 1; /* Allocate memory. */ if ((*ret = malloc(buflen)) == NULL) goto err0; /* Actually generate the string. */ va_start(ap, format); len = vsnprintf(*ret, buflen, format, ap); va_end(ap); /* Did we fail? */ if (len < 0) goto err1; /* Success! */ return (len); err1: free(*ret); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/libcperciva/util/asprintf.h000066400000000000000000000005021476705112600205120ustar00rootroot00000000000000#ifndef ASPRINTF_H_ #define ASPRINTF_H_ /* Avoid namespace collisions with BSD/GNU asprintf. */ #ifdef asprintf #undef asprintf #endif #define asprintf libcperciva_asprintf /** * asprintf(ret, format, ...): * Do asprintf(3) like GNU and BSD do. */ int asprintf(char **, const char *, ...); #endif /* !ASPRINTF_H_ */ tarsnap-1.0.41/libcperciva/util/b64encode.c000066400000000000000000000051201476705112600204310ustar00rootroot00000000000000#include #include #include "b64encode.h" static char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /** * b64encode(in, out, len): * Convert ${len} bytes from ${in} into RFC 1421 base-64 encoding, writing * the resulting ((len + 2) / 3) * 4 bytes to ${out}; and append a NUL byte. */ void b64encode(const uint8_t * in, char * out, size_t len) { uint32_t t; size_t j; /* Repeat {read up to 3 bytes; write 4 bytes} until we're done. */ while (len) { /* Read up to 3 bytes. */ for (t = 0, j = 0; j < 3; j++) { t <<= 8; if (j < len) t += *in++; } /* Write 4 bytes. */ for (j = 0; j < 4; j++) { if (j <= len) *out++ = b64chars[(t >> 18) & 0x3f]; else *out++ = '='; t <<= 6; } /* Adjust the remaining length. */ if (len < 3) len = 0; else len -= 3; } /* NUL terminate. */ *out++ = '\0'; } /** * b64decode(in, inlen, out, outlen): * Convert ${inlen} bytes of RFC 1421 base-64 encoding from ${in}, writing * the resulting bytes to ${out}; and pass the number of bytes output back * via ${outlen}. The buffer ${out} must contain at least (inlen/4)*3 bytes * of space; but ${outlen} might be less than this. Return non-zero if the * input ${in} is not valid base-64 encoded text. */ int b64decode(const char * in, size_t inlen, uint8_t * out, size_t * outlen) { uint32_t t; ptrdiff_t pos; size_t deadbytes = 0; size_t i; /* We must have a multiple of 4 input bytes. */ if (inlen % 4 != 0) goto bad; /* Check that we have valid input and count trailing '='s. */ for (i = 0; i < inlen; i++) { /* Must be characters from our b64 character set. */ if ((in[i] == '\0') || (strchr(b64chars, in[i]) == NULL)) goto bad; /* Is this a '=' character? */ if (in[i] == '=') deadbytes += 1; /* No non-'=' bytes after a '=' byte. */ if ((in[i] != '=') && (deadbytes > 0)) goto bad; } /* Can't have more than 2 trailing '=' bytes. */ if (deadbytes > 2) goto bad; /* We have no output yet. */ *outlen = 0; /* Loop until we run out of data. */ while (inlen) { /* Parse 4 bytes. */ for (t = 0, i = 0; i < 4; i++) { t <<= 6; pos = strchr(b64chars, in[i]) - b64chars; t += (uint32_t)(pos & 0x3f); } /* Output 3 bytes. */ for (i = 0; i < 3; i++) { out[i] = (t >> 16) & 0xff; t <<= 8; } /* Adjust pointers and lengths for the completed block. */ in += 4; inlen -= 4; out += 3; *outlen += 3; } /* Ignore dead bytes. */ *outlen -= deadbytes; /* Success! */ return (0); bad: /* The input is not valid base-64 encoded text. */ return (1); } tarsnap-1.0.41/libcperciva/util/b64encode.h000066400000000000000000000016531476705112600204450ustar00rootroot00000000000000#ifndef B64ENCODE_H_ #define B64ENCODE_H_ #include #include /* The resulting length after base-64 encoding, not including the NUL byte. */ #define b64len(origlen) ((((origlen) + 2) / 3) * 4) /** * b64encode(in, out, len): * Convert ${len} bytes from ${in} into RFC 1421 base-64 encoding, writing * the resulting ((len + 2) / 3) * 4 bytes to ${out}; and append a NUL byte. */ void b64encode(const uint8_t *, char *, size_t); /** * b64decode(in, inlen, out, outlen): * Convert ${inlen} bytes of RFC 1421 base-64 encoding from ${in}, writing * the resulting bytes to ${out}; and pass the number of bytes output back * via ${outlen}. The buffer ${out} must contain at least (inlen/4)*3 bytes * of space; but ${outlen} might be less than this. Return non-zero if the * input ${in} is not valid base-64 encoded text. */ int b64decode(const char *, size_t, uint8_t *, size_t *); #endif /* !B64ENCODE_H_ */ tarsnap-1.0.41/libcperciva/util/ctassert.h000066400000000000000000000011141476705112600205140ustar00rootroot00000000000000#ifndef CTASSERT_H_ #define CTASSERT_H_ /* * CTASSERT(foo) will produce a compile-time error if "foo" is not a constant * expression which evaluates to a non-zero value. */ /* Kill any existing definition, just in case it's different. */ #ifdef CTASSERT #undef CTASSERT #endif /* Define using libcperciva namespace to avoid collisions. */ #define CTASSERT(x) libcperciva_CTASSERT(x, __LINE__) #define libcperciva_CTASSERT(x, y) libcperciva__CTASSERT(x, y) #define libcperciva__CTASSERT(x, y) \ extern char libcperciva__assert ## y[(x) ? 1 : -1] #endif /* !CTASSERT_H_ */ tarsnap-1.0.41/libcperciva/util/entropy.c000066400000000000000000000062261476705112600203700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "warnp.h" #include "entropy.h" /** * XXX Portability * XXX We obtain random bytes from the operating system by opening * XXX /dev/urandom and reading them from that device; this works on * XXX modern UNIX-like operating systems but not on systems like * XXX win32 where there is no concept of /dev/urandom. */ /** * Entropy reader state. At present it holds a file descriptor for * /dev/urandom, but in the future this structure may gain other OS-dependent * state, e.g. a Windows Handle. */ struct entropy_read_cookie { int fd; }; /** * entropy_read_init(void): * Initialize the ability to produce random bytes from the operating system, * and return a cookie. */ struct entropy_read_cookie * entropy_read_init(void) { struct entropy_read_cookie * er; /* Allocate cookie. */ if ((er = malloc(sizeof(struct entropy_read_cookie))) == NULL) { warnp("malloc"); goto err0; } /* Open /dev/urandom. */ if ((er->fd = open("/dev/urandom", O_RDONLY)) == -1) { warnp("open(/dev/urandom)"); goto err1; } /* Success! */ return (er); err1: free(er); err0: /* Failure! */ return (NULL); } /** * entropy_read_fill(er, buf, buflen): * Fill the given buffer with random bytes provided by the operating system * using the resources in ${er}. */ int entropy_read_fill(struct entropy_read_cookie * er, uint8_t * buf, size_t buflen) { ssize_t lenread; /* Sanity checks. */ assert(er != NULL); assert(buflen <= SSIZE_MAX); /* Read bytes until we have filled the buffer. */ while (buflen > 0) { if ((lenread = read(er->fd, buf, buflen)) == -1) { warnp("read(/dev/urandom)"); goto err0; } /* The random device should never EOF. */ if (lenread == 0) { warn0("EOF on /dev/urandom?"); goto err0; } /* We've filled a portion of the buffer. */ buf += (size_t)lenread; buflen -= (size_t)lenread; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * entropy_read_done(er): * Release any resources used by ${er}. */ int entropy_read_done(struct entropy_read_cookie * er) { /* Sanity check. */ assert(er != NULL); /* Close the device. */ while (close(er->fd) == -1) { if (errno != EINTR) { warnp("close(/dev/urandom)"); goto err1; } } /* Clean up. */ free(er); /* Success! */ return (0); err1: free(er); /* Failure! */ return (-1); } /** * entropy_read(buf, buflen): * Fill the given buffer with random bytes provided by the operating system. */ int entropy_read(uint8_t * buf, size_t buflen) { struct entropy_read_cookie * er; /* Sanity-check the buffer size. */ assert(buflen <= SSIZE_MAX); /* Open /dev/urandom. */ if ((er = entropy_read_init()) == NULL) { warn0("entropy_read_init"); goto err0; } /* Read bytes until we have filled the buffer. */ if (entropy_read_fill(er, buf, buflen)) { warn0("entropy_read_fill"); goto err1; } /* Close the device. */ if (entropy_read_done(er)) { warn0("entropy_read_done"); goto err0; } /* Success! */ return (0); err1: entropy_read_done(er); err0: /* Failure! */ return (-1); } tarsnap-1.0.41/libcperciva/util/entropy.h000066400000000000000000000015271476705112600203740ustar00rootroot00000000000000#ifndef ENTROPY_H_ #define ENTROPY_H_ #include #include /* Opaque type. */ struct entropy_read_cookie; /** * entropy_read_init(void): * Initialize the ability to produce random bytes from the operating system, * and return a cookie. */ struct entropy_read_cookie * entropy_read_init(void); /** * entropy_read_fill(er, buf, buflen): * Fill the given buffer with random bytes provided by the operating system * using the resources in ${er}. */ int entropy_read_fill(struct entropy_read_cookie *, uint8_t *, size_t); /** * entropy_read_done(er): * Release any resources used by ${er}. */ int entropy_read_done(struct entropy_read_cookie *); /** * entropy_read(buf, buflen): * Fill the given buffer with random bytes provided by the operating system. */ int entropy_read(uint8_t *, size_t); #endif /* !ENTROPY_H_ */ tarsnap-1.0.41/libcperciva/util/getopt.c000066400000000000000000000206401476705112600201660ustar00rootroot00000000000000#include #include #include #include #include "getopt.h" /* * Standard getopt global variables. optreset starts as non-zero in order to * trigger initialization behaviour. */ const char * optarg = NULL; int optind = 1; int opterr = 1; int optreset = 1; /* * Quasi-internal global variables -- these are used via GETOPT macros. */ const char * getopt_dummy = "(dummy)"; int getopt_initialized = 0; /* * Internal variables. */ static const char * cmdname = NULL; static struct opt { const char * os; size_t olen; int hasarg; } * opts = NULL; /* Probably a sparse array: some values, some NULLs. */ static size_t nopts; /* Maximum number of options. */ static size_t opt_missing; static size_t opt_default; static size_t opt_found; static const char * packedopts; static char popt[3]; static int atexit_registered = 0; /* Print a message. */ #define PRINTMSG(...) do { \ if (cmdname != NULL) \ fprintf(stderr, "%s: ", cmdname); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) /* Print an error message and die. */ #define DIE(...) do { \ PRINTMSG(__VA_ARGS__); \ abort(); \ } while (0) /* Print a warning, if warnings are enabled. */ #define WARN(...) do { \ if (opterr == 0) \ break; \ if (opt_missing != opt_default) \ break; \ PRINTMSG(__VA_ARGS__); \ } while (0) /* Free allocated options array. */ static void getopt_atexit(void) { free(opts); opts = NULL; } /* Reset internal state. */ static void reset(int argc, char * const argv[]) { const char * p; /* If we have arguments, stash argv[0] for error messages. */ if (argc > 0) { /* Find the basename, without leading directories. */ for (p = cmdname = argv[0]; *p != '\0'; p++) { if (*p == '/') cmdname = p + 1; } } /* Discard any registered command-line options. */ free(opts); opts = NULL; /* Register atexit handler if we haven't done so already. */ if (!atexit_registered) { atexit(getopt_atexit); atexit_registered = 1; } /* We will start scanning from the first option. */ optind = 1; /* We're not in the middle of any packed options. */ packedopts = NULL; /* We haven't found any option yet. */ opt_found = (size_t)(-1); /* We're not initialized yet. */ getopt_initialized = 0; /* Finished resetting state. */ optreset = 0; } /* Search for an option string. */ static size_t searchopt(const char * os) { size_t i; /* Scan the array of options. */ for (i = 0; i < nopts; i++) { /* Is there an option in this slot? */ if (opts[i].os == NULL) continue; /* Does this match up to the length of the option string? */ if (strncmp(opts[i].os, os, opts[i].olen)) continue; /* Do we have