summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlhchavez <lhchavez@lhchavez.com>2021-08-26 05:40:20 -0700
committerlhchavez <lhchavez@lhchavez.com>2021-08-26 05:40:20 -0700
commit47c70fc54e1c521ba240429d7c7387a3b1f1ee65 (patch)
tree3393b3b08dde8c575da5b96ea20f2abf95e572cc
parent63f08e4258122d6f6ea1f04ec8c08779bf300b6c (diff)
parentfabacb7c6506f0cfb4cb29031855e50f00664b6b (diff)
downloadlibgit2-47c70fc54e1c521ba240429d7c7387a3b1f1ee65.tar.gz
Merge remote-tracking branch 'origin/main' into cgraph-write
-rw-r--r--.github/workflows/main.yml43
-rw-r--r--.github/workflows/nightly.yml75
-rw-r--r--CMakeLists.txt4
-rw-r--r--COPYING110
-rw-r--r--README.md2
-rw-r--r--ci/docker/bionic10
-rw-r--r--ci/docker/centos755
-rw-r--r--ci/docker/centos849
-rw-r--r--ci/docker/docurium1
-rw-r--r--ci/docker/entrypoint.sh4
-rw-r--r--ci/docker/focal10
-rw-r--r--ci/docker/xenial10
-rw-r--r--cmake/Findfutimens.cmake14
-rw-r--r--cmake/SelectHTTPSBackend.cmake4
-rw-r--r--deps/ntlmclient/CMakeLists.txt22
-rw-r--r--deps/ntlmclient/crypt.h23
-rw-r--r--deps/ntlmclient/crypt_commoncrypto.c40
-rw-r--r--deps/ntlmclient/crypt_commoncrypto.h6
-rw-r--r--deps/ntlmclient/crypt_mbedtls.c73
-rw-r--r--deps/ntlmclient/crypt_mbedtls.h7
-rw-r--r--deps/ntlmclient/crypt_openssl.c229
-rw-r--r--deps/ntlmclient/crypt_openssl.h79
-rw-r--r--deps/ntlmclient/ntlm.c268
-rw-r--r--deps/ntlmclient/ntlm.h22
-rw-r--r--deps/ntlmclient/ntlmclient.h19
-rw-r--r--deps/ntlmclient/unicode.h14
-rw-r--r--deps/ntlmclient/unicode_builtin.c43
-rw-r--r--deps/ntlmclient/unicode_builtin.h20
-rw-r--r--deps/ntlmclient/unicode_iconv.c80
-rw-r--r--deps/ntlmclient/unicode_iconv.h22
-rw-r--r--examples/general.c2
-rw-r--r--include/git2/attr.h89
-rw-r--r--include/git2/blame.h135
-rw-r--r--include/git2/blob.h12
-rw-r--r--include/git2/branch.h13
-rw-r--r--include/git2/checkout.h34
-rw-r--r--include/git2/common.h18
-rw-r--r--include/git2/diff.h49
-rw-r--r--include/git2/filter.h48
-rw-r--r--include/git2/remote.h5
-rw-r--r--include/git2/status.h174
-rw-r--r--include/git2/worktree.h1
-rw-r--r--script/valgrind.supp64
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/array.h8
-rw-r--r--src/attr.c224
-rw-r--r--src/attr_file.c92
-rw-r--r--src/attr_file.h36
-rw-r--r--src/attrcache.c55
-rw-r--r--src/attrcache.h8
-rw-r--r--src/blob.c15
-rw-r--r--src/branch.c16
-rw-r--r--src/cc-compat.h10
-rw-r--r--src/checkout.c20
-rw-r--r--src/common.h20
-rw-r--r--src/diff_driver.c6
-rw-r--r--src/features.h.in2
-rw-r--r--src/filter.c75
-rw-r--r--src/filter.h10
-rw-r--r--src/hash.c10
-rw-r--r--src/hash.h2
-rw-r--r--src/hash/sha1/sha1dc/sha1.c2
-rw-r--r--src/ignore.c11
-rw-r--r--src/libgit2.c18
-rw-r--r--src/netops.h2
-rw-r--r--src/odb.c16
-rw-r--r--src/odb.h3
-rw-r--r--src/path.c8
-rw-r--r--src/posix.c7
-rw-r--r--src/refdb_fs.c11
-rw-r--r--src/remote.c5
-rw-r--r--src/sortedcache.h16
-rw-r--r--src/streams/openssl.c283
-rw-r--r--src/streams/openssl.h10
-rw-r--r--src/streams/openssl_dynamic.c309
-rw-r--r--src/streams/openssl_dynamic.h348
-rw-r--r--src/streams/openssl_legacy.c203
-rw-r--r--src/streams/openssl_legacy.h63
-rw-r--r--src/transports/auth_ntlm.c2
-rw-r--r--src/transports/http.c8
-rw-r--r--src/tree.c3
-rw-r--r--src/vector.h6
-rw-r--r--src/win32/path_w32.c12
-rw-r--r--src/win32/path_w32.h1
-rw-r--r--src/win32/posix_w32.c7
-rw-r--r--src/win32/reparse.h8
-rw-r--r--src/worktree.c6
-rw-r--r--tests/attr/lookup.c3
-rw-r--r--tests/attr/repo.c14
-rw-r--r--tests/core/link.c10
-rw-r--r--tests/core/sha1.c2
-rw-r--r--tests/core/sortedcache.c6
-rw-r--r--tests/core/vector.c12
-rw-r--r--tests/fetchhead/nonetwork.c2
-rw-r--r--tests/filter/bare.c60
-rw-r--r--tests/ignore/status.c4
-rw-r--r--tests/index/addall.c4
-rw-r--r--tests/index/bypath.c5
-rw-r--r--tests/iterator/workdir.c2
-rw-r--r--tests/main.c4
-rw-r--r--tests/merge/workdir/setup.c4
-rw-r--r--tests/odb/foreach.c2
-rw-r--r--tests/odb/sorting.c29
-rw-r--r--tests/refs/branches/upstream.c25
-rw-r--r--tests/remote/fetch.c169
-rw-r--r--tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fbbin0 -> 20 bytes
-rw-r--r--tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322dbin0 -> 170 bytes
-rw-r--r--tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341cbin0 -> 658 bytes
-rw-r--r--tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0bin0 -> 658 bytes
-rw-r--r--tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f03
-rw-r--r--tests/resources/crlf.git/refs/heads/ident1
-rw-r--r--tests/resources/crlf.git/refs/heads/no-ident1
-rw-r--r--tests/worktree/merge.c6
-rw-r--r--tests/worktree/worktree.c17
114 files changed, 3343 insertions, 1030 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2bc91e73c..572f02544 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -11,7 +11,7 @@ on:
env:
docker-registry: docker.pkg.github.com
- docker-config-path: ci/docker
+ docker-config-path: source/ci/docker
jobs:
# Build the docker container images that we will use for our Linux
@@ -43,11 +43,14 @@ jobs:
dockerfile: bionic
base: multiarch/ubuntu-core:arm64-bionic
qemu: true
+ - name: centos7
+ - name: centos8
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
if: github.event_name != 'pull_request'
- name: Setup QEMU
@@ -55,7 +58,7 @@ jobs:
if: matrix.container.qemu == true
- name: Download existing container
run: |
- "${{ github.workspace }}/ci/getcontainer.sh" "${{ matrix.container.name }}" "${{ matrix.container.dockerfile }}"
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.container.name }}" "${{ matrix.container.dockerfile }}"
env:
DOCKER_REGISTRY: ${{ env.docker-registry }}
GITHUB_TOKEN: ${{ secrets.github_token }}
@@ -67,7 +70,9 @@ jobs:
BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
fi
docker build -t ${{ env.docker-registry-container-sha }} ${BASE_ARG} -f ${{ env.dockerfile }} .
+ docker tag ${{ env.docker-registry-container-sha }} ${{ env.docker-registry-container-latest }}
docker push ${{ env.docker-registry-container-sha }}
+ docker push ${{ env.docker-registry-container-latest }}
working-directory: ${{ env.docker-config-path }}
if: github.event_name != 'pull_request' && env.docker-container-exists != 'true'
@@ -86,7 +91,7 @@ jobs:
env:
CC: gcc
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DDEBUG_STRICT_ALLOC=ON
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
os: ubuntu-latest
- # Xenial, GCC, mbedTLS
container:
@@ -206,9 +211,10 @@ jobs:
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- name: Set up build environment
- run: ci/setup-${{ matrix.platform.setup-script }}.sh
+ run: source/ci/setup-${{ matrix.platform.setup-script }}.sh
shell: bash
if: matrix.platform.setup-script != ''
- name: Setup QEMU
@@ -216,7 +222,7 @@ jobs:
if: matrix.platform.container.qemu == true
- name: Download container
run: |
- "${{ github.workspace }}/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
env:
DOCKER_REGISTRY: ${{ env.docker-registry }}
GITHUB_TOKEN: ${{ secrets.github_token }}
@@ -233,8 +239,9 @@ jobs:
if [ -n "${{ matrix.platform.container.name }}" ]; then
docker run \
--rm \
- -v "$(pwd):/home/libgit2/source" \
- -w /home/libgit2/source \
+ --user libgit2:libgit2 \
+ -v "$(pwd)/source:/home/libgit2/source" \
+ -w /home/libgit2 \
-e ASAN_SYMBOLIZER_PATH \
-e CC \
-e CFLAGS \
@@ -247,11 +254,11 @@ jobs:
-e TSAN_OPTIONS \
-e UBSAN_OPTIONS \
${{ env.docker-registry-container-sha }} \
- /bin/bash -c "mkdir build && cd build && ../ci/build.sh && ../ci/test.sh"
+ /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
else
mkdir build && cd build
- ../ci/build.sh
- ../ci/test.sh
+ ../source/ci/build.sh
+ ../source/ci/test.sh
fi
shell: bash
@@ -265,18 +272,13 @@ jobs:
needs: [build_containers]
runs-on: ubuntu-latest
steps:
- - name: Setup defaults
- run: |
- if [ "${{ matrix.container.dockerfile }}" = "" ]; then
- echo "dockerfile=${{ matrix.container.dockerfile }}" >> $GITHUB_ENV
- else
- echo "dockerfile=${{ matrix.container.dockerfile }}" >> $GITHUB_ENV
- fi
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- name: Generate documentation
+ working-directory: source
run: |
git config user.name 'Documentation Generation'
git config user.email 'libgit2@users.noreply.github.com'
@@ -284,8 +286,8 @@ jobs:
docker login https://${{ env.docker-registry }} -u ${{ github.actor }} -p ${{ github.token }}
docker run \
--rm \
- -v "$(pwd):/home/libgit2/source" \
- -w /home/libgit2/source \
+ -v "$(pwd):/home/libgit2" \
+ -w /home/libgit2 \
${{ env.docker-registry }}/${{ github.repository }}/docurium:latest \
cm doc api.docurium
git checkout gh-pages
@@ -294,7 +296,8 @@ jobs:
name: Upload artifact
with:
name: api-documentation
- path: api-documentation.zip
+ path: source/api-documentation.zip
- name: Push documentation branch
+ working-directory: source
run: git push origin gh-pages
if: github.event_name != 'pull_request' && github.repository == 'libgit2/libgit2'
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index b52b398d8..47ebf464d 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -8,7 +8,7 @@ on:
env:
docker-registry: docker.pkg.github.com
- docker-config-path: ci/docker
+ docker-config-path: source/ci/docker
jobs:
# Run our nightly builds. We build a matrix with the various build
@@ -59,6 +59,14 @@ jobs:
CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
CMAKE_GENERATOR: Ninja
os: ubuntu-latest
+ - # Xenial, Clang, OpenSSL (dynamically loaded)
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
- # Focal, Clang 10, mbedTLS, MemorySanitizer
container:
name: focal
@@ -107,6 +115,40 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
os: ubuntu-latest
+ - # CentOS 7
+ container:
+ name: centos7
+ env:
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 7, OpenSSL (dynamically loaded)
+ container:
+ name: centos7
+ env:
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 8
+ container:
+ name: centos8
+ env:
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ SKIP_SSH_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 8, OpenSSL (dynamically loaded)
+ container:
+ name: centos8
+ env:
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ SKIP_SSH_TESTS: true
+ os: ubuntu-latest
- # macOS
os: macos-10.15
env:
@@ -163,6 +205,16 @@ jobs:
BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
+ - # Bionic, GCC, OpenSSL (dynamically loaded)
+ container:
+ name: bionic
+ dockerfile: bionic
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
- # Bionic, x86, Clang, OpenSSL
container:
name: bionic-x86
@@ -215,9 +267,10 @@ jobs:
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- name: Set up build environment
- run: ci/setup-${{ matrix.platform.setup-script }}.sh
+ run: source/ci/setup-${{ matrix.platform.setup-script }}.sh
shell: bash
if: matrix.platform.setup-script != ''
- name: Setup QEMU
@@ -225,7 +278,7 @@ jobs:
if: matrix.platform.container.qemu == true
- name: Download container
run: |
- "${{ github.workspace }}/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
env:
DOCKER_REGISTRY: ${{ env.docker-registry }}
GITHUB_TOKEN: ${{ secrets.github_token }}
@@ -242,8 +295,9 @@ jobs:
if [ -n "${{ matrix.platform.container.name }}" ]; then
docker run \
--rm \
- -v "$(pwd):/home/libgit2/source" \
- -w /home/libgit2/source \
+ --user libgit2:libgit2 \
+ -v "$(pwd)/source:/home/libgit2/source" \
+ -w /home/libgit2 \
-e ASAN_SYMBOLIZER_PATH \
-e CC \
-e CFLAGS \
@@ -255,11 +309,11 @@ jobs:
-e SKIP_SSH_TESTS \
-e TSAN_OPTIONS \
${{ env.docker-registry-container-sha }} \
- /bin/bash -c "mkdir build && cd build && ../ci/build.sh && ../ci/test.sh"
+ /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
else
mkdir build && cd build
- ../ci/build.sh
- ../ci/test.sh
+ ../source/ci/build.sh
+ ../source/ci/test.sh
fi
shell: bash
@@ -270,15 +324,16 @@ jobs:
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- name: Download container
run: |
- "${{ github.workspace }}/ci/getcontainer.sh" xenial
+ "${{ github.workspace }}/source/ci/getcontainer.sh" xenial
env:
DOCKER_REGISTRY: ${{ env.docker-registry }}
GITHUB_TOKEN: ${{ secrets.github_token }}
working-directory: ${{ env.docker-config-path }}
- name: Run Coverity
- run: ci/coverity.sh
+ run: source/ci/coverity.sh
env:
COVERITY_TOKEN: ${{ secrets.coverity_token }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 58214fd0e..fd046eb5c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,7 @@ INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgLibraries)
INCLUDE(FindThreads)
INCLUDE(FindStatNsec)
+INCLUDE(Findfutimens)
INCLUDE(GNUInstallDirs)
INCLUDE(IdeSplitSources)
INCLUDE(FeatureSummary)
@@ -50,6 +51,7 @@ OPTION(USE_STANDALONE_FUZZERS "Enable standalone fuzzers (compatible with gcc)"
OPTION(USE_LEAK_CHECKER "Run tests with leak checker" OFF)
OPTION(DEBUG_POOL "Enable debug pool allocator" OFF)
OPTION(DEBUG_STRICT_ALLOC "Enable strict allocator behavior" OFF)
+OPTION(DEBUG_STRICT_OPEN "Enable path validation in open" OFF)
OPTION(ENABLE_WERROR "Enable compilation with -Werror" OFF)
OPTION(USE_BUNDLED_ZLIB "Use the bundled version of zlib. Can be set to one of Bundled(ON)/Chromium. The Chromium option requires a x86_64 processor with SSE4.2 and CLMUL" OFF)
SET(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.")
@@ -232,6 +234,8 @@ ELSE ()
enable_warnings(unused-const-variable)
enable_warnings(unused-function)
enable_warnings(int-conversion)
+ enable_warnings(c11-extensions)
+ enable_warnings(c99-c11-compat)
# MinGW uses gcc, which expects POSIX formatting for printf, but
# uses the Windows C library, which uses its own format specifiers.
diff --git a/COPYING b/COPYING
index c0f61fb91..6bb39b0c1 100644
--- a/COPYING
+++ b/COPYING
@@ -420,7 +420,7 @@ The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
-
+
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@@ -1019,3 +1019,111 @@ following restrictions are are met:
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+----------------------------------------------------------------------
+
+Portions of the OpenSSL headers are included under the OpenSSL license:
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+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 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. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+====================================================================
+Copyright (c) 1998-2007 The OpenSSL Project. 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. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED 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 OpenSSL PROJECT OR
+ITS 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.
+
diff --git a/README.md b/README.md
index aa642549d..c5ef0224f 100644
--- a/README.md
+++ b/README.md
@@ -225,6 +225,8 @@ On most systems you can build the library using the following commands
Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
+If you're not familiar with CMake, [a more detailed explanation](https://preshing.com/20170511/how-to-build-a-cmake-based-project/) may be helpful.
+
Running Tests
-------------
diff --git a/ci/docker/bionic b/ci/docker/bionic
index 85bb6ecae..51af5c01c 100644
--- a/ci/docker/bionic
+++ b/ci/docker/bionic
@@ -1,4 +1,5 @@
ARG BASE=ubuntu:bionic
+
FROM ${BASE} AS apt
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
@@ -36,9 +37,8 @@ RUN cd /tmp && \
cd .. && \
rm -rf mbedtls-2.16.2
-FROM mbedtls AS configure
-COPY entrypoint.sh /usr/local/bin/entrypoint.sh
-RUN chmod a+x /usr/local/bin/entrypoint.sh
-RUN mkdir /var/run/sshd
+FROM mbedtls AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
-ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/centos7 b/ci/docker/centos7
new file mode 100644
index 000000000..098edb26a
--- /dev/null
+++ b/ci/docker/centos7
@@ -0,0 +1,55 @@
+ARG BASE=centos:7
+
+FROM ${BASE} AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ gcc \
+ gcc-c++ \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl https://libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
+ cd libssh2-1.8.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh-1.8.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS cmake
+RUN cd /tmp && \
+ curl -L https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1.tar.gz | tar -xz && \
+ cd cmake-3.21.1 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf cmake-3.21.1
+
+FROM cmake AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/centos8 b/ci/docker/centos8
new file mode 100644
index 000000000..f47198446
--- /dev/null
+++ b/ci/docker/centos8
@@ -0,0 +1,49 @@
+ARG BASE=centos:8
+
+FROM ${BASE} AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ cmake \
+ gcc \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python39 \
+ krb5-workstation \
+ krb5-libs
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl https://libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
+ cd libssh2-1.8.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh2-1.8.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
+RUN mkdir /var/run/sshd
+RUN echo "/usr/local/lib" > /etc/ld.so.conf.d/local && \
+ ldconfig
diff --git a/ci/docker/docurium b/ci/docker/docurium
index 7aa247532..1957bbb3b 100644
--- a/ci/docker/docurium
+++ b/ci/docker/docurium
@@ -1,4 +1,5 @@
ARG BASE=ubuntu:bionic
+
FROM ${BASE}
RUN apt update && apt install -y cmake pkg-config ruby ruby-dev llvm libclang-dev libssl-dev python-pygments
RUN gem install docurium
diff --git a/ci/docker/entrypoint.sh b/ci/docker/entrypoint.sh
deleted file mode 100644
index 8d96e3acd..000000000
--- a/ci/docker/entrypoint.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash -e
-useradd --shell /bin/bash libgit2
-chown --recursive libgit2:libgit2 /home/libgit2
-exec sudo --preserve-env --set-home --user=libgit2 "$@"
diff --git a/ci/docker/focal b/ci/docker/focal
index ce9726877..171387698 100644
--- a/ci/docker/focal
+++ b/ci/docker/focal
@@ -1,4 +1,5 @@
ARG BASE=ubuntu:focal
+
FROM ${BASE} AS apt
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
@@ -72,9 +73,8 @@ RUN cd /tmp && \
cd .. && \
rm -rf valgrind-3.15.0
-FROM valgrind AS configure
-COPY entrypoint.sh /usr/local/bin/entrypoint.sh
-RUN chmod a+x /usr/local/bin/entrypoint.sh
-RUN mkdir /var/run/sshd
+FROM valgrind AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
-ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/xenial b/ci/docker/xenial
index 1c8e585d2..4c3810af4 100644
--- a/ci/docker/xenial
+++ b/ci/docker/xenial
@@ -1,4 +1,5 @@
ARG BASE=ubuntu:xenial
+
FROM ${BASE} AS apt
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
@@ -59,9 +60,8 @@ RUN cd /tmp && \
cd .. && \
rm -rf valgrind-3.15.0
-FROM valgrind AS configure
-COPY entrypoint.sh /usr/local/bin/entrypoint.sh
-RUN chmod a+x /usr/local/bin/entrypoint.sh
-RUN mkdir /var/run/sshd
+FROM valgrind AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
-ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
diff --git a/cmake/Findfutimens.cmake b/cmake/Findfutimens.cmake
new file mode 100644
index 000000000..73b79528a
--- /dev/null
+++ b/cmake/Findfutimens.cmake
@@ -0,0 +1,14 @@
+INCLUDE(EnableWarnings)
+
+IF (APPLE)
+ # We cannot simply CHECK_FUNCTION_EXISTS on macOS because
+ # MACOSX_DEPLOYMENT_TARGET may be set to a version in the past
+ # that doesn't have futimens. Instead we need to enable warnings
+ # as errors, then check for the symbol existing in `sys/stat.h`,
+ # then reset warnings as errors.
+ ENABLE_WARNINGS(error)
+ CHECK_SYMBOL_EXISTS(futimens sys/stat.h HAVE_FUTIMENS)
+ DISABLE_WARNINGS(error)
+ELSE ()
+ CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
+ENDIF ()
diff --git a/cmake/SelectHTTPSBackend.cmake b/cmake/SelectHTTPSBackend.cmake
index afbeac424..4998f0f2a 100644
--- a/cmake/SelectHTTPSBackend.cmake
+++ b/cmake/SelectHTTPSBackend.cmake
@@ -108,6 +108,10 @@ IF(USE_HTTPS)
LIST(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
ELSEIF (USE_HTTPS STREQUAL "WinHTTP")
# WinHTTP setup was handled in the WinHTTP-specific block above
+ ELSEIF (USE_HTTPS STREQUAL "OpenSSL-Dynamic")
+ SET(GIT_OPENSSL 1)
+ SET(GIT_OPENSSL_DYNAMIC 1)
+ LIST(APPEND LIBGIT2_LIBS dl)
ELSE()
MESSAGE(FATAL_ERROR "Asked for backend ${USE_HTTPS} but it wasn't found")
ENDIF()
diff --git a/deps/ntlmclient/CMakeLists.txt b/deps/ntlmclient/CMakeLists.txt
index d933f4919..3e0d2c817 100644
--- a/deps/ntlmclient/CMakeLists.txt
+++ b/deps/ntlmclient/CMakeLists.txt
@@ -1,25 +1,37 @@
-FILE(GLOB SRC_NTLMCLIENT "ntlm.c" "unicode_builtin.c" "util.c")
+FILE(GLOB SRC_NTLMCLIENT "ntlm.c" "ntlm.h" "util.c" "util.h")
LIST(SORT SRC_NTLMCLIENT)
ADD_DEFINITIONS(-DNTLM_STATIC=1)
DISABLE_WARNINGS(implicit-fallthrough)
+IF(USE_ICONV)
+ ADD_DEFINITIONS(-DUNICODE_ICONV=1)
+ FILE(GLOB SRC_NTLMCLIENT_UNICODE "unicode_iconv.c" "unicode_iconv.h")
+ELSE()
+ ADD_DEFINITIONS(-DUNICODE_BUILTIN=1)
+ FILE(GLOB SRC_NTLMCLIENT_UNICODE "unicode_builtin.c" "unicode_builtin.h")
+ENDIF()
+
IF(USE_HTTPS STREQUAL "SecureTransport")
ADD_DEFINITIONS(-DCRYPT_COMMONCRYPTO)
- SET(SRC_NTLMCLIENT_CRYPTO "crypt_commoncrypto.c")
+ SET(SRC_NTLMCLIENT_CRYPTO "crypt_commoncrypto.c" "crypt_commoncrypto.h")
# CC_MD4 has been deprecated in macOS 10.15.
SET_SOURCE_FILES_PROPERTIES("crypt_commoncrypto.c" COMPILE_FLAGS "-Wno-deprecated")
ELSEIF(USE_HTTPS STREQUAL "OpenSSL")
ADD_DEFINITIONS(-DCRYPT_OPENSSL)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
- SET(SRC_NTLMCLIENT_CRYPTO "crypt_openssl.c")
+ SET(SRC_NTLMCLIENT_CRYPTO "crypt_openssl.c" "crypt_openssl.h")
+ELSEIF(USE_HTTPS STREQUAL "OpenSSL-Dynamic")
+ ADD_DEFINITIONS(-DCRYPT_OPENSSL)
+ ADD_DEFINITIONS(-DCRYPT_OPENSSL_DYNAMIC)
+ SET(SRC_NTLMCLIENT_CRYPTO "crypt_openssl.c" "crypt_openssl.h")
ELSEIF(USE_HTTPS STREQUAL "mbedTLS")
ADD_DEFINITIONS(-DCRYPT_MBEDTLS)
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
- SET(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c")
+ SET(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c" "crypt_mbedtls.h")
ELSE()
MESSAGE(FATAL_ERROR "Unable to use libgit2's HTTPS backend (${USE_HTTPS}) for NTLM crypto")
ENDIF()
-ADD_LIBRARY(ntlmclient OBJECT ${SRC_NTLMCLIENT} ${SRC_NTLMCLIENT_CRYPTO})
+ADD_LIBRARY(ntlmclient OBJECT ${SRC_NTLMCLIENT} ${SRC_NTLMCLIENT_UNICODE} ${SRC_NTLMCLIENT_CRYPTO})
diff --git a/deps/ntlmclient/crypt.h b/deps/ntlmclient/crypt.h
index 48be39946..4ad543ef7 100644
--- a/deps/ntlmclient/crypt.h
+++ b/deps/ntlmclient/crypt.h
@@ -9,6 +9,9 @@
#ifndef PRIVATE_CRYPT_COMMON_H__
#define PRIVATE_CRYPT_COMMON_H__
+#include "ntlmclient.h"
+#include "ntlm.h"
+
#if defined(CRYPT_OPENSSL)
# include "crypt_openssl.h"
#elif defined(CRYPT_MBEDTLS)
@@ -25,40 +28,42 @@
typedef unsigned char ntlm_des_block[CRYPT_DES_BLOCKSIZE];
+typedef struct ntlm_crypt_ctx ntlm_crypt_ctx;
+
+extern bool ntlm_crypt_init(ntlm_client *ntlm);
+
extern bool ntlm_random_bytes(
- ntlm_client *ntlm,
unsigned char *out,
+ ntlm_client *ntlm,
size_t len);
extern bool ntlm_des_encrypt(
ntlm_des_block *out,
+ ntlm_client *ntlm,
ntlm_des_block *plaintext,
ntlm_des_block *key);
extern bool ntlm_md4_digest(
unsigned char out[CRYPT_MD4_DIGESTSIZE],
+ ntlm_client *ntlm,
const unsigned char *in,
size_t in_len);
-extern ntlm_hmac_ctx *ntlm_hmac_ctx_init(void);
-
-extern bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx);
-
extern bool ntlm_hmac_md5_init(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *key,
size_t key_len);
extern bool ntlm_hmac_md5_update(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *data,
size_t data_len);
extern bool ntlm_hmac_md5_final(
unsigned char *out,
size_t *out_len,
- ntlm_hmac_ctx *ctx);
+ ntlm_client *ntlm);
-extern void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx);
+extern void ntlm_crypt_shutdown(ntlm_client *ntlm);
#endif /* PRIVATE_CRYPT_COMMON_H__ */
diff --git a/deps/ntlmclient/crypt_commoncrypto.c b/deps/ntlmclient/crypt_commoncrypto.c
index 54a0f097b..4ff57edd2 100644
--- a/deps/ntlmclient/crypt_commoncrypto.c
+++ b/deps/ntlmclient/crypt_commoncrypto.c
@@ -18,9 +18,15 @@
#include "ntlm.h"
#include "crypt.h"
+bool ntlm_crypt_init(ntlm_client *ntlm)
+{
+ memset(&ntlm->crypt_ctx, 0, sizeof(ntlm_crypt_ctx));
+ return true;
+}
+
bool ntlm_random_bytes(
- ntlm_client *ntlm,
unsigned char *out,
+ ntlm_client *ntlm,
size_t len)
{
int fd, ret;
@@ -49,11 +55,14 @@ bool ntlm_random_bytes(
bool ntlm_des_encrypt(
ntlm_des_block *out,
+ ntlm_client *ntlm,
ntlm_des_block *plaintext,
ntlm_des_block *key)
{
size_t written;
+ NTLM_UNUSED(ntlm);
+
CCCryptorStatus result = CCCrypt(kCCEncrypt,
kCCAlgorithmDES, kCCOptionECBMode,
key, sizeof(ntlm_des_block), NULL,
@@ -65,56 +74,47 @@ bool ntlm_des_encrypt(
bool ntlm_md4_digest(
unsigned char out[CRYPT_MD4_DIGESTSIZE],
+ ntlm_client *ntlm,
const unsigned char *in,
size_t in_len)
{
+ NTLM_UNUSED(ntlm);
return !!CC_MD4(in, in_len, out);
}
-ntlm_hmac_ctx *ntlm_hmac_ctx_init(void)
-{
- return calloc(1, sizeof(ntlm_hmac_ctx));
-}
-
-bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx)
-{
- memset(ctx, 0, sizeof(ntlm_hmac_ctx));
- return true;
-}
-
bool ntlm_hmac_md5_init(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *key,
size_t key_len)
{
- CCHmacInit(&ctx->native, kCCHmacAlgMD5, key, key_len);
+ CCHmacInit(&ntlm->crypt_ctx.hmac, kCCHmacAlgMD5, key, key_len);
return true;
}
bool ntlm_hmac_md5_update(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *data,
size_t data_len)
{
- CCHmacUpdate(&ctx->native, data, data_len);
+ CCHmacUpdate(&ntlm->crypt_ctx.hmac, data, data_len);
return true;
}
bool ntlm_hmac_md5_final(
unsigned char *out,
size_t *out_len,
- ntlm_hmac_ctx *ctx)
+ ntlm_client *ntlm)
{
if (*out_len < CRYPT_MD5_DIGESTSIZE)
return false;
- CCHmacFinal(&ctx->native, out);
+ CCHmacFinal(&ntlm->crypt_ctx.hmac, out);
*out_len = CRYPT_MD5_DIGESTSIZE;
return true;
}
-void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx)
+void ntlm_crypt_shutdown(ntlm_client *ntlm)
{
- free(ctx);
+ NTLM_UNUSED(ntlm);
}
diff --git a/deps/ntlmclient/crypt_commoncrypto.h b/deps/ntlmclient/crypt_commoncrypto.h
index e4075c9f6..e4df91d29 100644
--- a/deps/ntlmclient/crypt_commoncrypto.h
+++ b/deps/ntlmclient/crypt_commoncrypto.h
@@ -11,8 +11,8 @@
#include <CommonCrypto/CommonCrypto.h>
-typedef struct {
- CCHmacContext native;
-} ntlm_hmac_ctx;
+struct ntlm_crypt_ctx {
+ CCHmacContext hmac;
+};
#endif /* PRIVATE_CRYPT_COMMONCRYPTO_H__ */
diff --git a/deps/ntlmclient/crypt_mbedtls.c b/deps/ntlmclient/crypt_mbedtls.c
index bbab02d7d..6283c3eec 100644
--- a/deps/ntlmclient/crypt_mbedtls.c
+++ b/deps/ntlmclient/crypt_mbedtls.c
@@ -17,9 +17,24 @@
#include "ntlm.h"
#include "crypt.h"
+bool ntlm_crypt_init(ntlm_client *ntlm)
+{
+ const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+
+ mbedtls_md_init(&ntlm->crypt_ctx.hmac);
+
+ if (mbedtls_md_setup(&ntlm->crypt_ctx.hmac, info, 1) != 0) {
+ ntlm_client_set_errmsg(ntlm, "could not setup mbedtls digest");
+ return false;
+ }
+
+ return true;
+}
+
+
bool ntlm_random_bytes(
- ntlm_client *ntlm,
unsigned char *out,
+ ntlm_client *ntlm,
size_t len)
{
mbedtls_ctr_drbg_context ctr_drbg;
@@ -51,6 +66,7 @@ bool ntlm_random_bytes(
bool ntlm_des_encrypt(
ntlm_des_block *out,
+ ntlm_client *ntlm,
ntlm_des_block *plaintext,
ntlm_des_block *key)
{
@@ -60,8 +76,10 @@ bool ntlm_des_encrypt(
mbedtls_des_init(&ctx);
if (mbedtls_des_setkey_enc(&ctx, *key) ||
- mbedtls_des_crypt_ecb(&ctx, *plaintext, *out))
+ mbedtls_des_crypt_ecb(&ctx, *plaintext, *out)) {
+ ntlm_client_set_errmsg(ntlm, "DES encryption failed");
goto done;
+ }
success = true;
@@ -72,11 +90,14 @@ done:
bool ntlm_md4_digest(
unsigned char out[CRYPT_MD4_DIGESTSIZE],
+ ntlm_client *ntlm,
const unsigned char *in,
size_t in_len)
{
mbedtls_md4_context ctx;
+ NTLM_UNUSED(ntlm);
+
mbedtls_md4_init(&ctx);
mbedtls_md4_starts(&ctx);
mbedtls_md4_update(&ctx, in, in_len);
@@ -86,60 +107,40 @@ bool ntlm_md4_digest(
return true;
}
-ntlm_hmac_ctx *ntlm_hmac_ctx_init(void)
-{
- ntlm_hmac_ctx *ctx;
- const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
-
- if ((ctx = calloc(1, sizeof(ntlm_hmac_ctx))) == NULL)
- return NULL;
-
- mbedtls_md_init(&ctx->mbed);
-
- if (mbedtls_md_setup(&ctx->mbed, info, 1) != 0) {
- free(ctx);
- return false;
- }
-
- return ctx;
-}
-
-bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx)
-{
- return !mbedtls_md_hmac_reset(&ctx->mbed);
-}
-
bool ntlm_hmac_md5_init(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *key,
size_t key_len)
{
- return !mbedtls_md_hmac_starts(&ctx->mbed, key, key_len);
+ if (ntlm->crypt_ctx.hmac_initialized) {
+ if (mbedtls_md_hmac_reset(&ntlm->crypt_ctx.hmac))
+ return false;
+ }
+
+ ntlm->crypt_ctx.hmac_initialized = !mbedtls_md_hmac_starts(&ntlm->crypt_ctx.hmac, key, key_len);
+ return ntlm->crypt_ctx.hmac_initialized;
}
bool ntlm_hmac_md5_update(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *in,
size_t in_len)
{
- return !mbedtls_md_hmac_update(&ctx->mbed, in, in_len);
+ return !mbedtls_md_hmac_update(&ntlm->crypt_ctx.hmac, in, in_len);
}
bool ntlm_hmac_md5_final(
unsigned char *out,
size_t *out_len,
- ntlm_hmac_ctx *ctx)
+ ntlm_client *ntlm)
{
if (*out_len < CRYPT_MD5_DIGESTSIZE)
return false;
- return !mbedtls_md_hmac_finish(&ctx->mbed, out);
+ return !mbedtls_md_hmac_finish(&ntlm->crypt_ctx.hmac, out);
}
-void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx)
+void ntlm_crypt_shutdown(ntlm_client *ntlm)
{
- if (ctx) {
- mbedtls_md_free(&ctx->mbed);
- free(ctx);
- }
+ mbedtls_md_free(&ntlm->crypt_ctx.hmac);
}
diff --git a/deps/ntlmclient/crypt_mbedtls.h b/deps/ntlmclient/crypt_mbedtls.h
index eb49a4596..2fc85035d 100644
--- a/deps/ntlmclient/crypt_mbedtls.h
+++ b/deps/ntlmclient/crypt_mbedtls.h
@@ -11,8 +11,9 @@
#include "mbedtls/md.h"
-typedef struct {
- mbedtls_md_context_t mbed;
-} ntlm_hmac_ctx;
+struct ntlm_crypt_ctx {
+ mbedtls_md_context_t hmac;
+ unsigned int hmac_initialized : 1;
+};
#endif /* PRIVATE_CRYPT_MBEDTLS_H__ */
diff --git a/deps/ntlmclient/crypt_openssl.c b/deps/ntlmclient/crypt_openssl.c
index c0d36d891..463eae4f8 100644
--- a/deps/ntlmclient/crypt_openssl.c
+++ b/deps/ntlmclient/crypt_openssl.c
@@ -9,26 +9,166 @@
#include <stdlib.h>
#include <string.h>
-#include <openssl/rand.h>
-#include <openssl/des.h>
-#include <openssl/md4.h>
-#include <openssl/hmac.h>
-#include <openssl/err.h>
+#ifdef CRYPT_OPENSSL_DYNAMIC
+# include <dlfcn.h>
+#else
+# include <openssl/rand.h>
+# include <openssl/des.h>
+# include <openssl/md4.h>
+# include <openssl/hmac.h>
+# include <openssl/err.h>
+#endif
#include "ntlm.h"
#include "compat.h"
#include "util.h"
#include "crypt.h"
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(CRYPT_OPENSSL_DYNAMIC)
+
+static inline HMAC_CTX *HMAC_CTX_new(void)
+{
+ return calloc(1, sizeof(HMAC_CTX));
+}
+
+static inline int HMAC_CTX_reset(HMAC_CTX *ctx)
+{
+ ntlm_memzero(ctx, sizeof(HMAC_CTX));
+ return 1;
+}
+
+static inline void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ free(ctx);
+}
+
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(CRYPT_OPENSSL_DYNAMIC)
+
+static inline void HMAC_CTX_cleanup(HMAC_CTX *ctx)
+{
+ NTLM_UNUSED(ctx);
+}
+
+#endif
+
+
+#ifdef CRYPT_OPENSSL_DYNAMIC
+
+static bool ntlm_crypt_init_functions(ntlm_client *ntlm)
+{
+ void *handle;
+
+ if ((handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL &&
+ (handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
+ (handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
+ (handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
+ (handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) {
+ ntlm_client_set_errmsg(ntlm, "could not open libssl");
+ return false;
+ }
+
+ ntlm->crypt_ctx.des_set_key_fn = dlsym(handle, "DES_set_key");
+ ntlm->crypt_ctx.des_ecb_encrypt_fn = dlsym(handle, "DES_ecb_encrypt");
+ ntlm->crypt_ctx.err_get_error_fn = dlsym(handle, "ERR_get_error");
+ ntlm->crypt_ctx.err_lib_error_string_fn = dlsym(handle, "ERR_lib_error_string");
+ ntlm->crypt_ctx.evp_md5_fn = dlsym(handle, "EVP_md5");
+ ntlm->crypt_ctx.hmac_ctx_new_fn = dlsym(handle, "HMAC_CTX_new");
+ ntlm->crypt_ctx.hmac_ctx_free_fn = dlsym(handle, "HMAC_CTX_free");
+ ntlm->crypt_ctx.hmac_ctx_reset_fn = dlsym(handle, "HMAC_CTX_reset");
+ ntlm->crypt_ctx.hmac_init_ex_fn = dlsym(handle, "HMAC_Init_ex");
+ ntlm->crypt_ctx.hmac_update_fn = dlsym(handle, "HMAC_Update");
+ ntlm->crypt_ctx.hmac_final_fn = dlsym(handle, "HMAC_Final");
+ ntlm->crypt_ctx.md4_fn = dlsym(handle, "MD4");
+ ntlm->crypt_ctx.rand_bytes_fn = dlsym(handle, "RAND_bytes");
+
+ if (!ntlm->crypt_ctx.des_set_key_fn ||
+ !ntlm->crypt_ctx.des_ecb_encrypt_fn ||
+ !ntlm->crypt_ctx.err_get_error_fn ||
+ !ntlm->crypt_ctx.err_lib_error_string_fn ||
+ !ntlm->crypt_ctx.evp_md5_fn ||
+ !ntlm->crypt_ctx.hmac_init_ex_fn ||
+ !ntlm->crypt_ctx.hmac_update_fn ||
+ !ntlm->crypt_ctx.hmac_final_fn ||
+ !ntlm->crypt_ctx.md4_fn ||
+ !ntlm->crypt_ctx.rand_bytes_fn) {
+ ntlm_client_set_errmsg(ntlm, "could not load libssl functions");
+ dlclose(handle);
+ return false;
+ }
+
+ /* Toggle legacy HMAC context functions */
+ if (ntlm->crypt_ctx.hmac_ctx_new_fn &&
+ ntlm->crypt_ctx.hmac_ctx_free_fn &&
+ ntlm->crypt_ctx.hmac_ctx_reset_fn) {
+ ntlm->crypt_ctx.hmac_ctx_cleanup_fn = HMAC_CTX_cleanup;
+ } else {
+ ntlm->crypt_ctx.hmac_ctx_cleanup_fn = dlsym(handle, "HMAC_CTX_cleanup");
+
+ if (!ntlm->crypt_ctx.hmac_ctx_cleanup_fn) {
+ ntlm_client_set_errmsg(ntlm, "could not load legacy libssl functions");
+ dlclose(handle);
+ return false;
+ }
+
+ ntlm->crypt_ctx.hmac_ctx_new_fn = HMAC_CTX_new;
+ ntlm->crypt_ctx.hmac_ctx_free_fn = HMAC_CTX_free;
+ ntlm->crypt_ctx.hmac_ctx_reset_fn = HMAC_CTX_reset;
+ }
+
+ ntlm->crypt_ctx.openssl_handle = handle;
+ return true;
+}
+
+#else /* CRYPT_OPENSSL_DYNAMIC */
+
+static bool ntlm_crypt_init_functions(ntlm_client *ntlm)
+{
+ ntlm->crypt_ctx.des_set_key_fn = DES_set_key;
+ ntlm->crypt_ctx.des_ecb_encrypt_fn = DES_ecb_encrypt;
+ ntlm->crypt_ctx.err_get_error_fn = ERR_get_error;
+ ntlm->crypt_ctx.err_lib_error_string_fn = ERR_lib_error_string;
+ ntlm->crypt_ctx.evp_md5_fn = EVP_md5;
+ ntlm->crypt_ctx.hmac_ctx_new_fn = HMAC_CTX_new;
+ ntlm->crypt_ctx.hmac_ctx_free_fn = HMAC_CTX_free;
+ ntlm->crypt_ctx.hmac_ctx_reset_fn = HMAC_CTX_reset;
+ ntlm->crypt_ctx.hmac_ctx_cleanup_fn = HMAC_CTX_cleanup;
+ ntlm->crypt_ctx.hmac_init_ex_fn = HMAC_Init_ex;
+ ntlm->crypt_ctx.hmac_update_fn = HMAC_Update;
+ ntlm->crypt_ctx.hmac_final_fn = HMAC_Final;
+ ntlm->crypt_ctx.md4_fn = MD4;
+ ntlm->crypt_ctx.rand_bytes_fn = RAND_bytes;
+
+ return true;
+}
+
+#endif /* CRYPT_OPENSSL_DYNAMIC */
+
+bool ntlm_crypt_init(ntlm_client *ntlm)
+{
+ if (!ntlm_crypt_init_functions(ntlm))
+ return false;
+
+ ntlm->crypt_ctx.hmac = ntlm->crypt_ctx.hmac_ctx_new_fn();
+
+ if (ntlm->crypt_ctx.hmac == NULL) {
+ ntlm_client_set_errmsg(ntlm, "out of memory");
+ return false;
+ }
+
+ return true;
+}
+
bool ntlm_random_bytes(
- ntlm_client *ntlm,
unsigned char *out,
+ ntlm_client *ntlm,
size_t len)
{
- int rc = RAND_bytes(out, len);
+ int rc = ntlm->crypt_ctx.rand_bytes_fn(out, len);
if (rc != 1) {
- ntlm_client_set_errmsg(ntlm, ERR_lib_error_string(ERR_get_error()));
+ ntlm_client_set_errmsg(ntlm, ntlm->crypt_ctx.err_lib_error_string_fn(ntlm->crypt_ctx.err_get_error_fn()));
return false;
}
@@ -37,94 +177,81 @@ bool ntlm_random_bytes(
bool ntlm_des_encrypt(
ntlm_des_block *out,
+ ntlm_client *ntlm,
ntlm_des_block *plaintext,
ntlm_des_block *key)
{
DES_key_schedule keysched;
+ NTLM_UNUSED(ntlm);
+
memset(out, 0, sizeof(ntlm_des_block));
- DES_set_key(key, &keysched);
- DES_ecb_encrypt(plaintext, out, &keysched, DES_ENCRYPT);
+ ntlm->crypt_ctx.des_set_key_fn(key, &keysched);
+ ntlm->crypt_ctx.des_ecb_encrypt_fn(plaintext, out, &keysched, DES_ENCRYPT);
return true;
}
bool ntlm_md4_digest(
unsigned char out[CRYPT_MD4_DIGESTSIZE],
+ ntlm_client *ntlm,
const unsigned char *in,
size_t in_len)
{
- MD4(in, in_len, out);
+ ntlm->crypt_ctx.md4_fn(in, in_len, out);
return true;
}
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static inline void HMAC_CTX_free(HMAC_CTX *ctx)
-{
- if (ctx)
- HMAC_CTX_cleanup(ctx);
-
- free(ctx);
-}
-
-static inline int HMAC_CTX_reset(HMAC_CTX *ctx)
-{
- HMAC_CTX_cleanup(ctx);
- ntlm_memzero(ctx, sizeof(HMAC_CTX));
- return 1;
-}
-
-static inline HMAC_CTX *HMAC_CTX_new(void)
-{
- return calloc(1, sizeof(HMAC_CTX));
-}
-#endif
-
-ntlm_hmac_ctx *ntlm_hmac_ctx_init(void)
-{
- return HMAC_CTX_new();
-}
-
-bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx)
-{
- return HMAC_CTX_reset(ctx);
-}
-
bool ntlm_hmac_md5_init(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *key,
size_t key_len)
{
- return HMAC_Init_ex(ctx, key, key_len, EVP_md5(), NULL);
+ const EVP_MD *md5 = ntlm->crypt_ctx.evp_md5_fn();
+
+ ntlm->crypt_ctx.hmac_ctx_cleanup_fn(ntlm->crypt_ctx.hmac);
+
+ return ntlm->crypt_ctx.hmac_ctx_reset_fn(ntlm->crypt_ctx.hmac) &&
+ ntlm->crypt_ctx.hmac_init_ex_fn(ntlm->crypt_ctx.hmac, key, key_len, md5, NULL);
}
bool ntlm_hmac_md5_update(
- ntlm_hmac_ctx *ctx,
+ ntlm_client *ntlm,
const unsigned char *in,
size_t in_len)
{
- return HMAC_Update(ctx, in, in_len);
+ return ntlm->crypt_ctx.hmac_update_fn(ntlm->crypt_ctx.hmac, in, in_len);
}
bool ntlm_hmac_md5_final(
unsigned char *out,
size_t *out_len,
- ntlm_hmac_ctx *ctx)
+ ntlm_client *ntlm)
{
unsigned int len;
if (*out_len < CRYPT_MD5_DIGESTSIZE)
return false;
- if (!HMAC_Final(ctx, out, &len))
+ if (!ntlm->crypt_ctx.hmac_final_fn(ntlm->crypt_ctx.hmac, out, &len))
return false;
*out_len = len;
return true;
}
-void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx)
+void ntlm_crypt_shutdown(ntlm_client *ntlm)
{
- HMAC_CTX_free(ctx);
+ if (ntlm->crypt_ctx.hmac) {
+ ntlm->crypt_ctx.hmac_ctx_cleanup_fn(ntlm->crypt_ctx.hmac);
+ ntlm->crypt_ctx.hmac_ctx_free_fn(ntlm->crypt_ctx.hmac);
+ }
+
+#ifdef CRYPT_OPENSSL_DYNAMIC
+ if (ntlm->crypt_ctx.openssl_handle)
+ dlclose(ntlm->crypt_ctx.openssl_handle);
+#endif
+
+ memset(&ntlm->crypt_ctx, 0, sizeof(ntlm_crypt_ctx));
}
diff --git a/deps/ntlmclient/crypt_openssl.h b/deps/ntlmclient/crypt_openssl.h
index 4195db9a5..8654027db 100644
--- a/deps/ntlmclient/crypt_openssl.h
+++ b/deps/ntlmclient/crypt_openssl.h
@@ -9,13 +9,82 @@
#ifndef PRIVATE_CRYPT_OPENSSL_H__
#define PRIVATE_CRYPT_OPENSSL_H__
-#include <openssl/hmac.h>
+#ifndef CRYPT_OPENSSL_DYNAMIC
+# include <openssl/des.h>
+# include <openssl/hmac.h>
+#endif
/* OpenSSL 1.1.0 uses opaque structs, we'll reuse these. */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-typedef struct hmac_ctx_st ntlm_hmac_ctx;
-#else
-# define ntlm_hmac_ctx HMAC_CTX
+#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L
+# define HMAC_CTX struct hmac_ctx_st
+#endif
+
+#ifdef CRYPT_OPENSSL_DYNAMIC
+typedef unsigned char DES_cblock[8];
+typedef unsigned char const_DES_cblock[8];
+
+typedef unsigned long DES_LONG;
+
+typedef struct DES_ks {
+ union {
+ DES_cblock cblock;
+ DES_LONG deslong[2];
+ } ks[16];
+} DES_key_schedule;
+
+#define DES_ENCRYPT 1
+
+typedef void EVP_MD;
+typedef void ENGINE;
+typedef void EVP_PKEY_CTX;
+
+#define HMAC_MAX_MD_CBLOCK 128
+
+typedef struct env_md_ctx_st EVP_MD_CTX;
+struct env_md_ctx_st {
+ const EVP_MD *digest;
+ ENGINE *engine;
+ unsigned long flags;
+ void *md_data;
+ EVP_PKEY_CTX *pctx;
+ int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
+};
+
+typedef struct hmac_ctx_st {
+ const EVP_MD *md;
+ EVP_MD_CTX md_ctx;
+ EVP_MD_CTX i_ctx;
+ EVP_MD_CTX o_ctx;
+ unsigned int key_length;
+ unsigned char key[HMAC_MAX_MD_CBLOCK];
+} HMAC_CTX;
#endif
+struct ntlm_crypt_ctx {
+ HMAC_CTX *hmac;
+
+ void *openssl_handle;
+
+ void (*des_ecb_encrypt_fn)(const_DES_cblock *input, DES_cblock *output, DES_key_schedule *ks, int enc);
+ int (*des_set_key_fn)(const_DES_cblock *key, DES_key_schedule *schedule);
+
+ unsigned long (*err_get_error_fn)(void);
+ const char *(*err_lib_error_string_fn)(unsigned long e);
+
+ const EVP_MD *(*evp_md5_fn)(void);
+
+ HMAC_CTX *(*hmac_ctx_new_fn)(void);
+ int (*hmac_ctx_reset_fn)(HMAC_CTX *ctx);
+ void (*hmac_ctx_free_fn)(HMAC_CTX *ctx);
+ void (*hmac_ctx_cleanup_fn)(HMAC_CTX *ctx);
+
+ int (*hmac_init_ex_fn)(HMAC_CTX *ctx, const void *key, int key_len, const EVP_MD *md, ENGINE *impl);
+ int (*hmac_update_fn)(HMAC_CTX *ctx, const unsigned char *data, size_t len);
+ int (*hmac_final_fn)(HMAC_CTX *ctx, unsigned char *md, unsigned int *len);
+
+ unsigned char *(*md4_fn)(const unsigned char *d, size_t n, unsigned char *md);
+
+ int (*rand_bytes_fn)(unsigned char *buf, int num);
+};
+
#endif /* PRIVATE_CRYPT_OPENSSL_H__ */
diff --git a/deps/ntlmclient/ntlm.c b/deps/ntlmclient/ntlm.c
index 470a90143..3393be915 100644
--- a/deps/ntlmclient/ntlm.c
+++ b/deps/ntlmclient/ntlm.c
@@ -9,7 +9,6 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
-#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
@@ -24,6 +23,18 @@
#include "compat.h"
#include "util.h"
+#define NTLM_ASSERT_ARG(expr) do { \
+ if (!(expr)) \
+ return NTLM_CLIENT_ERROR_INVALID_INPUT; \
+ } while(0)
+
+#define NTLM_ASSERT(ntlm, expr) do { \
+ if (!(expr)) { \
+ ntlm_client_set_errmsg(ntlm, "internal error: " #expr); \
+ return -1; \
+ } \
+ } while(0)
+
unsigned char ntlm_client_signature[] = NTLM_SIGNATURE;
static bool supports_unicode(ntlm_client *ntlm)
@@ -52,17 +63,20 @@ ntlm_client *ntlm_client_init(ntlm_client_flags flags)
ntlm->flags = flags;
- if ((ntlm->hmac_ctx = ntlm_hmac_ctx_init()) == NULL ||
- (ntlm->unicode_ctx = ntlm_unicode_ctx_init(ntlm)) == NULL) {
- ntlm_hmac_ctx_free(ntlm->hmac_ctx);
- ntlm_unicode_ctx_free(ntlm->unicode_ctx);
- free(ntlm);
- return NULL;
- }
-
return ntlm;
}
+#define ENSURE_INITIALIZED(ntlm) \
+ do { \
+ if (!(ntlm)->unicode_initialized) \
+ (ntlm)->unicode_initialized = ntlm_unicode_init((ntlm)); \
+ if (!(ntlm)->crypt_initialized) \
+ (ntlm)->crypt_initialized = ntlm_crypt_init((ntlm)); \
+ if (!(ntlm)->unicode_initialized || \
+ !(ntlm)->crypt_initialized) \
+ return -1; \
+ } while(0)
+
void ntlm_client_set_errmsg(ntlm_client *ntlm, const char *errmsg)
{
ntlm->state = NTLM_STATE_ERROR;
@@ -71,7 +85,9 @@ void ntlm_client_set_errmsg(ntlm_client *ntlm, const char *errmsg)
const char *ntlm_client_errmsg(ntlm_client *ntlm)
{
- assert(ntlm);
+ if (!ntlm)
+ return "internal error";
+
return ntlm->errmsg ? ntlm->errmsg : "no error";
}
@@ -81,7 +97,7 @@ int ntlm_client_set_version(
uint8_t minor,
uint16_t build)
{
- assert(ntlm);
+ NTLM_ASSERT_ARG(ntlm);
ntlm->host_version.major = major;
ntlm->host_version.minor = minor;
@@ -93,20 +109,25 @@ int ntlm_client_set_version(
return 0;
}
+#define reset(ptr) do { free(ptr); ptr = NULL; } while(0)
+
+static void free_hostname(ntlm_client *ntlm)
+{
+ reset(ntlm->hostname);
+ reset(ntlm->hostdomain);
+ reset(ntlm->hostname_utf16);
+ ntlm->hostname_utf16_len = 0;
+}
+
int ntlm_client_set_hostname(
ntlm_client *ntlm,
const char *hostname,
const char *domain)
{
- assert(ntlm);
+ NTLM_ASSERT_ARG(ntlm);
+ ENSURE_INITIALIZED(ntlm);
- free(ntlm->hostname);
- free(ntlm->hostdomain);
- free(ntlm->hostname_utf16);
-
- ntlm->hostname = NULL;
- ntlm->hostdomain = NULL;
- ntlm->hostname_utf16 = NULL;
+ free_hostname(ntlm);
if (hostname && (ntlm->hostname = strdup(hostname)) == NULL) {
ntlm_client_set_errmsg(ntlm, "out of memory");
@@ -121,7 +142,7 @@ int ntlm_client_set_hostname(
if (hostname && supports_unicode(ntlm) && !ntlm_unicode_utf8_to_16(
&ntlm->hostname_utf16,
&ntlm->hostname_utf16_len,
- ntlm->unicode_ctx,
+ ntlm,
hostname,
strlen(hostname)))
return -1;
@@ -137,25 +158,20 @@ static void free_credentials(ntlm_client *ntlm)
if (ntlm->password_utf16)
ntlm_memzero(ntlm->password_utf16, ntlm->password_utf16_len);
- free(ntlm->username);
- free(ntlm->username_upper);
- free(ntlm->userdomain);
- free(ntlm->password);
-
- free(ntlm->username_utf16);
- free(ntlm->username_upper_utf16);
- free(ntlm->userdomain_utf16);
- free(ntlm->password_utf16);
-
- ntlm->username = NULL;
- ntlm->username_upper = NULL;
- ntlm->userdomain = NULL;
- ntlm->password = NULL;
-
- ntlm->username_utf16 = NULL;
- ntlm->username_upper_utf16 = NULL;
- ntlm->userdomain_utf16 = NULL;
- ntlm->password_utf16 = NULL;
+ reset(ntlm->username);
+ reset(ntlm->username_upper);
+ reset(ntlm->userdomain);
+ reset(ntlm->password);
+
+ reset(ntlm->username_utf16);
+ reset(ntlm->username_upper_utf16);
+ reset(ntlm->userdomain_utf16);
+ reset(ntlm->password_utf16);
+
+ ntlm->username_utf16_len = 0;
+ ntlm->username_upper_utf16_len = 0;
+ ntlm->userdomain_utf16_len = 0;
+ ntlm->password_utf16_len = 0;
}
int ntlm_client_set_credentials(
@@ -164,7 +180,8 @@ int ntlm_client_set_credentials(
const char *domain,
const char *password)
{
- assert(ntlm);
+ NTLM_ASSERT_ARG(ntlm);
+ ENSURE_INITIALIZED(ntlm);
free_credentials(ntlm);
@@ -185,7 +202,7 @@ int ntlm_client_set_credentials(
if (!ntlm_unicode_utf8_to_16(
&ntlm->username_utf16,
&ntlm->username_utf16_len,
- ntlm->unicode_ctx,
+ ntlm,
ntlm->username,
strlen(ntlm->username)))
return -1;
@@ -193,7 +210,7 @@ int ntlm_client_set_credentials(
if (!ntlm_unicode_utf8_to_16(
&ntlm->username_upper_utf16,
&ntlm->username_upper_utf16_len,
- ntlm->unicode_ctx,
+ ntlm,
ntlm->username_upper,
strlen(ntlm->username_upper)))
return -1;
@@ -202,7 +219,7 @@ int ntlm_client_set_credentials(
if (domain && supports_unicode(ntlm) && !ntlm_unicode_utf8_to_16(
&ntlm->userdomain_utf16,
&ntlm->userdomain_utf16_len,
- ntlm->unicode_ctx,
+ ntlm,
ntlm->userdomain,
strlen(ntlm->userdomain)))
return -1;
@@ -212,7 +229,8 @@ int ntlm_client_set_credentials(
int ntlm_client_set_target(ntlm_client *ntlm, const char *target)
{
- assert(ntlm);
+ NTLM_ASSERT_ARG(ntlm);
+ ENSURE_INITIALIZED(ntlm);
free(ntlm->target);
free(ntlm->target_utf16);
@@ -229,7 +247,7 @@ int ntlm_client_set_target(ntlm_client *ntlm, const char *target)
if (supports_unicode(ntlm) && !ntlm_unicode_utf8_to_16(
&ntlm->target_utf16,
&ntlm->target_utf16_len,
- ntlm->unicode_ctx,
+ ntlm,
ntlm->target,
strlen(ntlm->target)))
return -1;
@@ -240,14 +258,16 @@ int ntlm_client_set_target(ntlm_client *ntlm, const char *target)
int ntlm_client_set_nonce(ntlm_client *ntlm, uint64_t nonce)
{
- assert(ntlm);
+ NTLM_ASSERT_ARG(ntlm);
+
ntlm->nonce = nonce;
return 0;
}
int ntlm_client_set_timestamp(ntlm_client *ntlm, uint64_t timestamp)
{
- assert(ntlm);
+ NTLM_ASSERT_ARG(ntlm);
+
ntlm->timestamp = timestamp;
return 0;
}
@@ -475,7 +495,7 @@ static inline bool read_string_unicode(
size_t out_len;
int ret = ntlm_unicode_utf16_to_8(out,
&out_len,
- ntlm->unicode_ctx,
+ ntlm,
(char *)&message->buf[message->pos],
string_len);
@@ -593,7 +613,9 @@ int ntlm_client_negotiate(
size_t hostname_offset = 0;
uint32_t flags = 0;
- assert(out && out_len && ntlm);
+ NTLM_ASSERT_ARG(out);
+ NTLM_ASSERT_ARG(out_len);
+ NTLM_ASSERT_ARG(ntlm);
*out = NULL;
*out_len = 0;
@@ -676,20 +698,22 @@ int ntlm_client_negotiate(
return -1;
if (hostname_len > 0) {
- assert(hostname_offset == ntlm->negotiate.pos);
+ NTLM_ASSERT(ntlm, hostname_offset == ntlm->negotiate.pos);
+
if (!write_buf(ntlm, &ntlm->negotiate,
(const unsigned char *)ntlm->hostname, hostname_len))
return -1;
}
if (domain_len > 0) {
- assert(domain_offset == ntlm->negotiate.pos);
+ NTLM_ASSERT(ntlm, domain_offset == ntlm->negotiate.pos);
+
if (!write_buf(ntlm, &ntlm->negotiate,
(const unsigned char *)ntlm->hostdomain, domain_len))
return -1;
}
- assert(ntlm->negotiate.pos == ntlm->negotiate.len);
+ NTLM_ASSERT(ntlm, ntlm->negotiate.pos == ntlm->negotiate.len);
ntlm->state = NTLM_STATE_CHALLENGE;
@@ -711,7 +735,10 @@ int ntlm_client_set_challenge(
uint32_t name_offset, info_offset = 0;
bool unicode, has_target_info = false;
- assert(ntlm && (challenge_msg || !challenge_msg_len));
+ NTLM_ASSERT_ARG(ntlm);
+ NTLM_ASSERT_ARG(challenge_msg || !challenge_msg_len);
+
+ ENSURE_INITIALIZED(ntlm);
if (ntlm->state != NTLM_STATE_NEGOTIATE &&
ntlm->state != NTLM_STATE_CHALLENGE) {
@@ -940,6 +967,7 @@ static void des_key_from_password(
static inline bool generate_lm_hash(
ntlm_des_block out[2],
+ ntlm_client *ntlm,
const char *password)
{
/* LM encrypts this known plaintext using the password as a key */
@@ -968,8 +996,8 @@ static inline bool generate_lm_hash(
des_key_from_password(&key1, keystr1, keystr1_len);
des_key_from_password(&key2, keystr2, keystr2_len);
- return ntlm_des_encrypt(&out[0], &plaintext, &key1) &&
- ntlm_des_encrypt(&out[1], &plaintext, &key2);
+ return ntlm_des_encrypt(&out[0], ntlm, &plaintext, &key1) &&
+ ntlm_des_encrypt(&out[1], ntlm, &plaintext, &key2);
}
static void des_keys_from_lm_hash(ntlm_des_block out[3], ntlm_des_block lm_hash[2])
@@ -994,16 +1022,16 @@ static bool generate_lm_response(ntlm_client *ntlm)
ntlm_des_block *challenge = (ntlm_des_block *)&ntlm->challenge.nonce;
/* Generate the LM hash from the password */
- if (!generate_lm_hash(lm_hash, ntlm->password))
+ if (!generate_lm_hash(lm_hash, ntlm, ntlm->password))
return false;
/* Convert that LM hash to three DES keys */
des_keys_from_lm_hash(key, lm_hash);
/* Finally, encrypt the challenge with each of these keys */
- if (!ntlm_des_encrypt(&lm_response[0], challenge, &key[0]) ||
- !ntlm_des_encrypt(&lm_response[1], challenge, &key[1]) ||
- !ntlm_des_encrypt(&lm_response[2], challenge, &key[2]))
+ if (!ntlm_des_encrypt(&lm_response[0], ntlm, challenge, &key[0]) ||
+ !ntlm_des_encrypt(&lm_response[1], ntlm, challenge, &key[1]) ||
+ !ntlm_des_encrypt(&lm_response[2], ntlm, challenge, &key[2]))
return false;
memcpy(&ntlm->lm_response[0], lm_response[0], 8);
@@ -1022,12 +1050,13 @@ static bool generate_ntlm_hash(
if (ntlm->password && !ntlm_unicode_utf8_to_16(
&ntlm->password_utf16,
&ntlm->password_utf16_len,
- ntlm->unicode_ctx,
+ ntlm,
ntlm->password,
strlen(ntlm->password)))
return false;
return ntlm_md4_digest(out,
+ ntlm,
(const unsigned char *)ntlm->password_utf16,
ntlm->password_utf16_len);
}
@@ -1048,9 +1077,9 @@ static bool generate_ntlm_response(ntlm_client *ntlm)
des_key_from_password(&key[2], &ntlm_hash[14], 2);
/* Finally, encrypt the challenge with each of these keys */
- if (!ntlm_des_encrypt(&ntlm_response[0], challenge, &key[0]) ||
- !ntlm_des_encrypt(&ntlm_response[1], challenge, &key[1]) ||
- !ntlm_des_encrypt(&ntlm_response[2], challenge, &key[2]))
+ if (!ntlm_des_encrypt(&ntlm_response[0], ntlm, challenge, &key[0]) ||
+ !ntlm_des_encrypt(&ntlm_response[1], ntlm, challenge, &key[1]) ||
+ !ntlm_des_encrypt(&ntlm_response[2], ntlm, challenge, &key[2]))
return false;
memcpy(&ntlm->ntlm_response[0], ntlm_response[0], 8);
@@ -1081,16 +1110,15 @@ static bool generate_ntlm2_hash(
target_len = ntlm->target_utf16_len;
}
- if (!ntlm_hmac_ctx_reset(ntlm->hmac_ctx) ||
- !ntlm_hmac_md5_init(ntlm->hmac_ctx, ntlm_hash, sizeof(ntlm_hash)) ||
- !ntlm_hmac_md5_update(ntlm->hmac_ctx, username, username_len) ||
- !ntlm_hmac_md5_update(ntlm->hmac_ctx, target, target_len) ||
- !ntlm_hmac_md5_final(out, &out_len, ntlm->hmac_ctx)) {
+ if (!ntlm_hmac_md5_init(ntlm, ntlm_hash, sizeof(ntlm_hash)) ||
+ !ntlm_hmac_md5_update(ntlm, username, username_len) ||
+ !ntlm_hmac_md5_update(ntlm, target, target_len) ||
+ !ntlm_hmac_md5_final(out, &out_len, ntlm)) {
ntlm_client_set_errmsg(ntlm, "failed to create HMAC-MD5");
return false;
}
- assert(out_len == NTLM_NTLM2_HASH_LEN);
+ NTLM_ASSERT(ntlm, out_len == NTLM_NTLM2_HASH_LEN);
return true;
}
@@ -1103,18 +1131,15 @@ static bool generate_ntlm2_challengehash(
{
size_t out_len = 16;
- if (!ntlm_hmac_ctx_reset(ntlm->hmac_ctx) ||
- !ntlm_hmac_md5_init(ntlm->hmac_ctx,
- ntlm2_hash, NTLM_NTLM2_HASH_LEN) ||
- !ntlm_hmac_md5_update(ntlm->hmac_ctx,
- (const unsigned char *)&ntlm->challenge.nonce, 8) ||
- !ntlm_hmac_md5_update(ntlm->hmac_ctx, blob, blob_len) ||
- !ntlm_hmac_md5_final(out, &out_len, ntlm->hmac_ctx)) {
+ if (!ntlm_hmac_md5_init(ntlm, ntlm2_hash, NTLM_NTLM2_HASH_LEN) ||
+ !ntlm_hmac_md5_update(ntlm, (const unsigned char *)&ntlm->challenge.nonce, 8) ||
+ !ntlm_hmac_md5_update(ntlm, blob, blob_len) ||
+ !ntlm_hmac_md5_final(out, &out_len, ntlm)) {
ntlm_client_set_errmsg(ntlm, "failed to create HMAC-MD5");
return false;
}
- assert(out_len == 16);
+ NTLM_ASSERT(ntlm, out_len == 16);
return true;
}
@@ -1127,19 +1152,15 @@ static bool generate_lm2_response(ntlm_client *ntlm,
local_nonce = ntlm_htonll(ntlm->nonce);
- if (!ntlm_hmac_ctx_reset(ntlm->hmac_ctx) ||
- !ntlm_hmac_md5_init(ntlm->hmac_ctx,
- ntlm2_hash, NTLM_NTLM2_HASH_LEN) ||
- !ntlm_hmac_md5_update(ntlm->hmac_ctx,
- (const unsigned char *)&ntlm->challenge.nonce, 8) ||
- !ntlm_hmac_md5_update(ntlm->hmac_ctx,
- (const unsigned char *)&local_nonce, 8) ||
- !ntlm_hmac_md5_final(lm2_challengehash, &lm2_len, ntlm->hmac_ctx)) {
+ if (!ntlm_hmac_md5_init(ntlm, ntlm2_hash, NTLM_NTLM2_HASH_LEN) ||
+ !ntlm_hmac_md5_update(ntlm, (const unsigned char *)&ntlm->challenge.nonce, 8) ||
+ !ntlm_hmac_md5_update(ntlm, (const unsigned char *)&local_nonce, 8) ||
+ !ntlm_hmac_md5_final(lm2_challengehash, &lm2_len, ntlm)) {
ntlm_client_set_errmsg(ntlm, "failed to create HMAC-MD5");
return false;
}
- assert(lm2_len == 16);
+ NTLM_ASSERT(ntlm, lm2_len == 16);
memcpy(&ntlm->lm_response[0], lm2_challengehash, 16);
memcpy(&ntlm->lm_response[16], &local_nonce, 8);
@@ -1163,7 +1184,7 @@ static bool generate_nonce(ntlm_client *ntlm)
if (ntlm->nonce)
return true;
- if (!ntlm_random_bytes(ntlm, buf, 8))
+ if (!ntlm_random_bytes(buf, ntlm, 8))
return false;
memcpy(&ntlm->nonce, buf, sizeof(uint64_t));
@@ -1233,7 +1254,11 @@ int ntlm_client_response(
uint32_t flags = 0;
bool unicode;
- assert(out && out_len && ntlm);
+ NTLM_ASSERT_ARG(out);
+ NTLM_ASSERT_ARG(out_len);
+ NTLM_ASSERT_ARG(ntlm);
+
+ ENSURE_INITIALIZED(ntlm);
*out = NULL;
*out_len = 0;
@@ -1356,7 +1381,7 @@ int ntlm_client_response(
!write_buf(ntlm, &ntlm->response, session, session_len))
return -1;
- assert(ntlm->response.pos == ntlm->response.len);
+ NTLM_ASSERT(ntlm, ntlm->response.pos == ntlm->response.len);
ntlm->state = NTLM_STATE_COMPLETE;
@@ -1368,41 +1393,48 @@ int ntlm_client_response(
void ntlm_client_reset(ntlm_client *ntlm)
{
- ntlm_client_flags flags;
- ntlm_hmac_ctx *hmac_ctx;
- ntlm_unicode_ctx *unicode_ctx;
-
- assert(ntlm);
+ if (!ntlm)
+ return;
- free(ntlm->negotiate.buf);
- free(ntlm->challenge.target_info);
- free(ntlm->challenge.target);
- free(ntlm->challenge.target_domain);
- free(ntlm->challenge.target_domain_dns);
- free(ntlm->challenge.target_server);
- free(ntlm->challenge.target_server_dns);
- free(ntlm->response.buf);
+ ntlm->state = NTLM_STATE_NEGOTIATE;
- free(ntlm->hostname);
- free(ntlm->hostname_utf16);
- free(ntlm->hostdomain);
+ free_hostname(ntlm);
- free(ntlm->target);
- free(ntlm->target_utf16);
+ memset(&ntlm->host_version, 0, sizeof(ntlm_version));
- free(ntlm->ntlm2_response);
+ reset(ntlm->target);
+ reset(ntlm->target_utf16);
+ ntlm->target_utf16_len = 0;
free_credentials(ntlm);
- flags = ntlm->flags;
- hmac_ctx = ntlm->hmac_ctx;
- unicode_ctx = ntlm->unicode_ctx;
+ ntlm->nonce = 0;
+ ntlm->timestamp = 0;
- memset(ntlm, 0, sizeof(struct ntlm_client));
+ memset(ntlm->lm_response, 0, NTLM_LM_RESPONSE_LEN);
+ ntlm->lm_response_len = 0;
- ntlm->flags = flags;
- ntlm->hmac_ctx = hmac_ctx;
- ntlm->unicode_ctx = unicode_ctx;
+ memset(ntlm->ntlm_response, 0, NTLM_NTLM_RESPONSE_LEN);
+ ntlm->ntlm_response_len = 0;
+
+ reset(ntlm->ntlm2_response);
+ ntlm->ntlm2_response_len = 0;
+
+ reset(ntlm->negotiate.buf);
+ ntlm->negotiate.pos = 0;
+ ntlm->negotiate.len = 0;
+
+ reset(ntlm->response.buf);
+ ntlm->response.pos = 0;
+ ntlm->response.len = 0;
+
+ free(ntlm->challenge.target_info);
+ free(ntlm->challenge.target);
+ free(ntlm->challenge.target_domain);
+ free(ntlm->challenge.target_domain_dns);
+ free(ntlm->challenge.target_server);
+ free(ntlm->challenge.target_server_dns);
+ memset(&ntlm->challenge, 0, sizeof(ntlm_challenge));
}
void ntlm_client_free(ntlm_client *ntlm)
@@ -1410,10 +1442,10 @@ void ntlm_client_free(ntlm_client *ntlm)
if (!ntlm)
return;
- ntlm_client_reset(ntlm);
+ ntlm_crypt_shutdown(ntlm);
+ ntlm_unicode_shutdown(ntlm);
- ntlm_hmac_ctx_free(ntlm->hmac_ctx);
- ntlm_unicode_ctx_free(ntlm->unicode_ctx);
+ ntlm_client_reset(ntlm);
free(ntlm);
}
diff --git a/deps/ntlmclient/ntlm.h b/deps/ntlmclient/ntlm.h
index 0dad91ec0..227f5bcba 100644
--- a/deps/ntlmclient/ntlm.h
+++ b/deps/ntlmclient/ntlm.h
@@ -14,6 +14,8 @@
#include "crypt.h"
#include "compat.h"
+#define NTLM_UNUSED(x) ((void)(x))
+
#define NTLM_LM_RESPONSE_LEN 24
#define NTLM_NTLM_RESPONSE_LEN 24
#define NTLM_NTLM_HASH_LEN 16
@@ -66,9 +68,11 @@ struct ntlm_client {
ntlm_state state;
- /* crypto contexts */
- ntlm_hmac_ctx *hmac_ctx;
- ntlm_unicode_ctx *unicode_ctx;
+ /* subsystem contexts */
+ ntlm_crypt_ctx crypt_ctx;
+ ntlm_unicode_ctx unicode_ctx;
+ int crypt_initialized : 1,
+ unicode_initialized : 1;
/* error message as set by the library */
const char *errmsg;
@@ -85,24 +89,24 @@ struct ntlm_client {
char *password;
/* strings as converted to utf16 */
+ char *hostname_utf16;
char *target_utf16;
char *username_utf16;
char *username_upper_utf16;
char *userdomain_utf16;
- char *hostname_utf16;
char *password_utf16;
- /* timestamp and nonce; only for debugging */
- uint64_t nonce;
- uint64_t timestamp;
-
+ size_t hostname_utf16_len;
size_t username_utf16_len;
size_t username_upper_utf16_len;
size_t userdomain_utf16_len;
- size_t hostname_utf16_len;
size_t password_utf16_len;
size_t target_utf16_len;
+ /* timestamp and nonce; only for debugging */
+ uint64_t nonce;
+ uint64_t timestamp;
+
unsigned char lm_response[NTLM_LM_RESPONSE_LEN];
size_t lm_response_len;
diff --git a/deps/ntlmclient/ntlmclient.h b/deps/ntlmclient/ntlmclient.h
index d109a5c89..bf57b17c6 100644
--- a/deps/ntlmclient/ntlmclient.h
+++ b/deps/ntlmclient/ntlmclient.h
@@ -15,13 +15,26 @@
extern "C" {
#endif
-#define NTLM_CLIENT_VERSION "0.0.1"
+#define NTLM_CLIENT_VERSION "0.9.0"
#define NTLM_CLIENT_VERSION_MAJOR 0
-#define NTLM_CLIENT_VERSION_MINOR 0
-#define NTLM_CLIENT_VERSION_TEENY 1
+#define NTLM_CLIENT_VERSION_MINOR 9
+#define NTLM_CLIENT_VERSION_TEENY 0
typedef struct ntlm_client ntlm_client;
+typedef enum {
+ /**
+ * An error occurred; more details are available by querying
+ * `ntlm_client_errmsg`.
+ */
+ NTLM_CLIENT_ERROR = -1,
+
+ /**
+ * The input provided to the function is missing or invalid.
+ */
+ NTLM_CLIENT_ERROR_INVALID_INPUT = -2,
+} ntlm_error_code;
+
/*
* Flags for initializing the `ntlm_client` context. A combination of
* these flags can be provided to `ntlm_client_init`.
diff --git a/deps/ntlmclient/unicode.h b/deps/ntlmclient/unicode.h
index e3b17bcf7..b7c63f2ed 100644
--- a/deps/ntlmclient/unicode.h
+++ b/deps/ntlmclient/unicode.h
@@ -11,26 +11,32 @@
#include "compat.h"
+#ifdef UNICODE_ICONV
+# include "unicode_iconv.h"
+#elif UNICODE_BUILTIN
+# include "unicode_builtin.h"
+#endif
+
#define NTLM_UNICODE_MAX_LEN 2048
typedef struct ntlm_unicode_ctx ntlm_unicode_ctx;
-extern ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm);
+extern bool ntlm_unicode_init(ntlm_client *ntlm);
bool ntlm_unicode_utf8_to_16(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *ntlm,
const char *string,
size_t string_len);
bool ntlm_unicode_utf16_to_8(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *ntlm,
const char *string,
size_t string_len);
-extern void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx);
+extern void ntlm_unicode_shutdown(ntlm_client *ntlm);
#endif /* PRIVATE_UNICODE_H__ */
diff --git a/deps/ntlmclient/unicode_builtin.c b/deps/ntlmclient/unicode_builtin.c
index e1856cca9..e2ee0abf7 100644
--- a/deps/ntlmclient/unicode_builtin.c
+++ b/deps/ntlmclient/unicode_builtin.c
@@ -13,10 +13,6 @@
#include "unicode.h"
#include "compat.h"
-struct ntlm_unicode_ctx {
- ntlm_client *ntlm;
-};
-
typedef unsigned int UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
@@ -281,15 +277,10 @@ static ConversionResult ConvertUTF8toUTF16 (
}
-ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm)
+bool ntlm_unicode_init(ntlm_client *ntlm)
{
- ntlm_unicode_ctx *ctx;
-
- if ((ctx = malloc(sizeof(ntlm_unicode_ctx))) == NULL)
- return NULL;
-
- ctx->ntlm = ntlm;
- return ctx;
+ NTLM_UNUSED(ntlm);
+ return true;
}
typedef enum {
@@ -300,7 +291,7 @@ typedef enum {
static inline bool unicode_builtin_encoding_convert(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *ntlm,
const char *string,
size_t string_len,
unicode_builtin_encoding_direction direction)
@@ -332,7 +323,7 @@ static inline bool unicode_builtin_encoding_convert(
out_size = (out_size + 7) & ~7;
if ((out = malloc(out_size)) == NULL) {
- ntlm_client_set_errmsg(ctx->ntlm, "out of memory");
+ ntlm_client_set_errmsg(ntlm, "out of memory");
return false;
}
@@ -358,17 +349,17 @@ static inline bool unicode_builtin_encoding_convert(
success = true;
goto done;
case sourceExhausted:
- ntlm_client_set_errmsg(ctx->ntlm,
+ ntlm_client_set_errmsg(ntlm,
"invalid unicode string; trailing data remains");
goto done;
case targetExhausted:
break;
case sourceIllegal:
- ntlm_client_set_errmsg(ctx->ntlm,
+ ntlm_client_set_errmsg(ntlm,
"invalid unicode string; trailing data remains");
goto done;
default:
- ntlm_client_set_errmsg(ctx->ntlm,
+ ntlm_client_set_errmsg(ntlm,
"unknown unicode conversion failure");
goto done;
}
@@ -377,13 +368,12 @@ static inline bool unicode_builtin_encoding_convert(
out_size = ((((out_size << 1) - (out_size >> 1)) + 7) & ~7);
if (out_size > NTLM_UNICODE_MAX_LEN) {
- ntlm_client_set_errmsg(ctx->ntlm,
- "unicode conversion too large");
+ ntlm_client_set_errmsg(ntlm, "unicode conversion too large");
goto done;
}
if ((new_out = realloc(out, out_size)) == NULL) {
- ntlm_client_set_errmsg(ctx->ntlm, "out of memory");
+ ntlm_client_set_errmsg(ntlm, "out of memory");
goto done;
}
@@ -419,27 +409,26 @@ done:
bool ntlm_unicode_utf8_to_16(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *client,
const char *string,
size_t string_len)
{
return unicode_builtin_encoding_convert(converted, converted_len,
- ctx, string, string_len, unicode_builtin_utf8_to_16);
+ client, string, string_len, unicode_builtin_utf8_to_16);
}
bool ntlm_unicode_utf16_to_8(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *client,
const char *string,
size_t string_len)
{
return unicode_builtin_encoding_convert(converted, converted_len,
- ctx, string, string_len, unicode_builtin_utf16_to_8);
+ client, string, string_len, unicode_builtin_utf16_to_8);
}
-void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx)
+void ntlm_unicode_shutdown(ntlm_client *ntlm)
{
- if (ctx)
- free(ctx);
+ NTLM_UNUSED(ntlm);
}
diff --git a/deps/ntlmclient/unicode_builtin.h b/deps/ntlmclient/unicode_builtin.h
new file mode 100644
index 000000000..eabec40bf
--- /dev/null
+++ b/deps/ntlmclient/unicode_builtin.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Edward Thomson. All rights reserved.
+ *
+ * This file is part of ntlmclient, distributed under the MIT license.
+ * For full terms and copyright information, and for third-party
+ * copyright information, see the included LICENSE.txt file.
+ */
+
+#ifndef PRIVATE_UNICODE_BUILTIN_H__
+#define PRIVATE_UNICODE_BUILTIN_H__
+
+#include <locale.h>
+#include <iconv.h>
+
+#include "ntlmclient.h"
+
+struct ntlm_unicode_ctx {
+};
+
+#endif /* PRIVATE_UNICODE_BUILTIN_H__ */
diff --git a/deps/ntlmclient/unicode_iconv.c b/deps/ntlmclient/unicode_iconv.c
index d1fe07e26..e14da21f5 100644
--- a/deps/ntlmclient/unicode_iconv.c
+++ b/deps/ntlmclient/unicode_iconv.c
@@ -16,43 +16,23 @@
#include "ntlm.h"
#include "compat.h"
-struct ntlm_unicode_ctx {
- ntlm_client *ntlm;
- iconv_t utf8_to_16;
- iconv_t utf16_to_8;
-};
-
-ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm)
-{
- ntlm_unicode_ctx *ctx;
-
- if ((ctx = calloc(1, sizeof(ntlm_unicode_ctx))) == NULL)
- return NULL;
-
- ctx->ntlm = ntlm;
- ctx->utf8_to_16 = (iconv_t)-1;
- ctx->utf16_to_8 = (iconv_t)-1;
-
- return ctx;
-}
-
typedef enum {
unicode_iconv_utf8_to_16,
unicode_iconv_utf16_to_8
} unicode_iconv_encoding_direction;
-static inline bool unicode_iconv_init(ntlm_unicode_ctx *ctx)
+bool ntlm_unicode_init(ntlm_client *ntlm)
{
- if (ctx->utf8_to_16 != (iconv_t)-1 || ctx->utf16_to_8 != (iconv_t)-1)
- return true;
+ ntlm->unicode_ctx.utf8_to_16 = iconv_open("UTF-16LE", "UTF-8");
+ ntlm->unicode_ctx.utf16_to_8 = iconv_open("UTF-8", "UTF-16LE");
- if ((ctx->utf8_to_16 = iconv_open("UTF-16LE", "UTF-8")) == (iconv_t)-1 ||
- (ctx->utf16_to_8 = iconv_open("UTF-8", "UTF-16LE")) == (iconv_t)-1) {
+ if (ntlm->unicode_ctx.utf8_to_16 == (iconv_t)-1 ||
+ ntlm->unicode_ctx.utf16_to_8 == (iconv_t)-1) {
if (errno == EINVAL)
- ntlm_client_set_errmsg(ctx->ntlm,
+ ntlm_client_set_errmsg(ntlm,
"iconv does not support UTF8 <-> UTF16 conversion");
else
- ntlm_client_set_errmsg(ctx->ntlm, strerror(errno));
+ ntlm_client_set_errmsg(ntlm, strerror(errno));
return false;
}
@@ -63,7 +43,7 @@ static inline bool unicode_iconv_init(ntlm_unicode_ctx *ctx)
static inline bool unicode_iconv_encoding_convert(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *ntlm,
const char *string,
size_t string_len,
unicode_iconv_encoding_direction direction)
@@ -75,9 +55,6 @@ static inline bool unicode_iconv_encoding_convert(
*converted = NULL;
*converted_len = 0;
- if (!unicode_iconv_init(ctx))
- return false;
-
/*
* When translating UTF8 to UTF16, these strings are only used
* internally, and we obey the given length, so we can simply
@@ -86,11 +63,11 @@ static inline bool unicode_iconv_encoding_convert(
* terminate and expect an extra byte for UTF8, two for UTF16.
*/
if (direction == unicode_iconv_utf8_to_16) {
- converter = ctx->utf8_to_16;
+ converter = ntlm->unicode_ctx.utf8_to_16;
out_size = (string_len * 2) + 2;
nul_size = 2;
} else {
- converter = ctx->utf16_to_8;
+ converter = ntlm->unicode_ctx.utf16_to_8;
out_size = (string_len / 2) + 1;
nul_size = 1;
}
@@ -99,7 +76,7 @@ static inline bool unicode_iconv_encoding_convert(
out_size = (out_size + 7) & ~7;
if ((out = malloc(out_size)) == NULL) {
- ntlm_client_set_errmsg(ctx->ntlm, "out of memory");
+ ntlm_client_set_errmsg(ntlm, "out of memory");
return false;
}
@@ -117,7 +94,7 @@ static inline bool unicode_iconv_encoding_convert(
break;
if (ret == (size_t)-1 && errno != E2BIG) {
- ntlm_client_set_errmsg(ctx->ntlm, strerror(errno));
+ ntlm_client_set_errmsg(ntlm, strerror(errno));
goto on_error;
}
@@ -125,13 +102,12 @@ static inline bool unicode_iconv_encoding_convert(
out_size = ((((out_size << 1) - (out_size >> 1)) + 7) & ~7);
if (out_size > NTLM_UNICODE_MAX_LEN) {
- ntlm_client_set_errmsg(ctx->ntlm,
- "unicode conversion too large");
+ ntlm_client_set_errmsg(ntlm, "unicode conversion too large");
goto on_error;
}
if ((new_out = realloc(out, out_size)) == NULL) {
- ntlm_client_set_errmsg(ctx->ntlm, "out of memory");
+ ntlm_client_set_errmsg(ntlm, "out of memory");
goto on_error;
}
@@ -139,7 +115,7 @@ static inline bool unicode_iconv_encoding_convert(
}
if (in_start_len != 0) {
- ntlm_client_set_errmsg(ctx->ntlm,
+ ntlm_client_set_errmsg(ntlm,
"invalid unicode string; trailing data remains");
goto on_error;
}
@@ -165,37 +141,37 @@ on_error:
bool ntlm_unicode_utf8_to_16(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *ntlm,
const char *string,
size_t string_len)
{
return unicode_iconv_encoding_convert(
- converted, converted_len, ctx, string, string_len,
+ converted, converted_len, ntlm, string, string_len,
unicode_iconv_utf8_to_16);
}
bool ntlm_unicode_utf16_to_8(
char **converted,
size_t *converted_len,
- ntlm_unicode_ctx *ctx,
+ ntlm_client *ntlm,
const char *string,
size_t string_len)
{
return unicode_iconv_encoding_convert(
- converted, converted_len, ctx, string, string_len,
+ converted, converted_len, ntlm, string, string_len,
unicode_iconv_utf16_to_8);
}
-void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx)
+void ntlm_unicode_shutdown(ntlm_client *ntlm)
{
- if (!ctx)
- return;
-
- if (ctx->utf16_to_8 != (iconv_t)-1)
- iconv_close(ctx->utf16_to_8);
+ if (ntlm->unicode_ctx.utf16_to_8 != (iconv_t)0 &&
+ ntlm->unicode_ctx.utf16_to_8 != (iconv_t)-1)
+ iconv_close(ntlm->unicode_ctx.utf16_to_8);
- if (ctx->utf8_to_16 != (iconv_t)-1)
- iconv_close(ctx->utf8_to_16);
+ if (ntlm->unicode_ctx.utf8_to_16 != (iconv_t)0 &&
+ ntlm->unicode_ctx.utf8_to_16 != (iconv_t)-1)
+ iconv_close(ntlm->unicode_ctx.utf8_to_16);
- free(ctx);
+ ntlm->unicode_ctx.utf8_to_16 = (iconv_t)-1;
+ ntlm->unicode_ctx.utf16_to_8 = (iconv_t)-1;
}
diff --git a/deps/ntlmclient/unicode_iconv.h b/deps/ntlmclient/unicode_iconv.h
new file mode 100644
index 000000000..87a96a67d
--- /dev/null
+++ b/deps/ntlmclient/unicode_iconv.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) Edward Thomson. All rights reserved.
+ *
+ * This file is part of ntlmclient, distributed under the MIT license.
+ * For full terms and copyright information, and for third-party
+ * copyright information, see the included LICENSE.txt file.
+ */
+
+#ifndef PRIVATE_UNICODE_ICONV_H__
+#define PRIVATE_UNICODE_ICONV_H__
+
+#include <locale.h>
+#include <iconv.h>
+
+#include "ntlmclient.h"
+
+struct ntlm_unicode_ctx {
+ iconv_t utf8_to_16;
+ iconv_t utf16_to_8;
+};
+
+#endif /* PRIVATE_UNICODE_ICONV_H__ */
diff --git a/examples/general.c b/examples/general.c
index 5da2b6f6f..2127ec0e1 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -708,7 +708,7 @@ static void reference_listing(git_repository *repo)
/**
* ### Config Files
*
- * The [config API][config] allows you to list and updatee config values
+ * The [config API][config] allows you to list and update config values
* in any of the accessible config file locations (system, global, local).
*
* [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
diff --git a/include/git2/attr.h b/include/git2/attr.h
index a3ab5a7a2..62c2ed6e7 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -130,9 +130,32 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
*
* Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
* from a `.gitattributes` file in the repository at the HEAD revision.
+ *
+ * Passing the `GIT_ATTR_CHECK_INCLUDE_COMMIT` flag will use attributes
+ * from a `.gitattributes` file in a specific commit.
*/
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
+#define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4)
+
+/**
+* An options structure for querying attributes.
+*/
+typedef struct {
+ unsigned int version;
+
+ /** A combination of GIT_ATTR_CHECK flags */
+ unsigned int flags;
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified.
+ */
+ git_oid *commit_id;
+} git_attr_options;
+
+#define GIT_ATTR_OPTIONS_VERSION 1
+#define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION}
/**
* Look up the value of one git attribute for path.
@@ -157,6 +180,28 @@ GIT_EXTERN(int) git_attr_get(
const char *name);
/**
+ * Look up the value of one git attribute for path with extended options.
+ *
+ * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
+ * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
+ * use the string value for attributes set to a value. You
+ * should NOT modify or free this value.
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path The path to check for attributes. Relative paths are
+ * interpreted relative to the repo root. The file does
+ * not have to exist, but if it does not, then it will be
+ * treated as a plain file (not a directory).
+ * @param name The name of the attribute to look up.
+ */
+GIT_EXTERN(int) git_attr_get_ext(
+ const char **value_out,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ const char *name);
+
+/**
* Look up a list of git attributes for path.
*
* Use this if you have a known list of attributes that you want to
@@ -194,6 +239,30 @@ GIT_EXTERN(int) git_attr_get_many(
const char **names);
/**
+ * Look up a list of git attributes for path with extended options.
+ *
+ * @param values_out An array of num_attr entries that will have string
+ * pointers written into it for the values of the attributes.
+ * You should not modify or free the values that are written
+ * into this array (although of course, you should free the
+ * array itself if you allocated it).
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path The path inside the repo to check attributes. This
+ * does not have to exist, but if it does not, then
+ * it will be treated as a plain file (i.e. not a directory).
+ * @param num_attr The number of attributes being looked up
+ * @param names An array of num_attr strings containing attribute names.
+ */
+GIT_EXTERN(int) git_attr_get_many_ext(
+ const char **values_out,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ size_t num_attr,
+ const char **names);
+
+/**
* The callback used with git_attr_foreach.
*
* This callback will be invoked only once per attribute name, even if there
@@ -232,6 +301,26 @@ GIT_EXTERN(int) git_attr_foreach(
void *payload);
/**
+ * Loop over all the git attributes for a path with extended options.
+ *
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path Path inside the repo to check attributes. This does not have
+ * to exist, but if it does not, then it will be treated as a
+ * plain file (i.e. not a directory).
+ * @param callback Function to invoke on each attribute name and value.
+ * See git_attr_foreach_cb.
+ * @param payload Passed on as extra parameter to callback function.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ git_attr_foreach_cb callback,
+ void *payload);
+
+/**
* Flush the gitattributes cache.
*
* Call this if you have reason to believe that the attributes files on
diff --git a/include/git2/blame.h b/include/git2/blame.h
index f42c81552..d193ce14e 100644
--- a/include/git2/blame.h
+++ b/include/git2/blame.h
@@ -26,27 +26,52 @@ GIT_BEGIN_DECL
typedef enum {
/** Normal blame, the default */
GIT_BLAME_NORMAL = 0,
- /** Track lines that have moved within a file (like `git blame -M`).
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have moved within a file (like `git blame -M`).
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0),
- /** Track lines that have moved across files in the same commit (like `git blame -C`).
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have moved across files in the same commit
+ * (like `git blame -C`).
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1),
- /** Track lines that have been copied from another file that exists in the
- * same commit (like `git blame -CC`). Implies SAME_FILE.
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have been copied from another file that exists
+ * in the same commit (like `git blame -CC`). Implies SAME_FILE.
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2),
- /** Track lines that have been copied from another file that exists in *any*
- * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have been copied from another file that exists in
+ * *any* commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
- /** Restrict the search of commits to those reachable following only the
- * first parents. */
+
+ /**
+ * Restrict the search of commits to those reachable following only
+ * the first parents.
+ */
GIT_BLAME_FIRST_PARENT = (1<<4),
- /** Use mailmap file to map author and committer names and email addresses
- * to canonical real names and email addresses. The mailmap will be read
- * from the working directory, or HEAD in a bare repository. */
+
+ /**
+ * Use mailmap file to map author and committer names and email
+ * addresses to canonical real names and email addresses. The
+ * mailmap will be read from the working directory, or HEAD in a
+ * bare repository.
+ */
GIT_BLAME_USE_MAILMAP = (1<<5),
+
/** Ignore whitespace differences */
GIT_BLAME_IGNORE_WHITESPACE = (1<<6),
} git_blame_flag_t;
@@ -63,25 +88,33 @@ typedef struct git_blame_options {
/** A combination of `git_blame_flag_t` */
uint32_t flags;
- /** The lower bound on the number of alphanumeric
- * characters that must be detected as moving/copying within a file for it to
- * associate those lines with the parent commit. The default value is 20.
- * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
- * flags are specified.
+
+ /**
+ * The lower bound on the number of alphanumeric characters that
+ * must be detected as moving/copying within a file for it to
+ * associate those lines with the parent commit. The default value
+ * is 20.
+ *
+ * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
+ * flags are specified.
*/
uint16_t min_match_characters;
+
/** The id of the newest commit to consider. The default is HEAD. */
git_oid newest_commit;
+
/**
* The id of the oldest commit to consider.
* The default is the first commit encountered with a NULL parent.
*/
git_oid oldest_commit;
+
/**
* The first line in the file to blame.
* The default is 1 (line numbers start with 1).
*/
size_t min_line;
+
/**
* The last line in the file to blame.
* The default is the last line of the file.
@@ -108,41 +141,59 @@ GIT_EXTERN(int) git_blame_options_init(
/**
* Structure that represents a blame hunk.
- *
- * - `lines_in_hunk` is the number of lines in this hunk
- * - `final_commit_id` is the OID of the commit where this line was last
- * changed.
- * - `final_start_line_number` is the 1-based line number where this hunk
- * begins, in the final version of the file
- * - `final_signature` is the author of `final_commit_id`. If
- * `GIT_BLAME_USE_MAILMAP` has been specified, it will contain the canonical
- * real name and email address.
- * - `orig_commit_id` is the OID of the commit where this hunk was found. This
- * will usually be the same as `final_commit_id`, except when
- * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
- * - `orig_path` is the path to the file where this hunk originated, as of the
- * commit specified by `orig_commit_id`.
- * - `orig_start_line_number` is the 1-based line number where this hunk begins
- * in the file named by `orig_path` in the commit specified by
- * `orig_commit_id`.
- * - `orig_signature` is the author of `orig_commit_id`. If
- * `GIT_BLAME_USE_MAILMAP` has been specified, it will contain the canonical
- * real name and email address.
- * - `boundary` is 1 iff the hunk has been tracked to a boundary commit (the
- * root, or the commit specified in git_blame_options.oldest_commit)
*/
typedef struct git_blame_hunk {
+ /**
+ * The number of lines in this hunk.
+ */
size_t lines_in_hunk;
+ /**
+ * The OID of the commit where this line was last changed.
+ */
git_oid final_commit_id;
+
+ /**
+ * The 1-based line number where this hunk begins, in the final version
+ * of the file.
+ */
size_t final_start_line_number;
+
+ /**
+ * The author of `final_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been
+ * specified, it will contain the canonical real name and email address.
+ */
git_signature *final_signature;
+ /**
+ * The OID of the commit where this hunk was found.
+ * This will usually be the same as `final_commit_id`, except when
+ * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
+ */
git_oid orig_commit_id;
+
+ /**
+ * The path to the file where this hunk originated, as of the commit
+ * specified by `orig_commit_id`.
+ */
const char *orig_path;
+
+ /**
+ * The 1-based line number where this hunk begins in the file named by
+ * `orig_path` in the commit specified by `orig_commit_id`.
+ */
size_t orig_start_line_number;
+
+ /**
+ * The author of `orig_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been
+ * specified, it will contain the canonical real name and email address.
+ */
git_signature *orig_signature;
+ /**
+ * The 1 iff the hunk has been tracked to a boundary commit (the root,
+ * or the commit specified in git_blame_options.oldest_commit)
+ */
char boundary;
} git_blame_hunk;
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 3f6738675..fceb5c771 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -114,6 +114,12 @@ typedef enum {
* in the HEAD commit.
*/
GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD = (1 << 2),
+
+ /**
+ * When set, filters will be loaded from a `.gitattributes` file
+ * in the specified commit.
+ */
+ GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT = (1 << 3),
} git_blob_filter_flag_t;
/**
@@ -128,6 +134,12 @@ typedef struct {
/** Flags to control the filtering process, see `git_blob_filter_flag_t` above */
uint32_t flags;
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified.
+ */
+ git_oid *commit_id;
} git_blob_filter_options;
#define GIT_BLOB_FILTER_OPTIONS_VERSION 1
diff --git a/include/git2/branch.h b/include/git2/branch.h
index 0c0cc7ff7..24ea7f7d0 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -305,6 +305,19 @@ GIT_EXTERN(int) git_branch_remote_name(
GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname);
/**
+ * Retrieve the upstream merge of a local branch
+ *
+ * This will return the currently configured "branch.*.merge" for a given
+ * branch. This branch must be local.
+ *
+ * @param buf the buffer into which to write the name
+ * @param repo the repository in which to look
+ * @param refname the full name of the branch
+ * @return 0 or an error code
+ */
+ GIT_EXTERN(int) git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname);
+
+/**
* Determine whether a branch name is valid, meaning that (when prefixed
* with `refs/heads/`) that it is a valid reference name, and that any
* additional branch name restrictions are imposed (eg, it cannot start
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 3c87001bf..ca6f17aa6 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -194,18 +194,6 @@ typedef enum {
* Checkout will invoke an options notification callback (`notify_cb`) for
* certain cases - you pick which ones via `notify_flags`:
*
- * - GIT_CHECKOUT_NOTIFY_CONFLICT invokes checkout on conflicting paths.
- *
- * - GIT_CHECKOUT_NOTIFY_DIRTY notifies about "dirty" files, i.e. those that
- * do not need an update but no longer match the baseline. Core git
- * displays these files when checkout runs, but won't stop the checkout.
- *
- * - GIT_CHECKOUT_NOTIFY_UPDATED sends notification for any file changed.
- *
- * - GIT_CHECKOUT_NOTIFY_UNTRACKED notifies about untracked files.
- *
- * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files.
- *
* Returning a non-zero value from this callback will cancel the checkout.
* The non-zero return value will be propagated back and returned by the
* git_checkout_... call.
@@ -216,10 +204,32 @@ typedef enum {
*/
typedef enum {
GIT_CHECKOUT_NOTIFY_NONE = 0,
+
+ /**
+ * Invokes checkout on conflicting paths.
+ */
GIT_CHECKOUT_NOTIFY_CONFLICT = (1u << 0),
+
+ /**
+ * Notifies about "dirty" files, i.e. those that do not need an update
+ * but no longer match the baseline. Core git displays these files when
+ * checkout runs, but won't stop the checkout.
+ */
GIT_CHECKOUT_NOTIFY_DIRTY = (1u << 1),
+
+ /**
+ * Sends notification for any file changed.
+ */
GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
+
+ /**
+ * Notifies about untracked files.
+ */
GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
+
+ /**
+ * Notifies about ignored files.
+ */
GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
diff --git a/include/git2/common.h b/include/git2/common.h
index dee260e05..d278c01b6 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -91,10 +91,10 @@ GIT_BEGIN_DECL
/**
* The separator used in path list strings (ie like in the PATH
- * environment variable). A semi-colon ";" is used on Windows, and
- * a colon ":" for all other systems.
+ * environment variable). A semi-colon ";" is used on Windows and
+ * AmigaOS, and a colon ":" for all other systems.
*/
-#ifdef GIT_WIN32
+#if defined(GIT_WIN32) || defined(AMIGA)
#define GIT_PATH_LIST_SEPARATOR ';'
#else
#define GIT_PATH_LIST_SEPARATOR ':'
@@ -207,7 +207,9 @@ typedef enum {
GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS,
GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE,
GIT_OPT_GET_MWINDOW_FILE_LIMIT,
- GIT_OPT_SET_MWINDOW_FILE_LIMIT
+ GIT_OPT_SET_MWINDOW_FILE_LIMIT,
+ GIT_OPT_SET_ODB_PACKED_PRIORITY,
+ GIT_OPT_SET_ODB_LOOSE_PRIORITY
} git_libgit2_opt_t;
/**
@@ -421,6 +423,14 @@ typedef enum {
* > authentication, use expect/continue when POSTing data.
* > This option is not available on Windows.
*
+ * opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, int priority)
+ * > Override the default priority of the packed ODB backend which
+ * > is added when default backends are assigned to a repository
+ *
+ * opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, int priority)
+ * > Override the default priority of the loose ODB backend which
+ * > is added when default backends are assigned to a repository
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
diff --git a/include/git2/diff.h b/include/git2/diff.h
index c63d93dc8..9497793c3 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -241,32 +241,43 @@ typedef enum {
* Although this is called a "file", it could represent a file, a symbolic
* link, a submodule commit id, or even a tree (although that only if you
* are tracking type changes or ignored/untracked directories).
- *
- * The `id` is the `git_oid` of the item. If the entry represents an
- * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
- * then the oid will be zeroes.
- *
- * `path` is the NUL-terminated path to the entry relative to the working
- * directory of the repository.
- *
- * `size` is the size of the entry in bytes.
- *
- * `flags` is a combination of the `git_diff_flag_t` types
- *
- * `mode` is, roughly, the stat() `st_mode` value for the item. This will
- * be restricted to one of the `git_filemode_t` values.
- *
- * The `id_abbrev` represents the known length of the `id` field, when
- * converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
- * delta was created from reading a patch file, in which case it may be
- * abbreviated to something reasonable, like 7 characters.
*/
typedef struct {
+ /**
+ * The `git_oid` of the item. If the entry represents an
+ * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
+ * then the oid will be zeroes.
+ */
git_oid id;
+
+ /**
+ * The NUL-terminated path to the entry relative to the working
+ * directory of the repository.
+ */
const char *path;
+
+ /**
+ * The size of the entry in bytes.
+ */
git_object_size_t size;
+
+ /**
+ * A combination of the `git_diff_flag_t` types
+ */
uint32_t flags;
+
+ /**
+ * Roughly, the stat() `st_mode` value for the item. This will
+ * be restricted to one of the `git_filemode_t` values.
+ */
uint16_t mode;
+
+ /**
+ * Represents the known length of the `id` field, when
+ * converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
+ * delta was created from reading a patch file, in which case it may be
+ * abbreviated to something reasonable, like 7 characters.
+ */
uint16_t id_abbrev;
} git_diff_file;
diff --git a/include/git2/filter.h b/include/git2/filter.h
index a0185ee88..044c3b870 100644
--- a/include/git2/filter.h
+++ b/include/git2/filter.h
@@ -49,9 +49,34 @@ typedef enum {
/** Load attributes from `.gitattributes` in the root of HEAD */
GIT_FILTER_ATTRIBUTES_FROM_HEAD = (1u << 2),
+
+ /**
+ * Load attributes from `.gitattributes` in a given commit.
+ * This can only be specified in a `git_filter_options`.
+ */
+ GIT_FILTER_ATTRIBUTES_FROM_COMMIT = (1u << 3),
} git_filter_flag_t;
/**
+ * Filtering options
+ */
+typedef struct {
+ unsigned int version;
+
+ /** See `git_filter_flag_t` above */
+ uint32_t flags;
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified.
+ */
+ git_oid *commit_id;
+} git_filter_options;
+
+ #define GIT_FILTER_OPTIONS_VERSION 1
+ #define GIT_FILTER_OPTIONS_INIT {GIT_FILTER_OPTIONS_VERSION}
+
+/**
* A filter that can transform file data
*
* This represents a filter that can be used to transform or even replace
@@ -104,6 +129,29 @@ GIT_EXTERN(int) git_filter_list_load(
uint32_t flags);
/**
+ * Load the filter list for a given path.
+ *
+ * This will return 0 (success) but set the output git_filter_list to NULL
+ * if no filters are requested for the given file.
+ *
+ * @param filters Output newly created git_filter_list (or NULL)
+ * @param repo Repository object that contains `path`
+ * @param blob The blob to which the filter will be applied (if known)
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @param opts The `git_filter_options` to use when loading filters
+ * @return 0 on success (which could still return NULL if no filters are
+ * needed for the requested file), <0 on error
+ */
+GIT_EXTERN(int) git_filter_list_load_ext(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob,
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_options *opts);
+
+/**
* Query the filter list to see if a given filter (by name) will run.
* The built-in filters "crlf" and "ident" can be queried, otherwise this
* is the name of the filter specified by the filter attribute.
diff --git a/include/git2/remote.h b/include/git2/remote.h
index b82bd250b..5d7a5367d 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -253,6 +253,7 @@ GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, con
* @param repo the repository in which to perform the change
* @param remote the remote's name
* @param url the url to set
+ * @return 0, or an error code
*/
GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url);
@@ -876,8 +877,10 @@ GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *rem
* @param repo the repository in which to make the change
* @param remote the name of the remote
* @param value the new value to take.
+ * @return 0, or an error code.
*/
GIT_EXTERN(int) git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value);
+
/**
* Retrieve the ref-prune setting
*
@@ -944,7 +947,7 @@ GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name);
*
* This function must only be called after connecting.
*
- * @param out the buffern in which to store the reference name
+ * @param out the buffer in which to store the reference name
* @param remote the remote
* @return 0, GIT_ENOTFOUND if the remote does not have any references
* or none of them point to HEAD's commit, or an error message.
diff --git a/include/git2/status.h b/include/git2/status.h
index 9693cc478..543e3faa8 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -69,89 +69,141 @@ typedef int GIT_CALLBACK(git_status_cb)(
* With `git_status_foreach_ext`, this will control which changes get
* callbacks. With `git_status_list_new`, these will control which
* changes are included in the list.
- *
- * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
- * matches `git status --porcelain` regarding which files are
- * included and in what order.
- * - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
- * comparison, not looking at working directory changes.
- * - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
- * working directory comparison, not comparing the index to the HEAD.
*/
typedef enum {
+ /**
+ * The default. This roughly matches `git status --porcelain` regarding
+ * which files are included and in what order.
+ */
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
+
+ /**
+ * Only gives status based on HEAD to index comparison, not looking at
+ * working directory changes.
+ */
GIT_STATUS_SHOW_INDEX_ONLY = 1,
+
+ /**
+ * Only gives status based on index to working directory comparison,
+ * not comparing the index to the HEAD.
+ */
GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
} git_status_show_t;
/**
* Flags to control status callbacks
*
- * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
- * on untracked files. These will only be made if the workdir files are
- * included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
- * Again, these callbacks will only be made if the workdir files are
- * included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
- * made even on unmodified files.
- * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
- * skipped. This only applies if there are no pending typechanges to
- * the submodule (either from or to another type).
- * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
- * untracked directories should be included. Normally if an entire
- * directory is new, then just the top-level directory is included (with
- * a trailing slash on the entry name). This flag says to include all
- * of the individual files in the directory instead.
- * - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
- * should be treated as a literal path, and not as a pathspec pattern.
- * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
- * ignored directories should be included in the status. This is like
- * doing `git ls-files -o -i --exclude-standard` with core git.
- * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
- * should be processed between the head and the index and enables
- * the GIT_STATUS_INDEX_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
- * detection should be run between the index and the working directory
- * and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
- * sensitivity for the file system and forces the output to be in
- * case-sensitive order
- * - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
- * sensitivity for the file system and forces the output to be in
- * case-insensitive order
- * - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
- * should include rewritten files
- * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
- * doing a "soft" index reload (i.e. reloading the index data if the
- * file on disk has been modified outside libgit2).
- * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
- * in the index for files that are unchanged but have out of date stat
- * information in the index. It will result in less work being done on
- * subsequent calls to get status. This is mutually exclusive with the
- * NO_REFRESH option.
- *
* Calling `git_status_foreach()` is like calling the extended version
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
* and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS. Those options are bundled
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
*/
typedef enum {
+ /**
+ * Says that callbacks should be made on untracked files.
+ * These will only be made if the workdir files are included in the status
+ * "show" option.
+ */
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
+
+ /**
+ * Says that ignored files get callbacks.
+ * Again, these callbacks will only be made if the workdir files are
+ * included in the status "show" option.
+ */
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
+
+ /**
+ * Indicates that callback should be made even on unmodified files.
+ */
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
+
+ /**
+ * Indicates that submodules should be skipped.
+ * This only applies if there are no pending typechanges to the submodule
+ * (either from or to another type).
+ */
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
+
+ /**
+ * Indicates that all files in untracked directories should be included.
+ * Normally if an entire directory is new, then just the top-level
+ * directory is included (with a trailing slash on the entry name).
+ * This flag says to include all of the individual files in the directory
+ * instead.
+ */
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
+
+ /**
+ * Indicates that the given path should be treated as a literal path,
+ * and not as a pathspec pattern.
+ */
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
+
+ /**
+ * Indicates that the contents of ignored directories should be included
+ * in the status. This is like doing `git ls-files -o -i --exclude-standard`
+ * with core git.
+ */
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
+
+ /**
+ * Indicates that rename detection should be processed between the head and
+ * the index and enables the GIT_STATUS_INDEX_RENAMED as a possible status
+ * flag.
+ */
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
+
+ /**
+ * Indicates that rename detection should be run between the index and the
+ * working directory and enabled GIT_STATUS_WT_RENAMED as a possible status
+ * flag.
+ */
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
+
+ /**
+ * Overrides the native case sensitivity for the file system and forces
+ * the output to be in case-sensitive order.
+ */
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
+
+ /**
+ * Overrides the native case sensitivity for the file system and forces
+ * the output to be in case-insensitive order.
+ */
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
+
+ /**
+ * Iindicates that rename detection should include rewritten files.
+ */
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
+
+ /**
+ * Bypasses the default status behavior of doing a "soft" index reload
+ * (i.e. reloading the index data if the file on disk has been modified
+ * outside libgit2).
+ */
GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
+
+ /**
+ * Tells libgit2 to refresh the stat cache in the index for files that are
+ * unchanged but have out of date stat einformation in the index.
+ * It will result in less work being done on subsequent calls to get status.
+ * This is mutually exclusive with the NO_REFRESH option.
+ */
GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
+
+ /**
+ * Normally files that cannot be opened or read are ignored as
+ * these are often transient files; this option will return
+ * unreadable files as `GIT_STATUS_WT_UNREADABLE`.
+ */
GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14),
+
+ /**
+ * Unreadable files will be detected and given the status
+ * untracked instead of unreadable.
+ */
GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15),
} git_status_opt_t;
@@ -168,7 +220,10 @@ typedef enum {
*
*/
typedef struct {
- unsigned int version; /**< The version */
+ /**
+ * The struct version; pass `GIT_STATUS_OPTIONS_VERSION`.
+ */
+ unsigned int version;
/**
* The `show` value is one of the `git_status_show_t` constants that
@@ -177,21 +232,22 @@ typedef struct {
git_status_show_t show;
/**
- * The `flags` value is an OR'ed combination of the `git_status_opt_t`
- * values above.
+ * The `flags` value is an OR'ed combination of the
+ * `git_status_opt_t` values above.
*/
unsigned int flags;
/**
* The `pathspec` is an array of path patterns to match (using
- * fnmatch-style matching), or just an array of paths to match exactly if
- * `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified in the flags.
+ * fnmatch-style matching), or just an array of paths to match
+ * exactly if `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified
+ * in the flags.
*/
git_strarray pathspec;
/**
- * The `baseline` is the tree to be used for comparison to the working directory
- * and index; defaults to HEAD.
+ * The `baseline` is the tree to be used for comparison to the
+ * working directory and index; defaults to HEAD.
*/
git_tree *baseline;
} git_status_options;
diff --git a/include/git2/worktree.h b/include/git2/worktree.h
index 049511da1..23084d8cd 100644
--- a/include/git2/worktree.h
+++ b/include/git2/worktree.h
@@ -198,6 +198,7 @@ typedef enum {
typedef struct git_worktree_prune_options {
unsigned int version;
+ /** A combination of `git_worktree_prune_t` */
uint32_t flags;
} git_worktree_prune_options;
diff --git a/script/valgrind.supp b/script/valgrind.supp
index d938aa9c9..8c4549f62 100644
--- a/script/valgrind.supp
+++ b/script/valgrind.supp
@@ -42,6 +42,38 @@
}
{
+ ignore-openssl-init-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_stream_global_init
+ ...
+}
+
+{
+ ignore-openssl-legacy-init-leak
+ Memcheck:Leak
+ ...
+ fun:OPENSSL_init_ssl__legacy
+ ...
+}
+
+{
+ ignore-openssl-malloc-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_malloc
+ ...
+}
+
+{
+ ignore-openssl-realloc-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_realloc
+ ...
+}
+
+{
ignore-glibc-getaddrinfo-cache
Memcheck:Leak
...
@@ -65,6 +97,22 @@
}
{
+ ignore-libssh2-session-create
+ Memcheck:Leak
+ ...
+ fun:_git_ssh_session_create
+ ...
+}
+
+{
+ ignore-libssh2-setup-conn
+ Memcheck:Leak
+ ...
+ fun:_git_ssh_setup_conn
+ ...
+}
+
+{
ignore-libssh2-gcrypt-control-leak
Memcheck:Leak
...
@@ -178,3 +226,19 @@
obj:*libcrypto.so*
...
}
+
+{
+ ignore-dlopen-leak
+ Memcheck:Leak
+ ...
+ fun:dlopen
+ ...
+}
+
+{
+ ignore-dlopen-leak
+ Memcheck:Leak
+ ...
+ fun:_dlerror_run
+ ...
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8d1559551..45dec2796 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,6 +11,11 @@ IF(DEBUG_STRICT_ALLOC)
ENDIF()
ADD_FEATURE_INFO(debugalloc GIT_DEBUG_STRICT_ALLOC "debug strict allocators")
+IF(DEBUG_STRICT_OPEN)
+ SET(GIT_DEBUG_STRICT_OPEN 1)
+ENDIF()
+ADD_FEATURE_INFO(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open")
+
INCLUDE(PkgBuildConfig)
INCLUDE(SanitizeBool)
@@ -38,10 +43,10 @@ IF(ENABLE_TRACE)
ENDIF()
ADD_FEATURE_INFO(tracing GIT_TRACE "tracing support")
-CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
IF (HAVE_FUTIMENS)
SET(GIT_USE_FUTIMENS 1)
ENDIF ()
+ADD_FEATURE_INFO(futimens GIT_USE_FUTIMENS "futimens support")
CHECK_PROTOTYPE_DEFINITION(qsort_r
"void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
@@ -390,7 +395,7 @@ if(SONAME)
set_target_properties(git2 PROPERTIES VERSION ${libgit2_VERSION})
set_target_properties(git2 PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
if(LIBGIT2_FILENAME)
- target_compile_definitions(git2internal PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
+ target_compile_definitions(git2 PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
set_target_properties(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
elseif(DEFINED LIBGIT2_PREFIX)
set_target_properties(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
diff --git a/src/array.h b/src/array.h
index 03537e796..e649d845b 100644
--- a/src/array.h
+++ b/src/array.h
@@ -70,13 +70,13 @@ on_oom:
#define git_array_alloc(a) \
(((a).size >= (a).asize) ? \
git_array_grow(&(a), sizeof(*(a).ptr)) : \
- ((a).ptr ? &(a).ptr[(a).size++] : NULL))
+ ((a).ptr ? &(a).ptr[(a).size++] : (void *)NULL))
-#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
+#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL)
-#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : NULL)
+#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : (void *)NULL)
-#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
+#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : (void *)NULL)
#define git_array_size(a) (a).size
diff --git a/src/attr.c b/src/attr.c
index cab5e8b97..03b720c5a 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -36,16 +36,16 @@ git_attr_value_t git_attr_value(const char *attr)
static int collect_attr_files(
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *path,
git_vector *files);
static void release_attr_files(git_vector *files);
-int git_attr_get(
+int git_attr_get_ext(
const char **value,
git_repository *repo,
- uint32_t flags,
+ git_attr_options *opts,
const char *pathname,
const char *name)
{
@@ -61,6 +61,7 @@ int git_attr_get(
GIT_ASSERT_ARG(value);
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(name);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
*value = NULL;
@@ -70,7 +71,7 @@ int git_attr_get(
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
goto cleanup;
memset(&attr, 0, sizeof(attr));
@@ -97,6 +98,20 @@ cleanup:
return error;
}
+int git_attr_get(
+ const char **value,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ const char *name)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_get_ext(value, repo, &opts, pathname, name);
+}
+
typedef struct {
git_attr_name name;
@@ -107,7 +122,7 @@ int git_attr_get_many_with_session(
const char **values,
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *pathname,
size_t num_attr,
const char **names)
@@ -129,6 +144,7 @@ int git_attr_get_many_with_session(
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(pathname);
GIT_ASSERT_ARG(names);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
@@ -136,7 +152,7 @@ int git_attr_get_many_with_session(
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
goto cleanup;
info = git__calloc(num_attr, sizeof(attr_get_many_info));
@@ -190,8 +206,24 @@ int git_attr_get_many(
size_t num_attr,
const char **names)
{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
return git_attr_get_many_with_session(
- values, repo, NULL, flags, pathname, num_attr, names);
+ values, repo, NULL, &opts, pathname, num_attr, names);
+}
+
+int git_attr_get_many_ext(
+ const char **values,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ return git_attr_get_many_with_session(
+ values, repo, NULL, opts, pathname, num_attr, names);
}
int git_attr_foreach(
@@ -201,6 +233,20 @@ int git_attr_foreach(
int (*callback)(const char *name, const char *value, void *payload),
void *payload)
{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
+}
+
+int git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ int (*callback)(const char *name, const char *value, void *payload),
+ void *payload)
+{
int error;
git_attr_path path;
git_vector files = GIT_VECTOR_INIT;
@@ -213,6 +259,7 @@ int git_attr_foreach(
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(callback);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
@@ -220,7 +267,7 @@ int git_attr_foreach(
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 ||
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
(error = git_strmap_new(&seen)) < 0)
goto cleanup;
@@ -253,26 +300,43 @@ cleanup:
return error;
}
-static int preload_attr_file(
+static int preload_attr_source(
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *file,
- bool allow_macros)
+ git_attr_file_source *source)
{
int error;
git_attr_file *preload = NULL;
- if (!file)
+ if (!source)
return 0;
- if (!(error = git_attr_cache__get(&preload, repo, attr_session, source, base, file,
- git_attr_file__parse_buffer, allow_macros)))
+
+ error = git_attr_cache__get(&preload, repo, attr_session, source,
+ git_attr_file__parse_buffer, true);
+
+ if (!error)
git_attr_file__free(preload);
return error;
}
+GIT_INLINE(int) preload_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
+
+ if (!filename)
+ return 0;
+
+ source.base = base;
+ source.filename = filename;
+
+ return preload_attr_source(repo, attr_session, &source);
+}
+
static int system_attr_file(
git_buf *out,
git_attr_session *attr_session)
@@ -314,9 +378,12 @@ static int system_attr_file(
static int attr_setup(
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags)
+ git_attr_options *opts)
{
- git_buf path = GIT_BUF_INIT;
+ git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT;
+ git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
git_index *idx = NULL;
const char *workdir;
int error = 0;
@@ -332,45 +399,51 @@ static int attr_setup(
* definitions will be available for later file parsing.
*/
- if ((error = system_attr_file(&path, attr_session)) < 0 ||
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- NULL, path.ptr, true)) < 0) {
+ if ((error = system_attr_file(&system, attr_session)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) {
if (error != GIT_ENOTFOUND)
goto out;
+
+ error = 0;
}
- if ((error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- NULL, git_repository_attr_cache(repo)->cfg_attr_file, true)) < 0)
+ if ((error = preload_attr_file(repo, attr_session, NULL,
+ git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
goto out;
- git_buf_clear(&path); /* git_repository_item_path expects an empty buffer, because it uses git_buf_set */
- if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- path.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) {
+ if ((error = git_repository_item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
if (error != GIT_ENOTFOUND)
goto out;
+
+ error = 0;
}
if ((workdir = git_repository_workdir(repo)) != NULL &&
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- workdir, GIT_ATTR_FILE, true)) < 0)
+ (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0)
goto out;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_INDEX,
- NULL, GIT_ATTR_FILE, true)) < 0)
+ (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
goto out;
- if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 &&
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_HEAD,
- NULL, GIT_ATTR_FILE, true)) < 0)
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
+ (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
goto out;
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
+ commit_source.commit_id = opts->commit_id;
+
+ if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
+ goto out;
+ }
+
if (attr_session)
attr_session->init_setup = 1;
out:
- git_buf_dispose(&path);
+ git_buf_dispose(&system);
+ git_buf_dispose(&info);
return error;
}
@@ -415,56 +488,60 @@ int git_attr_add_macro(
typedef struct {
git_repository *repo;
git_attr_session *attr_session;
- uint32_t flags;
+ git_attr_options *opts;
const char *workdir;
git_index *index;
git_vector *files;
} attr_walk_up_info;
static int attr_decide_sources(
- uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
+ uint32_t flags,
+ bool has_wd,
+ bool has_index,
+ git_attr_file_source_t *srcs)
{
int count = 0;
switch (flags & 0x03) {
case GIT_ATTR_CHECK_FILE_THEN_INDEX:
if (has_wd)
- srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
break;
case GIT_ATTR_CHECK_INDEX_THEN_FILE:
if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
if (has_wd)
- srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
break;
case GIT_ATTR_CHECK_INDEX_ONLY:
if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
break;
}
- if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
- srcs[count++] = GIT_ATTR_FILE__FROM_HEAD;
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 ||
+ (flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
return count;
}
-static int push_attr_file(
+static int push_attr_source(
git_repository *repo,
git_attr_session *attr_session,
git_vector *list,
- git_attr_file_source source,
- const char *base,
- const char *filename,
+ git_attr_file_source *source,
bool allow_macros)
{
int error = 0;
git_attr_file *file = NULL;
error = git_attr_cache__get(&file, repo, attr_session,
- source, base, filename, git_attr_file__parse_buffer, allow_macros);
+ source,
+ git_attr_file__parse_buffer,
+ allow_macros);
if (error < 0)
return error;
@@ -477,20 +554,40 @@ static int push_attr_file(
return error;
}
+GIT_INLINE(int) push_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_vector *list,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
+ return push_attr_source(repo, attr_session, list, &source, true);
+}
+
static int push_one_attr(void *ref, const char *path)
{
attr_walk_up_info *info = (attr_walk_up_info *)ref;
- git_attr_file_source src[GIT_ATTR_FILE_NUM_SOURCES];
+ git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES];
int error = 0, n_src, i;
bool allow_macros;
- n_src = attr_decide_sources(
- info->flags, info->workdir != NULL, info->index != NULL, src);
+ n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
+ info->workdir != NULL,
+ info->index != NULL,
+ src);
+
allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
- for (i = 0; !error && i < n_src; ++i)
- error = push_attr_file(info->repo, info->attr_session, info->files,
- src[i], path, GIT_ATTR_FILE, allow_macros);
+ for (i = 0; !error && i < n_src; ++i) {
+ git_attr_file_source source = { src[i], path, GIT_ATTR_FILE };
+
+ if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts)
+ source.commit_id = info->opts->commit_id;
+
+ error = push_attr_source(info->repo, info->attr_session, info->files,
+ &source, allow_macros);
+ }
return error;
}
@@ -510,7 +607,7 @@ static void release_attr_files(git_vector *files)
static int collect_attr_files(
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *path,
git_vector *files)
{
@@ -519,7 +616,7 @@ static int collect_attr_files(
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
- if ((error = attr_setup(repo, attr_session, flags)) < 0)
+ if ((error = attr_setup(repo, attr_session, opts)) < 0)
return error;
/* Resolve path in a non-bare repo */
@@ -542,15 +639,14 @@ static int collect_attr_files(
*/
if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
- (error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- attrfile.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) {
+ (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
if (error != GIT_ENOTFOUND)
goto cleanup;
}
info.repo = repo;
info.attr_session = attr_session;
- info.flags = flags;
+ info.opts = opts;
info.workdir = workdir;
if (git_repository_index__weakptr(&info.index, repo) < 0)
git_error_clear(); /* no error even if there is no index */
@@ -565,18 +661,16 @@ static int collect_attr_files(
goto cleanup;
if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
- error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- NULL, git_repository_attr_cache(repo)->cfg_attr_file, true);
+ error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0)
goto cleanup;
}
- if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
+ if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
error = system_attr_file(&dir, attr_session);
if (!error)
- error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- NULL, dir.ptr, true);
+ error = push_attr_file(repo, attr_session, files, NULL, dir.ptr);
else if (error == GIT_ENOTFOUND)
error = 0;
}
diff --git a/src/attr_file.c b/src/attr_file.c
index 82692727d..f8627381c 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -33,7 +33,7 @@ static void attr_file_free(git_attr_file *file)
int git_attr_file__new(
git_attr_file **out,
git_attr_file_entry *entry,
- git_attr_file_source source)
+ git_attr_file_source *source)
{
git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
GIT_ERROR_CHECK_ALLOC(attrs);
@@ -47,8 +47,8 @@ int git_attr_file__new(
goto on_error;
GIT_REFCOUNT_INC(attrs);
- attrs->entry = entry;
- attrs->source = source;
+ attrs->entry = entry;
+ memcpy(&attrs->source, source, sizeof(git_attr_file_source));
*out = attrs;
return 0;
@@ -108,11 +108,12 @@ int git_attr_file__load(
git_repository *repo,
git_attr_session *attr_session,
git_attr_file_entry *entry,
- git_attr_file_source source,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros)
{
int error = 0;
+ git_commit *commit = NULL;
git_tree *tree = NULL;
git_tree_entry *tree_entry = NULL;
git_blob *blob = NULL;
@@ -128,11 +129,11 @@ int git_attr_file__load(
*out = NULL;
- switch (source) {
- case GIT_ATTR_FILE__IN_MEMORY:
+ switch (source->type) {
+ case GIT_ATTR_FILE_SOURCE_MEMORY:
/* in-memory attribute file doesn't need data */
break;
- case GIT_ATTR_FILE__FROM_INDEX: {
+ case GIT_ATTR_FILE_SOURCE_INDEX: {
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
(error = git_blob_lookup(&blob, repo, &id)) < 0)
return error;
@@ -145,7 +146,7 @@ int git_attr_file__load(
git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize);
break;
}
- case GIT_ATTR_FILE__FROM_FILE: {
+ case GIT_ATTR_FILE_SOURCE_FILE: {
int fd = -1;
/* For open or read errors, pretend that we got ENOTFOUND. */
@@ -162,10 +163,31 @@ int git_attr_file__load(
break;
}
- case GIT_ATTR_FILE__FROM_HEAD: {
- if ((error = git_repository_head_tree(&tree, repo)) < 0 ||
- (error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0 ||
- (error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
+ case GIT_ATTR_FILE_SOURCE_COMMIT: {
+ if (source->commit_id) {
+ if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 ||
+ (error = git_commit_tree(&tree, commit)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_repository_head_tree(&tree, repo)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) {
+ /*
+ * If the attributes file does not exist, we can
+ * cache an empty file for this commit to prevent
+ * needless future lookups.
+ */
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ break;
+ }
+
+ goto cleanup;
+ }
+
+ if ((error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
goto cleanup;
/*
@@ -182,7 +204,7 @@ int git_attr_file__load(
break;
}
default:
- git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source);
+ git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source->type);
return -1;
}
@@ -210,11 +232,11 @@ int git_attr_file__load(
/* write cache breakers */
if (nonexistent)
file->nonexistent = 1;
- else if (source == GIT_ATTR_FILE__FROM_INDEX)
+ else if (source->type == GIT_ATTR_FILE_SOURCE_INDEX)
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
- else if (source == GIT_ATTR_FILE__FROM_HEAD)
+ else if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT)
git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
- else if (source == GIT_ATTR_FILE__FROM_FILE)
+ else if (source->type == GIT_ATTR_FILE_SOURCE_FILE)
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
/* else always cacheable */
@@ -224,6 +246,7 @@ cleanup:
git_blob_free(blob);
git_tree_entry_free(tree_entry);
git_tree_free(tree);
+ git_commit_free(commit);
git_buf_dispose(&content);
return error;
@@ -232,7 +255,8 @@ cleanup:
int git_attr_file__out_of_date(
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file *file)
+ git_attr_file *file,
+ git_attr_file_source *source)
{
if (!file)
return 1;
@@ -245,15 +269,15 @@ int git_attr_file__out_of_date(
else if (file->nonexistent)
return 1;
- switch (file->source) {
- case GIT_ATTR_FILE__IN_MEMORY:
+ switch (file->source.type) {
+ case GIT_ATTR_FILE_SOURCE_MEMORY:
return 0;
- case GIT_ATTR_FILE__FROM_FILE:
+ case GIT_ATTR_FILE_SOURCE_FILE:
return git_futils_filestamp_check(
&file->cache_data.stamp, file->entry->fullpath);
- case GIT_ATTR_FILE__FROM_INDEX: {
+ case GIT_ATTR_FILE_SOURCE_INDEX: {
int error;
git_oid id;
@@ -264,21 +288,34 @@ int git_attr_file__out_of_date(
return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
}
- case GIT_ATTR_FILE__FROM_HEAD: {
- git_tree *tree;
+ case GIT_ATTR_FILE_SOURCE_COMMIT: {
+ git_tree *tree = NULL;
int error;
- if ((error = git_repository_head_tree(&tree, repo)) < 0)
+ if (source->commit_id) {
+ git_commit *commit = NULL;
+
+ if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0)
+ return error;
+
+ error = git_commit_tree(&tree, commit);
+
+ git_commit_free(commit);
+ } else {
+ error = git_repository_head_tree(&tree, repo);
+ }
+
+ if (error < 0)
return error;
- error = git_oid__cmp(&file->cache_data.oid, git_tree_id(tree));
+ error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0);
git_tree_free(tree);
return error;
}
default:
- git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source);
+ git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source.type);
return -1;
}
}
@@ -389,6 +426,7 @@ int git_attr_file__lookup_one(
int git_attr_file__load_standalone(git_attr_file **out, const char *path)
{
git_buf content = GIT_BUF_INIT;
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
git_attr_file *file = NULL;
int error;
@@ -400,7 +438,7 @@ int git_attr_file__load_standalone(git_attr_file **out, const char *path)
* don't have to free it - freeing file+pool will free cache entry, too.
*/
- if ((error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE)) < 0 ||
+ if ((error = git_attr_file__new(&file, NULL, &source)) < 0 ||
(error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 ||
(error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0)
goto out;
diff --git a/src/attr_file.h b/src/attr_file.h
index 617436b14..16e33caf1 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -37,12 +37,30 @@
(GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
typedef enum {
- GIT_ATTR_FILE__IN_MEMORY = 0,
- GIT_ATTR_FILE__FROM_FILE = 1,
- GIT_ATTR_FILE__FROM_INDEX = 2,
- GIT_ATTR_FILE__FROM_HEAD = 3,
+ GIT_ATTR_FILE_SOURCE_MEMORY = 0,
+ GIT_ATTR_FILE_SOURCE_FILE = 1,
+ GIT_ATTR_FILE_SOURCE_INDEX = 2,
+ GIT_ATTR_FILE_SOURCE_COMMIT = 3,
- GIT_ATTR_FILE_NUM_SOURCES = 4
+ GIT_ATTR_FILE_NUM_SOURCES = 4
+} git_attr_file_source_t;
+
+typedef struct {
+ /* The source location for the attribute file. */
+ git_attr_file_source_t type;
+
+ /*
+ * The filename of the attribute file to read (relative to the
+ * given base path).
+ */
+ const char *base;
+ const char *filename;
+
+ /*
+ * The commit ID when the given source type is a commit (or NULL
+ * for the repository's HEAD commit.)
+ */
+ git_oid *commit_id;
} git_attr_file_source;
extern const char *git_attr__true;
@@ -124,7 +142,7 @@ extern int git_attr_get_many_with_session(
const char **values_out,
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *path,
size_t num_attr,
const char **names);
@@ -142,7 +160,7 @@ typedef int (*git_attr_file_parser)(
int git_attr_file__new(
git_attr_file **out,
git_attr_file_entry *entry,
- git_attr_file_source source);
+ git_attr_file_source *source);
void git_attr_file__free(git_attr_file *file);
@@ -151,7 +169,7 @@ int git_attr_file__load(
git_repository *repo,
git_attr_session *attr_session,
git_attr_file_entry *ce,
- git_attr_file_source source,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros);
@@ -159,7 +177,7 @@ int git_attr_file__load_standalone(
git_attr_file **out, const char *path);
int git_attr_file__out_of_date(
- git_repository *repo, git_attr_session *session, git_attr_file *file);
+ git_repository *repo, git_attr_session *session, git_attr_file *file, git_attr_file_source *source);
int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros);
diff --git a/src/attrcache.c b/src/attrcache.c
index 32513da01..7fe2bfbdb 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -112,7 +112,7 @@ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
* Replace the existing value if another thread has
* created it in the meantime.
*/
- old = git_atomic_swap(entry->file[file->source], file);
+ old = git_atomic_swap(entry->file[file->source.type], file);
if (old) {
GIT_REFCOUNT_OWN(old, NULL);
@@ -136,7 +136,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
- old = git_atomic_compare_and_swap(&entry->file[file->source], file, NULL);
+ old = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
attr_cache_unlock(cache);
@@ -158,41 +158,42 @@ static int attr_cache_lookup(
git_attr_file_entry **out_entry,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename)
+ git_attr_file_source *source)
{
int error = 0;
git_buf path = GIT_BUF_INIT;
- const char *wd = git_repository_workdir(repo), *relfile;
+ const char *wd = git_repository_workdir(repo);
+ const char *filename;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL;
/* join base and path as needed */
- if (base != NULL && git_path_root(filename) < 0) {
+ if (source->base != NULL && git_path_root(source->filename) < 0) {
git_buf *p = attr_session ? &attr_session->tmp : &path;
- if (git_buf_joinpath(p, base, filename) < 0 ||
+ if (git_buf_joinpath(p, source->base, source->filename) < 0 ||
git_path_validate_workdir_buf(repo, p) < 0)
return -1;
filename = p->ptr;
+ } else {
+ filename = source->filename;
}
- relfile = filename;
- if (wd && !git__prefixcmp(relfile, wd))
- relfile += strlen(wd);
+ if (wd && !git__prefixcmp(filename, wd))
+ filename += strlen(wd);
/* check cache for existing entry */
if ((error = attr_cache_lock(cache)) < 0)
goto cleanup;
- entry = attr_cache_lookup_entry(cache, relfile);
- if (!entry)
- error = attr_cache_make_entry(&entry, repo, relfile);
- else if (entry->file[source] != NULL) {
- file = entry->file[source];
+ entry = attr_cache_lookup_entry(cache, filename);
+
+ if (!entry) {
+ error = attr_cache_make_entry(&entry, repo, filename);
+ } else if (entry->file[source->type] != NULL) {
+ file = entry->file[source->type];
GIT_REFCOUNT_INC(file);
}
@@ -210,9 +211,7 @@ int git_attr_cache__get(
git_attr_file **out,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros)
{
@@ -221,19 +220,21 @@ int git_attr_cache__get(
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL, *updated = NULL;
- if ((error = attr_cache_lookup(
- &file, &entry, repo, attr_session, source, base, filename)) < 0)
+ if ((error = attr_cache_lookup(&file, &entry, repo, attr_session, source)) < 0)
return error;
/* load file if we don't have one or if existing one is out of date */
- if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
- error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser, allow_macros);
+ if (!file ||
+ (error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0)
+ error = git_attr_file__load(&updated, repo, attr_session,
+ entry, source, parser,
+ allow_macros);
/* if we loaded the file, insert into and/or update cache */
if (updated) {
- if ((error = attr_cache_upsert(cache, updated)) < 0)
+ if ((error = attr_cache_upsert(cache, updated)) < 0) {
git_attr_file__free(updated);
- else {
+ } else {
git_attr_file__free(file); /* offset incref from lookup */
file = updated;
}
@@ -260,7 +261,7 @@ int git_attr_cache__get(
bool git_attr_cache__is_cached(
git_repository *repo,
- git_attr_file_source source,
+ git_attr_file_source_t source_type,
const char *filename)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
@@ -273,7 +274,7 @@ bool git_attr_cache__is_cached(
if ((entry = git_strmap_get(files, filename)) == NULL)
return false;
- return entry && (entry->file[source] != NULL);
+ return entry && (entry->file[source_type] != NULL);
}
diff --git a/src/attrcache.h b/src/attrcache.h
index 5e2fb29f4..b13e0e8f0 100644
--- a/src/attrcache.h
+++ b/src/attrcache.h
@@ -31,16 +31,14 @@ extern int git_attr_cache__get(
git_attr_file **file,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros);
extern bool git_attr_cache__is_cached(
git_repository *repo,
- git_attr_file_source source,
- const char *path);
+ git_attr_file_source_t source_type,
+ const char *filename);
extern int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
diff --git a/src/blob.c b/src/blob.c
index 169e34503..01ebf075e 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -421,7 +421,7 @@ int git_blob_filter(
int error = 0;
git_filter_list *fl = NULL;
git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
- git_filter_flag_t flags = GIT_FILTER_DEFAULT;
+ git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
GIT_ASSERT_ARG(blob);
GIT_ASSERT_ARG(path);
@@ -441,14 +441,19 @@ int git_blob_filter(
return 0;
if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
- flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
+ filter_opts.flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
- flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
+ filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
- if (!(error = git_filter_list_load(
+ if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) {
+ filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_COMMIT;
+ filter_opts.commit_id = opts.commit_id;
+ }
+
+ if (!(error = git_filter_list_load_ext(
&fl, git_blob_owner(blob), blob, path,
- GIT_FILTER_TO_WORKTREE, flags))) {
+ GIT_FILTER_TO_WORKTREE, &filter_opts))) {
error = git_filter_list_apply_to_blob(out, fl, blob);
diff --git a/src/branch.c b/src/branch.c
index 29ff0b9d9..e6818a86d 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -468,7 +468,7 @@ cleanup:
return error;
}
-int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
+static int git_branch_upstream_with_format(git_buf *buf, git_repository *repo, const char *refname, const char *format, const char *format_name)
{
int error;
git_config *cfg;
@@ -480,11 +480,11 @@ int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *r
return error;
if ((error = git_buf_sanitize(buf)) < 0 ||
- (error = retrieve_upstream_configuration(buf, cfg, refname, "branch.%s.remote")) < 0)
+ (error = retrieve_upstream_configuration(buf, cfg, refname, format)) < 0)
return error;
if (git_buf_len(buf) == 0) {
- git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream remote", refname);
+ git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream %s", refname, format_name);
error = GIT_ENOTFOUND;
git_buf_clear(buf);
}
@@ -492,6 +492,16 @@ int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *r
return error;
}
+int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
+{
+ return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.remote", "remote");
+}
+
+int git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname)
+{
+ return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.merge", "merge");
+}
+
int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
{
git_strarray remote_list = {0};
diff --git a/src/cc-compat.h b/src/cc-compat.h
index de1469da8..6bdc65145 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -43,7 +43,15 @@
# define GIT_ALIGN(x,size) x
#endif
-#define GIT_UNUSED(x) ((void)(x))
+#if defined(__GNUC__)
+# define GIT_UNUSED(x) \
+ do { \
+ typeof(x) _unused __attribute__((unused)); \
+ _unused = (x); \
+ } while (0)
+#else
+# define GIT_UNUSED(x) ((void)(x))
+#endif
/* Define the printf format specifier to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
diff --git a/src/checkout.c b/src/checkout.c
index 2f84df7a0..31d473ec8 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1513,7 +1513,7 @@ static int blob_content_to_file(
int flags = data->opts.file_open_flags;
mode_t file_mode = data->opts.file_mode ?
data->opts.file_mode : entry_filemode;
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
struct checkout_stream writer;
mode_t mode;
git_filter_list *fl = NULL;
@@ -1536,13 +1536,13 @@ static int blob_content_to_file(
return fd;
}
- filter_opts.attr_session = &data->attr_session;
- filter_opts.temp_buf = &data->tmp;
+ filter_session.attr_session = &data->attr_session;
+ filter_session.temp_buf = &data->tmp;
if (!data->opts.disable_filters &&
- (error = git_filter_list__load_ext(
+ (error = git_filter_list__load(
&fl, data->repo, blob, hint_path,
- GIT_FILTER_TO_WORKTREE, &filter_opts))) {
+ GIT_FILTER_TO_WORKTREE, &filter_session))) {
p_close(fd);
return error;
}
@@ -2064,7 +2064,7 @@ static int checkout_write_merge(
git_merge_file_result result = {0};
git_filebuf output = GIT_FILEBUF_INIT;
git_filter_list *fl = NULL;
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
int error = 0;
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
@@ -2114,12 +2114,12 @@ static int checkout_write_merge(
in_data.ptr = (char *)result.ptr;
in_data.size = result.len;
- filter_opts.attr_session = &data->attr_session;
- filter_opts.temp_buf = &data->tmp;
+ filter_session.attr_session = &data->attr_session;
+ filter_session.temp_buf = &data->tmp;
- if ((error = git_filter_list__load_ext(
+ if ((error = git_filter_list__load(
&fl, data->repo, NULL, git_buf_cstr(&path_workdir),
- GIT_FILTER_TO_WORKTREE, &filter_opts)) < 0 ||
+ GIT_FILTER_TO_WORKTREE, &filter_session)) < 0 ||
(error = git_filter_list__convert_buf(&out_data, fl, &in_data)) < 0)
goto done;
} else {
diff --git a/src/common.h b/src/common.h
index 9123fb595..9bb1116b5 100644
--- a/src/common.h
+++ b/src/common.h
@@ -19,6 +19,8 @@
# define GIT_INLINE(type) static __inline type
#elif defined(__GNUC__)
# define GIT_INLINE(type) static __inline__ type
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define GIT_INLINE(type) static inline type
#else
# define GIT_INLINE(type) static type
#endif
@@ -28,6 +30,24 @@
# define __has_builtin(x) 0
#endif
+/**
+ * Declare that a function's return value must be used.
+ *
+ * Used mostly to guard against potential silent bugs at runtime. This is
+ * recommended to be added to functions that:
+ *
+ * - Allocate / reallocate memory. This prevents memory leaks or errors where
+ * buffers are expected to have grown to a certain size, but could not be
+ * resized.
+ * - Acquire locks. When a lock cannot be acquired, that will almost certainly
+ * cause a data race / undefined behavior.
+ */
+#if defined(__GNUC__)
+# define GIT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define GIT_WARN_UNUSED_RESULT
+#endif
+
#include <assert.h>
#include <errno.h>
#include <limits.h>
diff --git a/src/diff_driver.c b/src/diff_driver.c
index e9f63cb17..8e9131feb 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -389,13 +389,13 @@ int git_diff_driver_lookup(
void git_diff_driver_free(git_diff_driver *driver)
{
- size_t i;
+ git_diff_driver_pattern *pat;
if (!driver)
return;
- for (i = 0; i < git_array_size(driver->fn_patterns); ++i)
- git_regexp_dispose(& git_array_get(driver->fn_patterns, i)->re);
+ while ((pat = git_array_pop(driver->fn_patterns)) != NULL)
+ git_regexp_dispose(&pat->re);
git_array_clear(driver->fn_patterns);
git_regexp_dispose(&driver->word_pattern);
diff --git a/src/features.h.in b/src/features.h.in
index ab523f90b..202cef49e 100644
--- a/src/features.h.in
+++ b/src/features.h.in
@@ -3,6 +3,7 @@
#cmakedefine GIT_DEBUG_POOL 1
#cmakedefine GIT_DEBUG_STRICT_ALLOC 1
+#cmakedefine GIT_DEBUG_STRICT_OPEN 1
#cmakedefine GIT_TRACE 1
#cmakedefine GIT_THREADS 1
@@ -34,6 +35,7 @@
#cmakedefine GIT_WINHTTP 1
#cmakedefine GIT_HTTPS 1
#cmakedefine GIT_OPENSSL 1
+#cmakedefine GIT_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SECURE_TRANSPORT 1
#cmakedefine GIT_MBEDTLS 1
diff --git a/src/filter.c b/src/filter.c
index 6c09a6ad5..eed175e88 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -19,12 +19,12 @@
#include "array.h"
struct git_filter_source {
- git_repository *repo;
- const char *path;
- git_oid oid; /* zero if unknown (which is likely) */
- uint16_t filemode; /* zero if unknown */
- git_filter_mode_t mode;
- uint32_t flags;
+ git_repository *repo;
+ const char *path;
+ git_oid oid; /* zero if unknown (which is likely) */
+ uint16_t filemode; /* zero if unknown */
+ git_filter_mode_t mode;
+ git_filter_options options;
};
typedef struct {
@@ -396,7 +396,7 @@ git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
uint32_t git_filter_source_flags(const git_filter_source *src)
{
- return src->flags;
+ return src->options.flags;
}
static int filter_list_new(
@@ -416,7 +416,8 @@ static int filter_list_new(
fl->source.repo = src->repo;
fl->source.path = fl->path;
fl->source.mode = src->mode;
- fl->source.flags = src->flags;
+
+ memcpy(&fl->source.options, &src->options, sizeof(git_filter_options));
*out = fl;
return 0;
@@ -425,25 +426,30 @@ static int filter_list_new(
static int filter_list_check_attributes(
const char ***out,
git_repository *repo,
- git_attr_session *attr_session,
+ git_filter_session *filter_session,
git_filter_def *fdef,
const git_filter_source *src)
{
const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
- uint32_t flags = 0;
+ git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT;
size_t i;
int error;
GIT_ERROR_CHECK_ALLOC(strs);
- if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
- flags |= GIT_ATTR_CHECK_NO_SYSTEM;
+ if ((src->options.flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
+ attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM;
+
+ if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
+ attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
- if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
- flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
+ if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) {
+ attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_COMMIT;
+ attr_opts.commit_id = src->options.commit_id;
+ }
error = git_attr_get_many_with_session(
- strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs);
+ strs, repo, filter_session->attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs);
/* if no values were found but no matches are needed, it's okay! */
if (error == GIT_ENOTFOUND && !fdef->nmatches) {
@@ -488,17 +494,17 @@ int git_filter_list_new(
src.repo = repo;
src.path = NULL;
src.mode = mode;
- src.flags = flags;
+ src.options.flags = flags;
return filter_list_new(out, &src);
}
-int git_filter_list__load_ext(
+int git_filter_list__load(
git_filter_list **filters,
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode,
- git_filter_options *filter_opts)
+ git_filter_session *filter_session)
{
int error = 0;
git_filter_list *fl = NULL;
@@ -515,7 +521,8 @@ int git_filter_list__load_ext(
src.repo = repo;
src.path = path;
src.mode = mode;
- src.flags = filter_opts->flags;
+
+ memcpy(&src.options, &filter_session->options, sizeof(git_filter_options));
if (blob)
git_oid_cpy(&src.oid, git_blob_id(blob));
@@ -529,7 +536,8 @@ int git_filter_list__load_ext(
if (fdef->nattrs > 0) {
error = filter_list_check_attributes(
- &values, repo, filter_opts->attr_session, fdef, &src);
+ &values, repo,
+ filter_session, fdef, &src);
if (error == GIT_ENOTFOUND) {
error = 0;
@@ -556,7 +564,7 @@ int git_filter_list__load_ext(
if ((error = filter_list_new(&fl, &src)) < 0)
break;
- fl->temp_buf = filter_opts->temp_buf;
+ fl->temp_buf = filter_session->temp_buf;
}
fe = git_array_alloc(fl->filters);
@@ -580,6 +588,23 @@ int git_filter_list__load_ext(
return error;
}
+int git_filter_list_load_ext(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_options *opts)
+{
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
+
+ if (opts)
+ memcpy(&filter_session.options, opts, sizeof(git_filter_options));
+
+ return git_filter_list__load(
+ filters, repo, blob, path, mode, &filter_session);
+}
+
int git_filter_list_load(
git_filter_list **filters,
git_repository *repo,
@@ -588,12 +613,12 @@ int git_filter_list_load(
git_filter_mode_t mode,
uint32_t flags)
{
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
- filter_opts.flags = flags;
+ filter_session.options.flags = flags;
- return git_filter_list__load_ext(
- filters, repo, blob, path, mode, &filter_opts);
+ return git_filter_list__load(
+ filters, repo, blob, path, mode, &filter_session);
}
void git_filter_list_free(git_filter_list *fl)
diff --git a/src/filter.h b/src/filter.h
index bbc4c0fb2..55ed50e97 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -16,24 +16,24 @@
#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
typedef struct {
+ git_filter_options options;
git_attr_session *attr_session;
git_buf *temp_buf;
- uint32_t flags;
-} git_filter_options;
+} git_filter_session;
-#define GIT_FILTER_OPTIONS_INIT {0}
+#define GIT_FILTER_SESSION_INIT {GIT_FILTER_OPTIONS_INIT, 0}
extern int git_filter_global_init(void);
extern void git_filter_free(git_filter *filter);
-extern int git_filter_list__load_ext(
+extern int git_filter_list__load(
git_filter_list **filters,
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode,
- git_filter_options *filter_opts);
+ git_filter_session *filter_session);
/*
* The given input buffer will be converted to the given output buffer.
diff --git a/src/hash.c b/src/hash.c
index d33423660..5a7278e42 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -16,7 +16,7 @@ int git_hash_ctx_init(git_hash_ctx *ctx)
{
int error;
- if ((error = git_hash_sha1_ctx_init(&ctx->sha1)) < 0)
+ if ((error = git_hash_sha1_ctx_init(&ctx->ctx.sha1)) < 0)
return error;
ctx->algo = GIT_HASH_ALGO_SHA1;
@@ -28,7 +28,7 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx)
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- git_hash_sha1_ctx_cleanup(&ctx->sha1);
+ git_hash_sha1_ctx_cleanup(&ctx->ctx.sha1);
return;
default:
/* unreachable */ ;
@@ -39,7 +39,7 @@ int git_hash_init(git_hash_ctx *ctx)
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- return git_hash_sha1_init(&ctx->sha1);
+ return git_hash_sha1_init(&ctx->ctx.sha1);
default:
/* unreachable */ ;
}
@@ -51,7 +51,7 @@ int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- return git_hash_sha1_update(&ctx->sha1, data, len);
+ return git_hash_sha1_update(&ctx->ctx.sha1, data, len);
default:
/* unreachable */ ;
}
@@ -63,7 +63,7 @@ int git_hash_final(git_oid *out, git_hash_ctx *ctx)
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- return git_hash_sha1_final(out, &ctx->sha1);
+ return git_hash_sha1_final(out, &ctx->ctx.sha1);
default:
/* unreachable */ ;
}
diff --git a/src/hash.h b/src/hash.h
index 017bb286c..87305cc79 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -27,7 +27,7 @@ typedef enum {
typedef struct git_hash_ctx {
union {
git_hash_sha1_ctx sha1;
- };
+ } ctx;
git_hash_algo_t algo;
} git_hash_ctx;
diff --git a/src/hash/sha1/sha1dc/sha1.c b/src/hash/sha1/sha1dc/sha1.c
index 9d3cf81d4..86b8cc075 100644
--- a/src/hash/sha1/sha1dc/sha1.c
+++ b/src/hash/sha1/sha1dc/sha1.c
@@ -10,10 +10,8 @@
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
-#ifdef __unix__
#include <sys/types.h> /* make sure macros like _BIG_ENDIAN visible */
#endif
-#endif
#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C
#include SHA1DC_CUSTOM_INCLUDE_SHA1_C
diff --git a/src/ignore.c b/src/ignore.c
index 948c58d85..f7551cddb 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -247,11 +247,12 @@ static int push_ignore_file(
const char *base,
const char *filename)
{
- int error = 0;
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
git_attr_file *file = NULL;
+ int error = 0;
+
+ error = git_attr_cache__get(&file, ignores->repo, NULL, &source, parse_ignore_file, false);
- error = git_attr_cache__get(&file, ignores->repo, NULL, GIT_ATTR_FILE__FROM_FILE,
- base, filename, parse_ignore_file, false);
if (error < 0)
return error;
@@ -272,13 +273,13 @@ static int push_one_ignore(void *payload, const char *path)
static int get_internal_ignores(git_attr_file **out, git_repository *repo)
{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_MEMORY, NULL, GIT_IGNORE_INTERNAL };
int error;
if ((error = git_attr_cache__init(repo)) < 0)
return error;
- error = git_attr_cache__get(out, repo, NULL, GIT_ATTR_FILE__IN_MEMORY, NULL,
- GIT_IGNORE_INTERNAL, NULL, false);
+ error = git_attr_cache__get(out, repo, NULL, &source, NULL, false);
/* if internal rules list is empty, insert default rules */
if (!error && !(*out)->rules.length)
diff --git a/src/libgit2.c b/src/libgit2.c
index f27c9997d..aee9cf2cd 100644
--- a/src/libgit2.c
+++ b/src/libgit2.c
@@ -36,20 +36,14 @@
# include "win32/w32_leakcheck.h"
#endif
-#ifdef GIT_OPENSSL
-# include <openssl/err.h>
-#endif
-
-#ifdef GIT_MBEDTLS
-# include <mbedtls/error.h>
-#endif
-
/* Declarations for tuneable settings */
extern size_t git_mwindow__window_size;
extern size_t git_mwindow__mapped_limit;
extern size_t git_mwindow__file_limit;
extern size_t git_indexer__max_objects;
extern bool git_disable_pack_keep_file_checks;
+extern int git_odb__packed_priority;
+extern int git_odb__loose_priority;
char *git__user_agent;
char *git__ssl_ciphers;
@@ -368,6 +362,14 @@ int git_libgit2_opts(int key, ...)
git_http__expect_continue = (va_arg(ap, int) != 0);
break;
+ case GIT_OPT_SET_ODB_PACKED_PRIORITY:
+ git_odb__packed_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_ODB_LOOSE_PRIORITY:
+ git_odb__loose_priority = va_arg(ap, int);
+ break;
+
default:
git_error_set(GIT_ERROR_INVALID, "invalid option key");
error = -1;
diff --git a/src/netops.h b/src/netops.h
index 771c87b64..7140b39bc 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -14,7 +14,7 @@
#include "net.h"
#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
+# include "streams/openssl.h"
#endif
typedef struct gitno_ssl {
diff --git a/src/odb.c b/src/odb.c
index 05bdaf6a3..e3a5381e6 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -23,14 +23,14 @@
#define GIT_ALTERNATES_FILE "info/alternates"
+#define GIT_ALTERNATES_MAX_DEPTH 5
+
/*
* We work under the assumption that most objects for long-running
* operations will be packed
*/
-#define GIT_LOOSE_PRIORITY 1
-#define GIT_PACKED_PRIORITY 2
-
-#define GIT_ALTERNATES_MAX_DEPTH 5
+int git_odb__loose_priority = GIT_ODB_DEFAULT_LOOSE_PRIORITY;
+int git_odb__packed_priority = GIT_ODB_DEFAULT_PACKED_PRIORITY;
bool git_odb__strict_hash_verification = true;
@@ -573,7 +573,7 @@ int git_odb__add_default_backends(
git_odb *db, const char *objects_dir,
bool as_alternates, int alternate_depth)
{
- size_t i;
+ size_t i = 0;
struct stat st;
ino_t inode;
git_odb_backend *loose, *packed;
@@ -582,7 +582,7 @@ int git_odb__add_default_backends(
* a cross-platform workaround for this */
#ifdef GIT_WIN32
GIT_UNUSED(i);
- GIT_UNUSED(st);
+ GIT_UNUSED(&st);
inode = 0;
#else
@@ -613,12 +613,12 @@ int git_odb__add_default_backends(
/* add the loose object backend */
if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 ||
- add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
+ add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0)
return -1;
/* add the packed file backend */
if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
- add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0)
+ add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
return -1;
if (git_mutex_lock(&db->lock) < 0) {
diff --git a/src/odb.h b/src/odb.h
index 5bebb6edc..4a8ebff19 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -24,6 +24,9 @@
#define GIT_OBJECT_DIR_MODE 0777
#define GIT_OBJECT_FILE_MODE 0444
+#define GIT_ODB_DEFAULT_LOOSE_PRIORITY 1
+#define GIT_ODB_DEFAULT_PACKED_PRIORITY 2
+
extern bool git_odb__strict_hash_verification;
/* DO NOT EXPORT */
diff --git a/src/path.c b/src/path.c
index 8928e49b8..ec573220e 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1915,7 +1915,13 @@ GIT_INLINE(bool) should_validate_longpaths(git_repository *repo)
}
#else
-# define should_validate_longpaths(repo) (GIT_UNUSED(repo), false)
+
+GIT_INLINE(bool) should_validate_longpaths(git_repository *repo)
+{
+ GIT_UNUSED(repo);
+
+ return false;
+}
#endif
int git_path_validate_workdir(git_repository *repo, const char *path)
diff --git a/src/posix.c b/src/posix.c
index bf764ae6b..c40134824 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -109,6 +109,13 @@ int p_open(const char *path, volatile int flags, ...)
{
mode_t mode = 0;
+ #ifdef GIT_DEBUG_STRICT_OPEN
+ if (strstr(path, "//") != NULL) {
+ errno = EACCES;
+ return -1;
+ }
+ #endif
+
if (flags & O_CREAT) {
va_list arg_list;
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 0b8e103c2..0cb925516 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -122,7 +122,7 @@ static int packed_reload(refdb_fs_backend *backend)
*/
if (error <= 0) {
if (error == GIT_ENOTFOUND) {
- git_sortedcache_clear(backend->refcache, true);
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, true));
git_error_clear();
error = 0;
}
@@ -131,7 +131,7 @@ static int packed_reload(refdb_fs_backend *backend)
/* At this point, refresh the packed refs from the loaded buffer. */
- git_sortedcache_clear(backend->refcache, false);
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false));
scan = (char *)packedrefs.ptr;
eof = scan + packedrefs.size;
@@ -219,7 +219,7 @@ static int packed_reload(refdb_fs_backend *backend)
parse_failed:
git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
- git_sortedcache_clear(backend->refcache, false);
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false));
git_sortedcache_wunlock(backend->refcache);
git_buf_dispose(&packedrefs);
@@ -578,7 +578,7 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
}
}
- if ((error = git_buf_printf(&path, "%s/", backend->commonpath)) < 0 ||
+ if ((error = git_buf_puts(&path, backend->commonpath)) < 0 ||
(error = git_buf_put(&path, ref_prefix, ref_prefix_len)) < 0) {
git_buf_dispose(&path);
return error;
@@ -1609,8 +1609,9 @@ static char *setup_namespace(git_repository *repo, const char *in)
GIT_MKDIR_PATH, NULL) < 0)
goto done;
- /* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */
+ /* Return root of the namespaced gitpath, i.e. without the trailing 'refs' */
git_buf_rtruncate_at_char(&path, '/');
+ git_buf_putc(&path, '/');
out = git_buf_detach(&path);
done:
diff --git a/src/remote.c b/src/remote.c
index 5f6ba5bf7..ec68cc0f6 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -1460,6 +1460,11 @@ static int update_tips_for_spec(
if (error < 0 && error != GIT_ENOTFOUND)
goto on_error;
+ if (!(error || error == GIT_ENOTFOUND)
+ && !spec->force
+ && !git_graph_descendant_of(remote->repo, &head->oid, &old))
+ continue;
+
if (error == GIT_ENOTFOUND) {
memset(&old, 0, GIT_OID_RAWSZ);
diff --git a/src/sortedcache.h b/src/sortedcache.h
index ca8b10674..0e1f63ceb 100644
--- a/src/sortedcache.h
+++ b/src/sortedcache.h
@@ -58,7 +58,7 @@ typedef struct {
* may be NULL. The cache makes it easy to load this and check
* if it has been modified since the last load and/or write.
*/
-int git_sortedcache_new(
+GIT_WARN_UNUSED_RESULT int git_sortedcache_new(
git_sortedcache **out,
size_t item_path_offset, /* use offsetof(struct, path-field) macro */
git_sortedcache_free_item_fn free_item,
@@ -71,7 +71,7 @@ int git_sortedcache_new(
* - `copy_item` can be NULL to just use memcpy
* - if `lock`, grabs read lock on `src` during copy and releases after
*/
-int git_sortedcache_copy(
+GIT_WARN_UNUSED_RESULT int git_sortedcache_copy(
git_sortedcache **out,
git_sortedcache *src,
bool lock,
@@ -100,7 +100,7 @@ const char *git_sortedcache_path(git_sortedcache *sc);
*/
/* Lock sortedcache for write */
-int git_sortedcache_wlock(git_sortedcache *sc);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_wlock(git_sortedcache *sc);
/* Unlock sorted cache when done with write */
void git_sortedcache_wunlock(git_sortedcache *sc);
@@ -120,7 +120,8 @@ void git_sortedcache_wunlock(git_sortedcache *sc);
*
* @return 0 if up-to-date, 1 if out-of-date, <0 on error
*/
-int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_lockandload(
+ git_sortedcache *sc, git_buf *buf);
/* Refresh file timestamp after write completes
* You should already be holding the write lock when you call this.
@@ -132,12 +133,13 @@ void git_sortedcache_updated(git_sortedcache *sc);
* If `wlock` is true, grabs write lock and releases when done, otherwise
* you should already be holding a write lock when you call this.
*/
-int git_sortedcache_clear(git_sortedcache *sc, bool wlock);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_clear(
+ git_sortedcache *sc, bool wlock);
/* Find and/or insert item, returning pointer to item data.
* You should already be holding the write lock when you call this.
*/
-int git_sortedcache_upsert(
+GIT_WARN_UNUSED_RESULT int git_sortedcache_upsert(
void **out, git_sortedcache *sc, const char *key);
/* Removes entry at pos from cache
@@ -155,7 +157,7 @@ int git_sortedcache_remove(git_sortedcache *sc, size_t pos);
*/
/* Lock sortedcache for read */
-int git_sortedcache_rlock(git_sortedcache *sc);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_rlock(git_sortedcache *sc);
/* Unlock sorted cache when done with read */
void git_sortedcache_runlock(git_sortedcache *sc);
diff --git a/src/streams/openssl.c b/src/streams/openssl.c
index d866832c6..89c96780c 100644
--- a/src/streams/openssl.c
+++ b/src/streams/openssl.c
@@ -6,11 +6,14 @@
*/
#include "streams/openssl.h"
+#include "streams/openssl_legacy.h"
+#include "streams/openssl_dynamic.h"
#ifdef GIT_OPENSSL
#include <ctype.h>
+#include "common.h"
#include "runtime.h"
#include "settings.h"
#include "posix.h"
@@ -26,156 +29,17 @@
# include <netinet/in.h>
#endif
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/x509v3.h>
-#include <openssl/bio.h>
+#ifndef GIT_OPENSSL_DYNAMIC
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+#endif
SSL_CTX *git__ssl_ctx;
#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
-#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
- (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
-# define OPENSSL_LEGACY_API
-#endif
-
-/*
- * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
- * which do not exist in previous versions. We define these inline functions so
- * we can program against the interface instead of littering the implementation
- * with ifdefs. We do the same for OPENSSL_init_ssl.
- */
-#if defined(OPENSSL_LEGACY_API)
-static int OPENSSL_init_ssl(int opts, void *settings)
-{
- GIT_UNUSED(opts);
- GIT_UNUSED(settings);
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
- return 0;
-}
-
-static BIO_METHOD* BIO_meth_new(int type, const char *name)
-{
- BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
- if (!meth) {
- return NULL;
- }
-
- meth->type = type;
- meth->name = name;
-
- return meth;
-}
-
-static void BIO_meth_free(BIO_METHOD *biom)
-{
- git__free(biom);
-}
-
-static int BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
-{
- biom->bwrite = write;
- return 1;
-}
-
-static int BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
-{
- biom->bread = read;
- return 1;
-}
-
-static int BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
-{
- biom->bputs = puts;
- return 1;
-}
-
-static int BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
-
-{
- biom->bgets = gets;
- return 1;
-}
-
-static int BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
-{
- biom->ctrl = ctrl;
- return 1;
-}
-
-static int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
-{
- biom->create = create;
- return 1;
-}
-
-static int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
-{
- biom->destroy = destroy;
- return 1;
-}
-
-static int BIO_get_new_index(void)
-{
- /* This exists as of 1.1 so before we'd just have 0 */
- return 0;
-}
-
-static void BIO_set_init(BIO *b, int init)
-{
- b->init = init;
-}
-
-static void BIO_set_data(BIO *a, void *ptr)
-{
- a->ptr = ptr;
-}
-
-static void *BIO_get_data(BIO *a)
-{
- return a->ptr;
-}
-
-static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
-{
- return ASN1_STRING_data((ASN1_STRING *)x);
-}
-
-# if defined(GIT_THREADS)
-static git_mutex *openssl_locks;
-
-static void openssl_locking_function(
- int mode, int n, const char *file, int line)
-{
- int lock;
-
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- lock = mode & CRYPTO_LOCK;
-
- if (lock) {
- (void)git_mutex_lock(&openssl_locks[n]);
- } else {
- git_mutex_unlock(&openssl_locks[n]);
- }
-}
-
-static void shutdown_ssl_locking(void)
-{
- int num_locks, i;
-
- num_locks = CRYPTO_num_locks();
- CRYPTO_set_locking_callback(NULL);
-
- for (i = 0; i < num_locks; ++i)
- git_mutex_free(&openssl_locks[i]);
- git__free(openssl_locks);
-}
-# endif /* GIT_THREADS */
-#endif /* OPENSSL_LEGACY_API */
static BIO_METHOD *git_stream_bio_method;
static int init_bio_method(void);
@@ -198,46 +62,47 @@ static void shutdown_ssl(void)
}
#ifdef VALGRIND
-#ifdef OPENSSL_LEGACY_API
-static void *git_openssl_malloc(size_t bytes)
-{
- return git__calloc(1, bytes);
-}
+# if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
-static void *git_openssl_realloc(void *mem, size_t size)
-{
- return git__realloc(mem, size);
-}
-
-static void git_openssl_free(void *mem)
-{
- return git__free(mem);
-}
-#else
static void *git_openssl_malloc(size_t bytes, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
return git__calloc(1, bytes);
}
-
+
static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
return git__realloc(mem, size);
}
-
+
static void git_openssl_free(void *mem, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
- return git__free(mem);
+ git__free(mem);
+}
+# else /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */
+static void *git_openssl_malloc(size_t bytes)
+{
+ return git__calloc(1, bytes);
}
-#endif
-#endif
-int git_openssl_stream_global_init(void)
+static void *git_openssl_realloc(void *mem, size_t size)
+{
+ return git__realloc(mem, size);
+}
+
+static void git_openssl_free(void *mem)
+{
+ git__free(mem);
+}
+# endif /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */
+#endif /* VALGRIND */
+
+static int openssl_init(void)
{
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
const char *ciphers = git_libgit2__ssl_ciphers();
@@ -251,13 +116,18 @@ int git_openssl_stream_global_init(void)
#endif
#ifdef VALGRIND
- /* Swap in our own allocator functions that initialize allocated memory */
- if (!allocators_initialized &&
+ /*
+ * Swap in our own allocator functions that initialize
+ * allocated memory to avoid spurious valgrind warnings.
+ * Don't error on failure; many builds of OpenSSL do not
+ * allow you to set these functions.
+ */
+ if (!allocators_initialized) {
CRYPTO_set_mem_functions(git_openssl_malloc,
git_openssl_realloc,
- git_openssl_free) != 1)
- goto error;
- allocators_initialized = true;
+ git_openssl_free);
+ allocators_initialized = true;
+ }
#endif
OPENSSL_init_ssl(0, NULL);
@@ -296,42 +166,60 @@ error:
return -1;
}
-#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
-static void threadid_cb(CRYPTO_THREADID *threadid)
+/*
+ * When we use dynamic loading, we defer OpenSSL initialization until
+ * it's first used. `openssl_ensure_initialized` will do the work
+ * under a mutex.
+ */
+git_mutex openssl_mutex;
+bool openssl_initialized;
+
+int git_openssl_stream_global_init(void)
{
- GIT_UNUSED(threadid);
- CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
-}
+#ifndef GIT_OPENSSL_DYNAMIC
+ return openssl_init();
+#else
+ if (git_mutex_init(&openssl_mutex) != 0)
+ return -1;
+
+ return 0;
#endif
+}
-int git_openssl_set_locking(void)
+static int openssl_ensure_initialized(void)
{
-#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
- int num_locks, i;
+#ifdef GIT_OPENSSL_DYNAMIC
+ int error = 0;
- CRYPTO_THREADID_set_callback(threadid_cb);
+ if (git_mutex_lock(&openssl_mutex) != 0)
+ return -1;
- num_locks = CRYPTO_num_locks();
- openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
- GIT_ERROR_CHECK_ALLOC(openssl_locks);
+ if (!openssl_initialized) {
+ if ((error = git_openssl_stream_dynamic_init()) == 0)
+ error = openssl_init();
- for (i = 0; i < num_locks; i++) {
- if (git_mutex_init(&openssl_locks[i]) != 0) {
- git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
- return -1;
- }
+ openssl_initialized = true;
}
- CRYPTO_set_locking_callback(openssl_locking_function);
- return git_runtime_shutdown_register(shutdown_ssl_locking);
+ error |= git_mutex_unlock(&openssl_mutex);
+ return error;
-#elif !defined(OPENSSL_LEGACY_API)
- return 0;
#else
+ return 0;
+#endif
+}
+
+#if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+int git_openssl_set_locking(void)
+{
+# ifdef GIT_THREADS
+ return 0;
+# else
git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
return -1;
-#endif
+# endif
}
+#endif
static int bio_create(BIO *b)
@@ -794,6 +682,9 @@ static int openssl_stream_wrap(
int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host)
{
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
return openssl_stream_wrap(out, in, host, 0);
}
@@ -806,6 +697,9 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
if ((error = git_socket_stream_new(&stream, host, port)) < 0)
return error;
@@ -819,6 +713,9 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
int git_openssl__set_cert_location(const char *file, const char *path)
{
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
char errmsg[256];
diff --git a/src/streams/openssl.h b/src/streams/openssl.h
index 826d1efbc..89fb60a82 100644
--- a/src/streams/openssl.h
+++ b/src/streams/openssl.h
@@ -8,14 +8,22 @@
#define INCLUDE_streams_openssl_h__
#include "common.h"
+#include "streams/openssl_legacy.h"
+#include "streams/openssl_dynamic.h"
#include "git2/sys/stream.h"
extern int git_openssl_stream_global_init(void);
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+# endif
+
#ifdef GIT_OPENSSL
extern int git_openssl__set_cert_location(const char *file, const char *path);
-
extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
extern int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host);
#endif
diff --git a/src/streams/openssl_dynamic.c b/src/streams/openssl_dynamic.c
new file mode 100644
index 000000000..da16b6ed7
--- /dev/null
+++ b/src/streams/openssl_dynamic.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_dynamic.h"
+
+#if defined(GIT_OPENSSL) && defined(GIT_OPENSSL_DYNAMIC)
+
+#include "runtime.h"
+
+#include <dlfcn.h>
+
+unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x);
+const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x);
+int (*ASN1_STRING_length)(const ASN1_STRING *x);
+int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in);
+int (*ASN1_STRING_type)(const ASN1_STRING *x);
+
+void *(*BIO_get_data)(BIO *a);
+int (*BIO_get_new_index)(void);
+int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings);
+void (*BIO_meth_free)(BIO_METHOD *biom);
+int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *));
+int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *));
+int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+BIO_METHOD *(*BIO_meth_new)(int type, const char *name);
+BIO *(*BIO_new)(const BIO_METHOD *type);
+void (*BIO_set_data)(BIO *a, void *ptr);
+void (*BIO_set_init)(BIO *a, int init);
+
+void (*CRYPTO_free)(void *ptr, const char *file, int line);
+void *(*CRYPTO_malloc)(size_t num, const char *file, int line);
+int (*CRYPTO_num_locks)(void);
+void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line));
+int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem));
+int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id));
+void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val);
+
+char *(*ERR_error_string)(unsigned long e, char *buf);
+void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len);
+unsigned long (*ERR_get_error)(void);
+
+int (*SSL_connect)(SSL *ssl);
+long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg);
+void (*SSL_free)(SSL *ssl);
+int (*SSL_get_error)(SSL *ssl, int ret);
+X509 *(*SSL_get_peer_certificate)(const SSL *ssl);
+long (*SSL_get_verify_result)(const SSL *ssl);
+int (*SSL_library_init)(void);
+void (*SSL_load_error_strings)(void);
+SSL *(*SSL_new)(SSL_CTX *ctx);
+int (*SSL_read)(SSL *ssl, const void *buf, int num);
+void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio);
+int (*SSL_shutdown)(SSL *ssl);
+int (*SSL_write)(SSL *ssl, const void *buf, int num);
+
+long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
+void (*SSL_CTX_free)(SSL_CTX *ctx);
+SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method);
+int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
+int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx);
+long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options);
+void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
+int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+
+const SSL_METHOD *(*SSLv23_method)(void);
+const SSL_METHOD *(*TLS_method)(void);
+
+ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne);
+X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc);
+int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos);
+void (*X509_free)(X509 *a);
+void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx);
+X509_NAME *(*X509_get_subject_name)(const X509 *x);
+
+int (*i2d_X509)(X509 *a, unsigned char **ppout);
+
+int (*OPENSSL_sk_num)(const void *sk);
+void *(*OPENSSL_sk_value)(const void *sk, int i);
+void (*OPENSSL_sk_free)(void *sk);
+
+int (*sk_num)(const void *sk);
+void *(*sk_value)(const void *sk, int i);
+void (*sk_free)(void *sk);
+
+void *openssl_handle;
+
+GIT_INLINE(void *) openssl_sym(int *err, const char *name, bool required)
+{
+ void *symbol;
+
+ /* if we've seen an err, noop to retain it */
+ if (*err)
+ return NULL;
+
+
+ if ((symbol = dlsym(openssl_handle, name)) == NULL && required) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load ssl function '%s': %s", name, msg ? msg : "unknown error");
+ *err = -1;
+ }
+
+ return symbol;
+}
+
+static void dynamic_shutdown(void)
+{
+ dlclose(openssl_handle);
+ openssl_handle = NULL;
+}
+
+int git_openssl_stream_dynamic_init(void)
+{
+ int err = 0;
+
+ if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) {
+ git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
+ return -1;
+ }
+
+ ASN1_STRING_data = (unsigned char *(*)(ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_data", false);
+ ASN1_STRING_get0_data = (const unsigned char *(*)(const ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_get0_data", false);
+ ASN1_STRING_length = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_length", true);
+ ASN1_STRING_to_UTF8 = (int (*)(unsigned char **, const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_to_UTF8", true);
+ ASN1_STRING_type = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_type", true);
+
+ BIO_get_data = (void *(*)(BIO *))openssl_sym(&err, "BIO_get_data", false);
+ BIO_get_new_index = (int (*)(void))openssl_sym(&err, "BIO_get_new_index", false);
+ BIO_meth_free = (void (*)(BIO_METHOD *))openssl_sym(&err, "BIO_meth_free", false);
+ BIO_meth_new = (BIO_METHOD *(*)(int, const char *))openssl_sym(&err, "BIO_meth_new", false);
+ BIO_meth_set_create = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_create", false);
+ BIO_meth_set_ctrl = (int (*)(BIO_METHOD *, long (*)(BIO *, int, long, void *)))openssl_sym(&err, "BIO_meth_set_ctrl", false);
+ BIO_meth_set_destroy = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_destroy", false);
+ BIO_meth_set_gets = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_gets", false);
+ BIO_meth_set_puts = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *)))openssl_sym(&err, "BIO_meth_set_puts", false);
+ BIO_meth_set_read = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_read", false);
+ BIO_meth_set_write = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *, int)))openssl_sym(&err, "BIO_meth_set_write", false);
+ BIO_new = (BIO *(*)(const BIO_METHOD *))openssl_sym(&err, "BIO_new", true);
+ BIO_set_data = (void (*)(BIO *a, void *))openssl_sym(&err, "BIO_set_data", false);
+ BIO_set_init = (void (*)(BIO *a, int))openssl_sym(&err, "BIO_set_init", false);
+
+ CRYPTO_free = (void (*)(void *, const char *, int))openssl_sym(&err, "CRYPTO_free", true);
+ CRYPTO_malloc = (void *(*)(size_t, const char *, int))openssl_sym(&err, "CRYPTO_malloc", true);
+ CRYPTO_num_locks = (int (*)(void))openssl_sym(&err, "CRYPTO_num_locks", false);
+ CRYPTO_set_locking_callback = (void (*)(void (*)(int, int, const char *, int)))openssl_sym(&err, "CRYPTO_set_locking_callback", false);
+ CRYPTO_set_mem_functions = (int (*)(void *(*)(size_t), void *(*)(void *, size_t), void (*f)(void *)))openssl_sym(&err, "CRYPTO_set_mem_functions", true);
+
+ CRYPTO_THREADID_set_callback = (int (*)(void (*)(CRYPTO_THREADID *)))openssl_sym(&err, "CRYPTO_THREADID_set_callback", false);
+ CRYPTO_THREADID_set_numeric = (void (*)(CRYPTO_THREADID *, unsigned long))openssl_sym(&err, "CRYPTO_THREADID_set_numeric", false);
+
+ ERR_error_string = (char *(*)(unsigned long, char *))openssl_sym(&err, "ERR_error_string", true);
+ ERR_error_string_n = (void (*)(unsigned long, char *, size_t))openssl_sym(&err, "ERR_error_string_n", true);
+ ERR_get_error = (unsigned long (*)(void))openssl_sym(&err, "ERR_get_error", true);
+
+ OPENSSL_init_ssl = (int (*)(uint64_t opts, const void *settings))openssl_sym(&err, "OPENSSL_init_ssl", false);
+ OPENSSL_sk_num = (int (*)(const void *))openssl_sym(&err, "OPENSSL_sk_num", false);
+ OPENSSL_sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "OPENSSL_sk_value", false);
+ OPENSSL_sk_free = (void (*)(void *))openssl_sym(&err, "OPENSSL_sk_free", false);
+
+ sk_num = (int (*)(const void *))openssl_sym(&err, "sk_num", false);
+ sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "sk_value", false);
+ sk_free = (void (*)(void *))openssl_sym(&err, "sk_free", false);
+
+ SSL_connect = (int (*)(SSL *))openssl_sym(&err, "SSL_connect", true);
+ SSL_ctrl = (long (*)(SSL *, int, long, void *))openssl_sym(&err, "SSL_ctrl", true);
+ SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", true);
+ SSL_library_init = (int (*)(void))openssl_sym(&err, "SSL_library_init", false);
+ SSL_free = (void (*)(SSL *))openssl_sym(&err, "SSL_free", true);
+ SSL_get_error = (int (*)(SSL *, int))openssl_sym(&err, "SSL_get_error", true);
+ SSL_get_verify_result = (long (*)(const SSL *ssl))openssl_sym(&err, "SSL_get_verify_result", true);
+ SSL_load_error_strings = (void (*)(void))openssl_sym(&err, "SSL_load_error_strings", false);
+ SSL_new = (SSL *(*)(SSL_CTX *))openssl_sym(&err, "SSL_new", true);
+ SSL_read = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_read", true);
+ SSL_set_bio = (void (*)(SSL *, BIO *, BIO *))openssl_sym(&err, "SSL_set_bio", true);
+ SSL_shutdown = (int (*)(SSL *ssl))openssl_sym(&err, "SSL_shutdown", true);
+ SSL_write = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_write", true);
+
+ SSL_CTX_ctrl = (long (*)(SSL_CTX *, int, long, void *))openssl_sym(&err, "SSL_CTX_ctrl", true);
+ SSL_CTX_free = (void (*)(SSL_CTX *))openssl_sym(&err, "SSL_CTX_free", true);
+ SSL_CTX_new = (SSL_CTX *(*)(const SSL_METHOD *))openssl_sym(&err, "SSL_CTX_new", true);
+ SSL_CTX_set_cipher_list = (int (*)(SSL_CTX *, const char *))openssl_sym(&err, "SSL_CTX_set_cipher_list", true);
+ SSL_CTX_set_default_verify_paths = (int (*)(SSL_CTX *ctx))openssl_sym(&err, "SSL_CTX_set_default_verify_paths", true);
+ SSL_CTX_set_options = (long (*)(SSL_CTX *, long))openssl_sym(&err, "SSL_CTX_set_options", false);
+ SSL_CTX_set_verify = (void (*)(SSL_CTX *, int, int (*)(int, X509_STORE_CTX *)))openssl_sym(&err, "SSL_CTX_set_verify", true);
+ SSL_CTX_load_verify_locations = (int (*)(SSL_CTX *, const char *, const char *))openssl_sym(&err, "SSL_CTX_load_verify_locations", true);
+
+ SSLv23_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "SSLv23_method", false);
+ TLS_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "TLS_method", false);
+
+ X509_NAME_ENTRY_get_data = (ASN1_STRING *(*)(const X509_NAME_ENTRY *))openssl_sym(&err, "X509_NAME_ENTRY_get_data", true);
+ X509_NAME_get_entry = (X509_NAME_ENTRY *(*)(X509_NAME *, int))openssl_sym(&err, "X509_NAME_get_entry", true);
+ X509_NAME_get_index_by_NID = (int (*)(X509_NAME *, int, int))openssl_sym(&err, "X509_NAME_get_index_by_NID", true);
+ X509_free = (void (*)(X509 *))openssl_sym(&err, "X509_free", true);
+ X509_get_ext_d2i = (void *(*)(const X509 *x, int nid, int *crit, int *idx))openssl_sym(&err, "X509_get_ext_d2i", true);
+ X509_get_subject_name = (X509_NAME *(*)(const X509 *))openssl_sym(&err, "X509_get_subject_name", true);
+
+ i2d_X509 = (int (*)(X509 *a, unsigned char **ppout))openssl_sym(&err, "i2d_X509", true);
+
+ if (err)
+ goto on_error;
+
+ /* Add legacy functionality */
+ if (!OPENSSL_init_ssl) {
+ OPENSSL_init_ssl = OPENSSL_init_ssl__legacy;
+
+ if (!SSL_library_init ||
+ !SSL_load_error_strings ||
+ !CRYPTO_num_locks ||
+ !CRYPTO_set_locking_callback ||
+ !CRYPTO_THREADID_set_callback ||
+ !CRYPTO_THREADID_set_numeric) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl initialization functions");
+ goto on_error;
+ }
+ }
+
+ if (!SSL_CTX_set_options)
+ SSL_CTX_set_options = SSL_CTX_set_options__legacy;
+
+ if (TLS_method)
+ SSLv23_method = TLS_method;
+
+ if (!BIO_meth_new) {
+ BIO_meth_new = BIO_meth_new__legacy;
+ BIO_meth_new = BIO_meth_new__legacy;
+ BIO_meth_free = BIO_meth_free__legacy;
+ BIO_meth_set_write = BIO_meth_set_write__legacy;
+ BIO_meth_set_read = BIO_meth_set_read__legacy;
+ BIO_meth_set_puts = BIO_meth_set_puts__legacy;
+ BIO_meth_set_gets = BIO_meth_set_gets__legacy;
+ BIO_meth_set_ctrl = BIO_meth_set_ctrl__legacy;
+ BIO_meth_set_create = BIO_meth_set_create__legacy;
+ BIO_meth_set_destroy = BIO_meth_set_destroy__legacy;
+ BIO_get_new_index = BIO_get_new_index__legacy;
+ BIO_set_data = BIO_set_data__legacy;
+ BIO_set_init = BIO_set_init__legacy;
+ BIO_get_data = BIO_get_data__legacy;
+ }
+
+ if (!ASN1_STRING_get0_data) {
+ if (!ASN1_STRING_data) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl string function");
+ goto on_error;
+ }
+
+ ASN1_STRING_get0_data = ASN1_STRING_get0_data__legacy;
+ }
+
+ if ((!OPENSSL_sk_num && !sk_num) ||
+ (!OPENSSL_sk_value && !sk_value) ||
+ (!OPENSSL_sk_free && !sk_free)) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl stack functions");
+ goto on_error;
+ }
+
+ if (git_runtime_shutdown_register(dynamic_shutdown) != 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ dlclose(openssl_handle);
+ return -1;
+}
+
+
+int sk_GENERAL_NAME_num(const GENERAL_NAME *sk)
+{
+ if (OPENSSL_sk_num)
+ return OPENSSL_sk_num(sk);
+ else if (sk_num)
+ return sk_num(sk);
+
+ GIT_ASSERT_WITH_RETVAL(false, 0);
+ return 0;
+}
+
+GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i)
+{
+ if (OPENSSL_sk_value)
+ return OPENSSL_sk_value(sk, i);
+ else if (sk_value)
+ return sk_value(sk, i);
+
+ GIT_ASSERT_WITH_RETVAL(false, NULL);
+ return NULL;
+}
+
+void GENERAL_NAMES_free(GENERAL_NAME *sk)
+{
+ if (OPENSSL_sk_free)
+ OPENSSL_sk_free(sk);
+ else if (sk_free)
+ sk_free(sk);
+}
+
+#endif /* GIT_OPENSSL && GIT_OPENSSL_DYNAMIC */
diff --git a/src/streams/openssl_dynamic.h b/src/streams/openssl_dynamic.h
new file mode 100644
index 000000000..12d927a61
--- /dev/null
+++ b/src/streams/openssl_dynamic.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2007 The OpenSSL Project. 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. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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 product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECC cipher suite support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+/* ====================================================================
+ * Copyright 2005 Nokia. All rights reserved.
+ *
+ * The portions of the attached software ("Contribution") is developed by
+ * Nokia Corporation and is licensed pursuant to the OpenSSL open source
+ * license.
+ *
+ * The Contribution, originally written by Mika Kousa and Pasi Eronen of
+ * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites
+ * support (see RFC 4279) to OpenSSL.
+ *
+ * No patent licenses or other rights except those expressly stated in
+ * the OpenSSL open source license shall be deemed granted or received
+ * expressly, by implication, estoppel, or otherwise.
+ *
+ * No assurances are provided by Nokia that the Contribution does not
+ * infringe the patent or other intellectual property rights of any third
+ * party or that the license provides you with all the necessary rights
+ * to make use of the Contribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN
+ * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA
+ * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY
+ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
+ * OTHERWISE.
+ */
+
+#ifndef INCLUDE_streams_openssl_dynamic_h__
+#define INCLUDE_streams_openssl_dynamic_h__
+
+#ifdef GIT_OPENSSL_DYNAMIC
+
+# define BIO_CTRL_FLUSH 11
+
+# define BIO_TYPE_SOURCE_SINK 0x0400
+
+# define CRYPTO_LOCK 1
+
+# define GEN_DNS 2
+# define GEN_IPADD 7
+
+# define NID_commonName 13
+# define NID_subject_alt_name 85
+
+# define SSL_VERIFY_NONE 0x00
+
+# define SSL_CTRL_OPTIONS 32
+# define SSL_CTRL_MODE 33
+# define SSL_CTRL_SET_TLSEXT_HOSTNAME 55
+
+# define SSL_ERROR_NONE 0
+# define SSL_ERROR_SSL 1
+# define SSL_ERROR_WANT_READ 2
+# define SSL_ERROR_WANT_WRITE 3
+# define SSL_ERROR_WANT_X509_LOOKUP 4
+# define SSL_ERROR_SYSCALL 5
+# define SSL_ERROR_ZERO_RETURN 6
+# define SSL_ERROR_WANT_CONNECT 7
+# define SSL_ERROR_WANT_ACCEPT 8
+
+# define SSL_OP_NO_COMPRESSION 0x00020000L
+# define SSL_OP_NO_SSLv2 0x01000000L
+# define SSL_OP_NO_SSLv3 0x02000000L
+
+# define SSL_MODE_AUTO_RETRY 0x00000004L
+
+# define TLSEXT_NAMETYPE_host_name 0
+
+# define V_ASN1_UTF8STRING 12
+
+# define X509_V_OK 0
+
+/* Most of the OpenSSL types are mercifully opaque, so we can treat them like `void *` */
+typedef struct bio_st BIO;
+typedef struct bio_method_st BIO_METHOD;
+typedef void bio_info_cb;
+typedef void * CRYPTO_EX_DATA;
+typedef void CRYPTO_THREADID;
+typedef void GENERAL_NAMES;
+typedef void SSL;
+typedef void SSL_CTX;
+typedef void SSL_METHOD;
+typedef void X509;
+typedef void X509_NAME;
+typedef void X509_NAME_ENTRY;
+typedef void X509_STORE_CTX;
+
+typedef struct {
+ int length;
+ int type;
+ unsigned char *data;
+ long flags;
+} ASN1_STRING;
+
+typedef struct {
+ int type;
+ union {
+ char *ptr;
+ ASN1_STRING *ia5;
+ } d;
+} GENERAL_NAME;
+
+struct bio_st {
+ BIO_METHOD *method;
+ /* bio, mode, argp, argi, argl, ret */
+ long (*callback) (struct bio_st *, int, const char *, int, long, long);
+ char *cb_arg; /* first argument for the callback */
+ int init;
+ int shutdown;
+ int flags; /* extra storage */
+ int retry_reason;
+ int num;
+ void *ptr;
+ struct bio_st *next_bio; /* used by filter BIOs */
+ struct bio_st *prev_bio; /* used by filter BIOs */
+ int references;
+ unsigned long num_read;
+ unsigned long num_write;
+ CRYPTO_EX_DATA ex_data;
+};
+
+struct bio_method_st {
+ int type;
+ const char *name;
+ int (*bwrite) (BIO *, const char *, int);
+ int (*bread) (BIO *, char *, int);
+ int (*bputs) (BIO *, const char *);
+ int (*bgets) (BIO *, char *, int);
+ long (*ctrl) (BIO *, int, long, void *);
+ int (*create) (BIO *);
+ int (*destroy) (BIO *);
+ long (*callback_ctrl) (BIO *, int, bio_info_cb *);
+};
+
+extern unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x);
+extern const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x);
+extern int (*ASN1_STRING_length)(const ASN1_STRING *x);
+extern int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in);
+extern int (*ASN1_STRING_type)(const ASN1_STRING *x);
+
+extern void *(*BIO_get_data)(BIO *a);
+extern int (*BIO_get_new_index)(void);
+extern int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings);
+extern void (*BIO_meth_free)(BIO_METHOD *biom);
+extern int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *));
+extern int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+extern int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *));
+extern int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+extern int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+extern int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+extern int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+extern BIO_METHOD *(*BIO_meth_new)(int type, const char *name);
+extern BIO *(*BIO_new)(const BIO_METHOD *type);
+extern void (*BIO_set_data)(BIO *a, void *ptr);
+extern void (*BIO_set_init)(BIO *a, int init);
+
+extern void (*CRYPTO_free)(void *ptr, const char *file, int line);
+extern void *(*CRYPTO_malloc)(size_t num, const char *file, int line);
+extern int (*CRYPTO_num_locks)(void);
+extern void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line));
+extern int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem));
+extern int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id));
+extern void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val);
+
+extern char *(*ERR_error_string)(unsigned long e, char *buf);
+extern void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len);
+extern unsigned long (*ERR_get_error)(void);
+
+# define OPENSSL_malloc(num) CRYPTO_malloc(num, __FILE__, __LINE__)
+# define OPENSSL_free(addr) CRYPTO_free(addr, __FILE__, __LINE__)
+
+extern int (*SSL_connect)(SSL *ssl);
+extern long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg);
+extern void (*SSL_free)(SSL *ssl);
+extern int (*SSL_get_error)(SSL *ssl, int ret);
+extern X509 *(*SSL_get_peer_certificate)(const SSL *ssl);
+extern long (*SSL_get_verify_result)(const SSL *ssl);
+extern int (*SSL_library_init)(void);
+extern void (*SSL_load_error_strings)(void);
+extern SSL *(*SSL_new)(SSL_CTX *ctx);
+extern int (*SSL_read)(SSL *ssl, const void *buf, int num);
+extern void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio);
+extern int (*SSL_shutdown)(SSL *ssl);
+extern int (*SSL_write)(SSL *ssl, const void *buf, int num);
+
+# define SSL_set_tlsext_host_name(s, name) SSL_ctrl((s), SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (char *)(name));
+
+extern long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
+extern void (*SSL_CTX_free)(SSL_CTX *ctx);
+extern SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method);
+extern int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
+extern int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx);
+extern long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options);
+extern void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
+extern int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+
+# define SSL_CTX_set_mode(ctx, mode) SSL_CTX_ctrl((ctx), SSL_CTRL_MODE, (mode), NULL);
+
+extern const SSL_METHOD *(*SSLv23_method)(void);
+extern const SSL_METHOD *(*TLS_method)(void);
+
+extern ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne);
+extern X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc);
+extern int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos);
+extern void (*X509_free)(X509 *a);
+extern void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx);
+extern X509_NAME *(*X509_get_subject_name)(const X509 *x);
+
+extern int (*i2d_X509)(X509 *a, unsigned char **ppout);
+
+extern int (*OPENSSL_sk_num)(const void *sk);
+extern void *(*OPENSSL_sk_value)(const void *sk, int i);
+extern void (*OPENSSL_sk_free)(void *sk);
+
+extern int (*sk_num)(const void *sk);
+extern void *(*sk_value)(const void *sk, int i);
+extern void (*sk_free)(void *sk);
+
+extern int sk_GENERAL_NAME_num(const GENERAL_NAME *sk);
+extern GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i);
+extern void GENERAL_NAMES_free(GENERAL_NAME *sk);
+
+extern int git_openssl_stream_dynamic_init(void);
+
+#endif /* GIT_OPENSSL_DYNAMIC */
+
+#endif
diff --git a/src/streams/openssl_legacy.c b/src/streams/openssl_legacy.c
new file mode 100644
index 000000000..1c1ff60c0
--- /dev/null
+++ b/src/streams/openssl_legacy.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_legacy.h"
+
+#include "runtime.h"
+#include "git2/sys/openssl.h"
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC)
+
+/*
+ * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
+ * which do not exist in previous versions. We define these inline functions so
+ * we can program against the interface instead of littering the implementation
+ * with ifdefs. We do the same for OPENSSL_init_ssl.
+ */
+
+int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings)
+{
+ GIT_UNUSED(opts);
+ GIT_UNUSED(settings);
+ SSL_load_error_strings();
+ SSL_library_init();
+ return 0;
+}
+
+BIO_METHOD* BIO_meth_new__legacy(int type, const char *name)
+{
+ BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
+ if (!meth) {
+ return NULL;
+ }
+
+ meth->type = type;
+ meth->name = name;
+
+ return meth;
+}
+
+void BIO_meth_free__legacy(BIO_METHOD *biom)
+{
+ git__free(biom);
+}
+
+int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+{
+ biom->bwrite = write;
+ return 1;
+}
+
+int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+{
+ biom->bread = read;
+ return 1;
+}
+
+int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+{
+ biom->bputs = puts;
+ return 1;
+}
+
+int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+
+{
+ biom->bgets = gets;
+ return 1;
+}
+
+int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+{
+ biom->ctrl = ctrl;
+ return 1;
+}
+
+int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *))
+{
+ biom->create = create;
+ return 1;
+}
+
+int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+ biom->destroy = destroy;
+ return 1;
+}
+
+int BIO_get_new_index__legacy(void)
+{
+ /* This exists as of 1.1 so before we'd just have 0 */
+ return 0;
+}
+
+void BIO_set_init__legacy(BIO *b, int init)
+{
+ b->init = init;
+}
+
+void BIO_set_data__legacy(BIO *a, void *ptr)
+{
+ a->ptr = ptr;
+}
+
+void *BIO_get_data__legacy(BIO *a)
+{
+ return a->ptr;
+}
+
+const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *)x);
+}
+
+long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op)
+{
+ return SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, op, NULL);
+}
+
+# if defined(GIT_THREADS)
+static git_mutex *openssl_locks;
+
+static void openssl_locking_function(int mode, int n, const char *file, int line)
+{
+ int lock;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ lock = mode & CRYPTO_LOCK;
+
+ if (lock)
+ (void)git_mutex_lock(&openssl_locks[n]);
+ else
+ git_mutex_unlock(&openssl_locks[n]);
+}
+
+static void shutdown_ssl_locking(void)
+{
+ int num_locks, i;
+
+ num_locks = CRYPTO_num_locks();
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i = 0; i < num_locks; ++i)
+ git_mutex_free(&openssl_locks[i]);
+ git__free(openssl_locks);
+}
+
+static void threadid_cb(CRYPTO_THREADID *threadid)
+{
+ GIT_UNUSED(threadid);
+ CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
+}
+
+int git_openssl_set_locking(void)
+{
+ int num_locks, i;
+
+#ifndef GIT_THREADS
+ git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
+ return -1;
+#endif
+
+#ifdef GIT_OPENSSL_DYNAMIC
+ /*
+ * This function is required on legacy versions of OpenSSL; when building
+ * with dynamically-loaded OpenSSL, we detect whether we loaded it or not.
+ */
+ if (!CRYPTO_set_locking_callback)
+ return 0;
+#endif
+
+ CRYPTO_THREADID_set_callback(threadid_cb);
+
+ num_locks = CRYPTO_num_locks();
+ openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
+ GIT_ERROR_CHECK_ALLOC(openssl_locks);
+
+ for (i = 0; i < num_locks; i++) {
+ if (git_mutex_init(&openssl_locks[i]) != 0) {
+ git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
+ return -1;
+ }
+ }
+
+ CRYPTO_set_locking_callback(openssl_locking_function);
+ return git_runtime_shutdown_register(shutdown_ssl_locking);
+}
+#endif /* GIT_THREADS */
+
+#endif /* GIT_OPENSSL_LEGACY || GIT_OPENSSL_DYNAMIC */
diff --git a/src/streams/openssl_legacy.h b/src/streams/openssl_legacy.h
new file mode 100644
index 000000000..1f74fd95a
--- /dev/null
+++ b/src/streams/openssl_legacy.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_openssl_legacy_h__
+#define INCLUDE_streams_openssl_legacy_h__
+
+#include "streams/openssl_dynamic.h"
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+
+# if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
+# define GIT_OPENSSL_LEGACY
+# endif
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+# define OPENSSL_init_ssl OPENSSL_init_ssl__legacy
+# define BIO_meth_new BIO_meth_new__legacy
+# define BIO_meth_free BIO_meth_free__legacy
+# define BIO_meth_set_write BIO_meth_set_write__legacy
+# define BIO_meth_set_read BIO_meth_set_read__legacy
+# define BIO_meth_set_puts BIO_meth_set_puts__legacy
+# define BIO_meth_set_gets BIO_meth_set_gets__legacy
+# define BIO_meth_set_ctrl BIO_meth_set_ctrl__legacy
+# define BIO_meth_set_create BIO_meth_set_create__legacy
+# define BIO_meth_set_destroy BIO_meth_set_destroy__legacy
+# define BIO_get_new_index BIO_get_new_index__legacy
+# define BIO_set_data BIO_set_data__legacy
+# define BIO_set_init BIO_set_init__legacy
+# define BIO_get_data BIO_get_data__legacy
+# define ASN1_STRING_get0_data ASN1_STRING_get0_data__legacy
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC)
+
+extern int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings);
+extern BIO_METHOD* BIO_meth_new__legacy(int type, const char *name);
+extern void BIO_meth_free__legacy(BIO_METHOD *biom);
+extern int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+extern int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+extern int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+extern int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+extern int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+extern int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *));
+extern int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *));
+extern int BIO_get_new_index__legacy(void);
+extern void BIO_set_data__legacy(BIO *a, void *ptr);
+extern void BIO_set_init__legacy(BIO *b, int init);
+extern void *BIO_get_data__legacy(BIO *a);
+extern const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x);
+extern long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op);
+
+#endif
+
+#endif
diff --git a/src/transports/auth_ntlm.c b/src/transports/auth_ntlm.c
index e0960bf9d..b5110616b 100644
--- a/src/transports/auth_ntlm.c
+++ b/src/transports/auth_ntlm.c
@@ -14,7 +14,7 @@
#ifdef GIT_NTLM
-#include "ntlm.h"
+#include "ntlmclient.h"
typedef struct {
git_http_auth_context parent;
diff --git a/src/transports/http.c b/src/transports/http.c
index 4538dd143..9871be5ad 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -104,6 +104,11 @@ static int apply_url_credentials(
const char *username,
const char *password)
{
+ GIT_ASSERT_ARG(username);
+
+ if (!password)
+ password = "";
+
if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
return git_credential_userpass_plaintext_new(cred, username, password);
@@ -138,8 +143,7 @@ static int handle_auth(
/* Start with URL-specified credentials, if there were any. */
if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) &&
!server->url_cred_presented &&
- server->url.username &&
- server->url.password) {
+ server->url.username) {
error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password);
server->url_cred_presented = 1;
diff --git a/src/tree.c b/src/tree.c
index 76821e3a0..b1df79eac 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -1251,8 +1251,9 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli
}
case GIT_TREE_UPDATE_REMOVE:
{
+ tree_stack_entry *last = git_array_last(stack);
char *basename = git_path_basename(update->path);
- error = git_treebuilder_remove(git_array_last(stack)->bld, basename);
+ error = git_treebuilder_remove(last->bld, basename);
git__free(basename);
break;
}
diff --git a/src/vector.h b/src/vector.h
index cc4c314d5..3dcec3d13 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -26,11 +26,13 @@ typedef struct git_vector {
#define GIT_VECTOR_INIT {0}
-int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp);
+GIT_WARN_UNUSED_RESULT int git_vector_init(
+ git_vector *v, size_t initial_size, git_vector_cmp cmp);
void git_vector_free(git_vector *v);
void git_vector_free_deep(git_vector *v); /* free each entry and self */
void git_vector_clear(git_vector *v);
-int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp);
+GIT_WARN_UNUSED_RESULT int git_vector_dup(
+ git_vector *v, const git_vector *src, git_vector_cmp cmp);
void git_vector_swap(git_vector *a, git_vector *b);
int git_vector_size_hint(git_vector *v, size_t size_hint);
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index 581673b1b..e6640c85e 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -381,14 +381,14 @@ int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path)
switch (reparse_buf->ReparseTag) {
case IO_REPARSE_TAG_SYMLINK:
- target = reparse_buf->SymbolicLinkReparseBuffer.PathBuffer +
- (reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
- target_len = reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
+ target = reparse_buf->ReparseBuffer.SymbolicLink.PathBuffer +
+ (reparse_buf->ReparseBuffer.SymbolicLink.SubstituteNameOffset / sizeof(WCHAR));
+ target_len = reparse_buf->ReparseBuffer.SymbolicLink.SubstituteNameLength / sizeof(WCHAR);
break;
case IO_REPARSE_TAG_MOUNT_POINT:
- target = reparse_buf->MountPointReparseBuffer.PathBuffer +
- (reparse_buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
- target_len = reparse_buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
+ target = reparse_buf->ReparseBuffer.MountPoint.PathBuffer +
+ (reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset / sizeof(WCHAR));
+ target_len = reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength / sizeof(WCHAR);
break;
default:
errno = EINVAL;
diff --git a/src/win32/path_w32.h b/src/win32/path_w32.h
index dab8b96fa..4fadf8d08 100644
--- a/src/win32/path_w32.h
+++ b/src/win32/path_w32.h
@@ -8,7 +8,6 @@
#define INCLUDE_win32_path_w32_h__
#include "common.h"
-#include "vector.h"
/**
* Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 0a8f2bee0..7fcc472e9 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -543,6 +543,13 @@ int p_open(const char *path, int flags, ...)
mode_t mode = 0;
struct open_opts opts = {0};
+ #ifdef GIT_DEBUG_STRICT_OPEN
+ if (strstr(path, "//") != NULL) {
+ errno = EACCES;
+ return -1;
+ }
+ #endif
+
if (git_win32_path_from_utf8(wpath, path) < 0)
return -1;
diff --git a/src/win32/reparse.h b/src/win32/reparse.h
index 5f7408a1b..23312319f 100644
--- a/src/win32/reparse.h
+++ b/src/win32/reparse.h
@@ -26,18 +26,18 @@ typedef struct _GIT_REPARSE_DATA_BUFFER {
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
- } SymbolicLinkReparseBuffer;
+ } SymbolicLink;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
- } MountPointReparseBuffer;
+ } MountPoint;
struct {
UCHAR DataBuffer[1];
- } GenericReparseBuffer;
- };
+ } Generic;
+ } ReparseBuffer;
} GIT_REPARSE_DATA_BUFFER;
#define REPARSE_DATA_HEADER_SIZE 8
diff --git a/src/worktree.c b/src/worktree.c
index 0bced6d4a..fe8db7743 100644
--- a/src/worktree.c
+++ b/src/worktree.c
@@ -43,7 +43,7 @@ int git_worktree_list(git_strarray *wts, git_repository *repo)
wts->count = 0;
wts->strings = NULL;
- if ((error = git_buf_printf(&path, "%s/worktrees/", repo->commondir)) < 0)
+ if ((error = git_buf_joinpath(&path, repo->commondir, "worktrees/")) < 0)
goto exit;
if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr))
goto exit;
@@ -182,7 +182,7 @@ int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *na
*out = NULL;
- if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0)
+ if ((error = git_buf_join3(&path, '/', repo->commondir, "worktrees", name)) < 0)
goto out;
if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0)
@@ -592,7 +592,7 @@ int git_worktree_prune(git_worktree *wt,
}
/* Delete gitdir in parent repository */
- if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0)
+ if ((err = git_buf_join3(&path, '/', wt->commondir_path, "worktrees", wt->name)) < 0)
goto out;
if (!git_path_exists(path.ptr))
{
diff --git a/tests/attr/lookup.c b/tests/attr/lookup.c
index 29a66b001..bfb2e872f 100644
--- a/tests/attr/lookup.c
+++ b/tests/attr/lookup.c
@@ -236,6 +236,7 @@ void test_attr_lookup__check_attr_examples(void)
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
+ git_attr_file_source source = {0};
struct attr_expected cases[] = {
{ "abc", "foo", EXPECT_TRUE, NULL },
@@ -250,7 +251,7 @@ void test_attr_lookup__from_buffer(void)
{ NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file, NULL, 0));
+ cl_git_pass(git_attr_file__new(&file, NULL, &source));
cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", true));
diff --git a/tests/attr/repo.c b/tests/attr/repo.c
index 36beeb095..eabc033eb 100644
--- a/tests/attr/repo.c
+++ b/tests/attr/repo.c
@@ -71,11 +71,11 @@ void test_attr_repo__get_one(void)
}
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_one_start_deep(void)
@@ -92,11 +92,11 @@ void test_attr_repo__get_one_start_deep(void)
}
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_many(void)
@@ -341,7 +341,7 @@ void test_attr_repo__sysdir_with_session(void)
g_repo = cl_git_sandbox_reopen();
cl_git_pass(git_attr_session__init(&session, g_repo));
- cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, 0, "file", ARRAY_SIZE(attrs), attrs));
+ cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs));
cl_assert_equal_s(values[0], "1");
cl_assert_equal_s(values[1], "2");
diff --git a/tests/core/link.c b/tests/core/link.c
index 1e5ed454c..0493edf1d 100644
--- a/tests/core/link.c
+++ b/tests/core/link.c
@@ -123,7 +123,7 @@ static void do_junction(const char *old, const char *new)
reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen);
cl_assert(reparse_buf);
- subst_utf16 = reparse_buf->MountPointReparseBuffer.PathBuffer;
+ subst_utf16 = reparse_buf->ReparseBuffer.MountPoint.PathBuffer;
print_utf16 = subst_utf16 + subst_utf16_len + 1;
ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1,
@@ -135,10 +135,10 @@ static void do_junction(const char *old, const char *new)
cl_assert_equal_i(print_utf16_len, ret);
reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
- reparse_buf->MountPointReparseBuffer.SubstituteNameOffset = 0;
- reparse_buf->MountPointReparseBuffer.SubstituteNameLength = subst_byte_len;
- reparse_buf->MountPointReparseBuffer.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR));
- reparse_buf->MountPointReparseBuffer.PrintNameLength = print_byte_len;
+ reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset = 0;
+ reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength = subst_byte_len;
+ reparse_buf->ReparseBuffer.MountPoint.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR));
+ reparse_buf->ReparseBuffer.MountPoint.PrintNameLength = print_byte_len;
reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE;
cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT,
diff --git a/tests/core/sha1.c b/tests/core/sha1.c
index f81d40854..196b00352 100644
--- a/tests/core/sha1.c
+++ b/tests/core/sha1.c
@@ -52,7 +52,7 @@ void test_core_sha1__detect_collision_attack(void)
git_oid oid, expected;
#ifdef GIT_SHA1_COLLISIONDETECT
- GIT_UNUSED(expected);
+ GIT_UNUSED(&expected);
cl_git_fail(sha1_file(&oid, FIXTURE_DIR "/shattered-1.pdf"));
cl_assert_equal_s("SHA1 collision attack detected", git_error_last()->message);
#else
diff --git a/tests/core/sortedcache.c b/tests/core/sortedcache.c
index 35e92ece9..d5bbcea19 100644
--- a/tests/core/sortedcache.c
+++ b/tests/core/sortedcache.c
@@ -54,7 +54,7 @@ void test_core_sortedcache__name_only(void)
cl_assert_equal_i(
GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc"));
- git_sortedcache_clear(sc, true);
+ cl_git_pass(git_sortedcache_clear(sc, true));
cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
cl_assert(git_sortedcache_entry(sc, 0) == NULL);
@@ -154,7 +154,7 @@ void test_core_sortedcache__in_memory(void)
cl_assert_equal_i(0, free_count);
- git_sortedcache_clear(sc, true);
+ cl_git_pass(git_sortedcache_clear(sc, true));
cl_assert_equal_i(5, free_count);
@@ -247,7 +247,7 @@ static void sortedcache_test_reload(git_sortedcache *sc)
cl_assert(git_sortedcache_lockandload(sc, &buf) > 0);
- git_sortedcache_clear(sc, false); /* clear once we already have lock */
+ cl_git_pass(git_sortedcache_clear(sc, false)); /* clear once we already have lock */
for (scan = buf.ptr; *scan; scan = after + 1) {
int val = strtol(scan, &after, 0);
diff --git a/tests/core/vector.c b/tests/core/vector.c
index a7e1a0325..08cd2c19b 100644
--- a/tests/core/vector.c
+++ b/tests/core/vector.c
@@ -8,7 +8,7 @@ void test_core_vector__0(void)
{
git_vector x;
int i;
- git_vector_init(&x, 1, NULL);
+ cl_git_pass(git_vector_init(&x, 1, NULL));
for (i = 0; i < 10; ++i) {
git_vector_insert(&x, (void*) 0xabc);
}
@@ -21,7 +21,7 @@ void test_core_vector__1(void)
{
git_vector x;
/* make initial capacity exact for our insertions. */
- git_vector_init(&x, 3, NULL);
+ cl_git_pass(git_vector_init(&x, 3, NULL));
git_vector_insert(&x, (void*) 0xabc);
git_vector_insert(&x, (void*) 0xdef);
git_vector_insert(&x, (void*) 0x123);
@@ -76,7 +76,7 @@ void test_core_vector__3(void)
{
git_vector x;
intptr_t i;
- git_vector_init(&x, 1, &compare_them);
+ cl_git_pass(git_vector_init(&x, 1, &compare_them));
for (i = 0; i < 10; i += 2) {
git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
@@ -99,7 +99,7 @@ void test_core_vector__4(void)
{
git_vector x;
intptr_t i;
- git_vector_init(&x, 1, &compare_them);
+ cl_git_pass(git_vector_init(&x, 1, &compare_them));
for (i = 0; i < 10; i += 2) {
git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
@@ -163,7 +163,7 @@ void test_core_vector__5(void)
git_vector x;
int i;
- git_vector_init(&x, 1, &compare_structs);
+ cl_git_pass(git_vector_init(&x, 1, &compare_structs));
for (i = 0; i < 10; i += 2)
git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
@@ -205,7 +205,7 @@ void test_core_vector__remove_matching(void)
size_t i;
void *compare;
- git_vector_init(&x, 1, NULL);
+ cl_git_pass(git_vector_init(&x, 1, NULL));
git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 1);
diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
index 02e7ecfdb..6881af40a 100644
--- a/tests/fetchhead/nonetwork.c
+++ b/tests/fetchhead/nonetwork.c
@@ -82,7 +82,7 @@ void test_fetchhead_nonetwork__write(void)
int equals = 0;
size_t i;
- git_vector_init(&fetchhead_vector, 6, NULL);
+ cl_git_pass(git_vector_init(&fetchhead_vector, 6, NULL));
cl_set_cleanup(&cleanup_repository, "./test1");
cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
diff --git a/tests/filter/bare.c b/tests/filter/bare.c
index 7319b5203..f8e34232f 100644
--- a/tests/filter/bare.c
+++ b/tests/filter/bare.c
@@ -132,3 +132,63 @@ void test_filter_bare__sanitizes(void)
git_blob_free(blob);
}
+void test_filter_bare__from_specific_commit_one(void)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_blob *blob;
+ git_buf buf = { 0 };
+ git_oid commit_id;
+
+ cl_git_pass(git_oid_fromstr(&commit_id, "b8986fec0f7bde90f78ac72706e782d82f24f2f0"));
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT;
+ opts.commit_id = &commit_id;
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "055c872")); /* ident */
+
+ cl_assert_equal_s("$Id$\n", git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "ident.bin", &opts));
+ cl_assert_equal_s("$Id$\n", buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "ident.identlf", &opts));
+ cl_assert_equal_s("$Id: 055c8729cdcc372500a08db659c045e16c4409fb $\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_bare__from_specific_commit_with_no_attributes_file(void)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_blob *blob;
+ git_buf buf = { 0 };
+ git_oid commit_id;
+
+ cl_git_pass(git_oid_fromstr(&commit_id, "5afb6a14a864e30787857dd92af837e8cdd2cb1b"));
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT;
+ opts.commit_id = &commit_id;
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
diff --git a/tests/ignore/status.c b/tests/ignore/status.c
index 188fbe469..83e821bdd 100644
--- a/tests/ignore/status.c
+++ b/tests/ignore/status.c
@@ -70,9 +70,9 @@ void test_ignore_status__0(void)
/* confirm that ignore files were cached */
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/exclude"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/exclude"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitignore"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitignore"));
}
diff --git a/tests/index/addall.c b/tests/index/addall.c
index d1ef31daf..6f95f6386 100644
--- a/tests/index/addall.c
+++ b/tests/index/addall.c
@@ -318,11 +318,9 @@ void test_index_addall__files_in_folders(void)
void test_index_addall__hidden_files(void)
{
+#ifdef GIT_WIN32
git_index *index;
- GIT_UNUSED(index);
-
-#ifdef GIT_WIN32
addall_create_test_repo(true);
cl_git_pass(git_repository_index(&index, g_repo));
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
index 21d3d3ed0..b32a0a789 100644
--- a/tests/index/bypath.c
+++ b/tests/index/bypath.c
@@ -49,13 +49,10 @@ void test_index_bypath__add_submodule_unregistered(void)
void test_index_bypath__add_hidden(void)
{
+#ifdef GIT_WIN32
const git_index_entry *entry;
bool hidden;
- GIT_UNUSED(entry);
- GIT_UNUSED(hidden);
-
-#ifdef GIT_WIN32
cl_git_mkfile("submod2/hidden_file", "you can't see me");
cl_git_pass(git_win32__hidden(&hidden, "submod2/hidden_file"));
diff --git a/tests/iterator/workdir.c b/tests/iterator/workdir.c
index 547fb7d95..82ee363f9 100644
--- a/tests/iterator/workdir.c
+++ b/tests/iterator/workdir.c
@@ -1024,7 +1024,7 @@ static void create_paths(const char *root, int depth)
int i;
cl_git_pass(git_buf_puts(&fullpath, root));
- cl_git_pass(git_buf_putc(&fullpath, '/'));
+ cl_git_pass(git_path_to_dir(&fullpath));
root_len = fullpath.size;
diff --git a/tests/main.c b/tests/main.c
index 207a6a8be..56751c288 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -18,7 +18,9 @@ int main(int argc, char *argv[])
res = git_libgit2_init();
if (res < 0) {
- fprintf(stderr, "failed to init libgit2");
+ const git_error *err = git_error_last();
+ const char *msg = err ? err->message : "unknown failure";
+ fprintf(stderr, "failed to init libgit2: %s\n", msg);
return res;
}
diff --git a/tests/merge/workdir/setup.c b/tests/merge/workdir/setup.c
index ad29fcd94..0b85f2712 100644
--- a/tests/merge/workdir/setup.c
+++ b/tests/merge/workdir/setup.c
@@ -49,7 +49,7 @@ static bool test_file_contents(const char *filename, const char *expected)
git_buf file_path_buf = GIT_BUF_INIT, file_buf = GIT_BUF_INIT;
bool equals;
- git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
+ git_buf_joinpath(&file_path_buf, git_repository_path(repo), filename);
cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr));
equals = (strcmp(file_buf.ptr, expected) == 0);
@@ -64,7 +64,7 @@ static void write_file_contents(const char *filename, const char *output)
{
git_buf file_path_buf = GIT_BUF_INIT;
- git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo),
+ git_buf_joinpath(&file_path_buf, git_repository_path(repo),
filename);
cl_git_rewritefile(file_path_buf.ptr, output);
diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c
index 7a45a57ae..02112380b 100644
--- a/tests/odb/foreach.c
+++ b/tests/odb/foreach.c
@@ -127,7 +127,7 @@ void test_odb_foreach__files_in_objects_dir(void)
cl_fixture_sandbox("testrepo.git");
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
- cl_git_pass(git_buf_printf(&buf, "%s/objects/somefile", git_repository_path(repo)));
+ cl_git_pass(git_buf_joinpath(&buf, git_repository_path(repo), "objects/somefile"));
cl_git_mkfile(buf.ptr, "");
git_buf_dispose(&buf);
diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c
index 6af8b0d1b..e027230fa 100644
--- a/tests/odb/sorting.c
+++ b/tests/odb/sorting.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "git2/sys/odb_backend.h"
+#include "odb.h"
typedef struct {
git_odb_backend base;
@@ -43,6 +44,11 @@ void test_odb_sorting__cleanup(void)
{
git_odb_free(_odb);
_odb = NULL;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY,
+ GIT_ODB_DEFAULT_LOOSE_PRIORITY));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY,
+ GIT_ODB_DEFAULT_PACKED_PRIORITY));
}
void test_odb_sorting__basic_backends_sorting(void)
@@ -68,3 +74,26 @@ void test_odb_sorting__alternate_backends_sorting(void)
check_backend_sorting(_odb);
}
+
+void test_odb_sorting__override_default_backend_priority(void)
+{
+ git_odb *new_odb;
+ git_odb_backend *loose, *packed, *backend;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, 5));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, 3));
+ git_odb_backend_pack(&packed, "./testrepo.git/objects");
+ git_odb_backend_loose(&loose, "./testrepo.git/objects", -1, 0, 0, 0);
+
+ cl_git_pass(git_odb_open(&new_odb, cl_fixture("testrepo.git/objects")));
+ cl_assert_equal_sz(2, git_odb_num_backends(new_odb));
+
+ cl_git_pass(git_odb_get_backend(&backend, new_odb, 0));
+ cl_assert_equal_p(loose->read, backend->read);
+
+ cl_git_pass(git_odb_get_backend(&backend, new_odb, 1));
+ cl_assert_equal_p(packed->read, backend->read);
+
+ git_odb_free(new_odb);
+ loose->free(loose);
+ packed->free(packed);
+}
diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c
index 928a9ff7b..919705e07 100644
--- a/tests/refs/branches/upstream.c
+++ b/tests/refs/branches/upstream.c
@@ -71,6 +71,31 @@ void test_refs_branches_upstream__upstream_remote(void)
git_buf_dispose(&buf);
}
+void test_refs_branches_upstream__upstream_merge(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+ git_buf buf = GIT_BUF_INIT;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ /* check repository */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ assert_config_entry_value(repository, "branch.test.remote", "test");
+ assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master");
+
+ git_reference_free(branch);
+
+ /* check merge branch */
+ cl_git_pass(git_branch_upstream_merge(&buf, repository, "refs/heads/test"));
+ cl_assert_equal_s("refs/heads/master", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_sandbox_cleanup();
+}
+
void test_refs_branches_upstream__upstream_remote_empty_value(void)
{
git_repository *repository;
diff --git a/tests/remote/fetch.c b/tests/remote/fetch.c
new file mode 100644
index 000000000..392801e0b
--- /dev/null
+++ b/tests/remote/fetch.c
@@ -0,0 +1,169 @@
+#include "../clar_libgit2.h"
+
+#include "remote.h"
+#include "repository.h"
+
+static git_repository *repo1;
+static git_repository *repo2;
+static char* repo1_path;
+static char* repo2_path;
+
+static const char *REPO1_REFNAME = "refs/heads/main";
+static const char *REPO2_REFNAME = "refs/remotes/repo1/main";
+static char *FORCE_FETCHSPEC = "+refs/heads/main:refs/remotes/repo1/main";
+static char *NON_FORCE_FETCHSPEC = "refs/heads/main:refs/remotes/repo1/main";
+
+void test_remote_fetch__initialize(void) {
+ git_config *c;
+ git_buf repo1_path_buf = GIT_BUF_INIT;
+ git_buf repo2_path_buf = GIT_BUF_INIT;
+ const char *sandbox = clar_sandbox_path();
+
+ cl_git_pass(git_buf_joinpath(&repo1_path_buf, sandbox, "fetchtest_repo1"));
+ repo1_path = git_buf_detach(&repo1_path_buf);
+ cl_git_pass(git_repository_init(&repo1, repo1_path, true));
+
+ cl_git_pass(git_buf_joinpath(&repo2_path_buf, sandbox, "fetchtest_repo2"));
+ repo2_path = git_buf_detach(&repo2_path_buf);
+ cl_git_pass(git_repository_init(&repo2, repo2_path, true));
+
+ cl_git_pass(git_repository_config(&c, repo1));
+ cl_git_pass(git_config_set_string(c, "user.email", "some@email"));
+ cl_git_pass(git_config_set_string(c, "user.name", "some@name"));
+ git_config_free(c);
+ git_buf_dispose(&repo1_path_buf);
+ git_buf_dispose(&repo2_path_buf);
+}
+
+void test_remote_fetch__cleanup(void) {
+ git_repository_free(repo1);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_futils_rmdir_r(repo1_path, NULL, GIT_RMDIR_REMOVE_FILES));
+ free(repo1_path);
+
+ cl_git_pass(git_futils_rmdir_r(repo2_path, NULL, GIT_RMDIR_REMOVE_FILES));
+ free(repo2_path);
+}
+
+
+/**
+ * This checks that the '+' flag on fetchspecs is respected. We create a
+ * repository that has a reference to two commits, one a child of the other.
+ * We fetch this repository into a second repository. Then we reset the
+ * reference in the first repository and run the fetch again. If the '+' flag
+ * is used then the reference in the second repository will change, but if it
+ * is not then it should stay the same.
+ *
+ * @param commit1id A pointer to an OID which will be populated with the first
+ * commit.
+ * @param commit2id A pointer to an OID which will be populated with the second
+ * commit, which is a descendant of the first.
+ * @param force Whether to use a spec with '+' prefixed to force the refs
+ * to update
+ */
+void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
+ bool force) {
+ char *refspec_strs = {
+ force ? FORCE_FETCHSPEC : NON_FORCE_FETCHSPEC,
+ };
+ git_strarray refspecs = {
+ .count = 1,
+ .strings = &refspec_strs,
+ };
+
+ // create two commits in repo 1 and a reference to them
+ {
+ git_oid empty_tree_id;
+ git_tree *empty_tree;
+ git_signature *sig;
+ git_treebuilder *tb;
+ cl_git_pass(git_treebuilder_new(&tb, repo1, NULL));
+ cl_git_pass(git_treebuilder_write(&empty_tree_id, tb));
+ cl_git_pass(git_tree_lookup(&empty_tree, repo1, &empty_tree_id));
+ cl_git_pass(git_signature_default(&sig, repo1));
+ cl_git_pass(git_commit_create(commit1id, repo1, REPO1_REFNAME, sig,
+ sig, NULL, "one", empty_tree, 0, NULL));
+ cl_git_pass(git_commit_create_v(commit2id, repo1, REPO1_REFNAME, sig,
+ sig, NULL, "two", empty_tree, 1, commit1id));
+
+ git_tree_free(empty_tree);
+ git_signature_free(sig);
+ git_treebuilder_free(tb);
+ }
+
+ // fetch the reference via the remote
+ {
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo2,
+ git_repository_path(repo1)));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, "some message"));
+
+ git_remote_free(remote);
+ }
+
+ // assert that repo2 references the second commit
+ {
+ const git_oid *target;
+ git_reference *ref;
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, commit2id), 0);
+ git_reference_free(ref);
+ }
+
+ // set the reference in repo1 to point to the older commit
+ {
+ git_reference *ref;
+ git_reference *ref2;
+ cl_git_pass(git_reference_lookup(&ref, repo1, REPO1_REFNAME));
+ cl_git_pass(git_reference_set_target(&ref2, ref, commit1id,
+ "rollback"));
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ }
+
+ // fetch the reference again
+ {
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo2,
+ git_repository_path(repo1)));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, "some message"));
+
+ git_remote_free(remote);
+ }
+}
+
+void test_remote_fetch__dont_update_refs_if_not_descendant_and_not_force(void) {
+ const git_oid *target;
+ git_oid commit1id;
+ git_oid commit2id;
+ git_reference *ref;
+
+ do_time_travelling_fetch(&commit1id, &commit2id, false);
+
+ // assert that the reference in repo2 has not changed
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, &commit2id), 0);
+
+ git_reference_free(ref);
+}
+
+void test_remote_fetch__do_update_refs_if_not_descendant_and_force(void) {
+ const git_oid *target;
+ git_oid commit1id;
+ git_oid commit2id;
+ git_reference *ref;
+
+ do_time_travelling_fetch(&commit1id, &commit2id, true);
+
+ // assert that the reference in repo2 has changed
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, &commit1id), 0);
+
+ git_reference_free(ref);
+}
diff --git a/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb b/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb
new file mode 100644
index 000000000..44076ca39
--- /dev/null
+++ b/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb
Binary files differ
diff --git a/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d b/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d
new file mode 100644
index 000000000..ca97967b8
--- /dev/null
+++ b/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d
Binary files differ
diff --git a/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c b/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c
new file mode 100644
index 000000000..0576e6229
--- /dev/null
+++ b/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c
Binary files differ
diff --git a/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0 b/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0
new file mode 100644
index 000000000..7501c88e6
--- /dev/null
+++ b/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0
Binary files differ
diff --git a/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0 b/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0
new file mode 100644
index 000000000..d745d20e6
--- /dev/null
+++ b/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0
@@ -0,0 +1,3 @@
+xM
+0]s%3Dt $b6FQ2McnSWU`6*CĽGw,|S,\#Y8t); qȳ
+5VG-~ۥ2=`ֲml?13 cn H]aj@U \ No newline at end of file
diff --git a/tests/resources/crlf.git/refs/heads/ident b/tests/resources/crlf.git/refs/heads/ident
new file mode 100644
index 000000000..8732f0cb9
--- /dev/null
+++ b/tests/resources/crlf.git/refs/heads/ident
@@ -0,0 +1 @@
+b8986fec0f7bde90f78ac72706e782d82f24f2f0
diff --git a/tests/resources/crlf.git/refs/heads/no-ident b/tests/resources/crlf.git/refs/heads/no-ident
new file mode 100644
index 000000000..fa9a6737b
--- /dev/null
+++ b/tests/resources/crlf.git/refs/heads/no-ident
@@ -0,0 +1 @@
+1ec507638b806aba45d6142082885f2a9e88322d
diff --git a/tests/worktree/merge.c b/tests/worktree/merge.c
index 4b743829c..2a1206032 100644
--- a/tests/worktree/merge.c
+++ b/tests/worktree/merge.c
@@ -70,9 +70,9 @@ void test_worktree_merge__merge_setup(void)
ours, (const git_annotated_commit **)&theirs, 1));
for (i = 0; i < ARRAY_SIZE(merge_files); i++) {
- git_buf_clear(&path);
- cl_git_pass(git_buf_printf(&path, "%s/%s",
- fixture.worktree->gitdir, merge_files[i]));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.worktree->gitdir,
+ merge_files[i]));
cl_assert(git_path_exists(path.ptr));
}
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
index f2078a3f9..9b87bfae6 100644
--- a/tests/worktree/worktree.c
+++ b/tests/worktree/worktree.c
@@ -44,8 +44,9 @@ void test_worktree_worktree__list_with_invalid_worktree_dirs(void)
git_strarray wts;
size_t i, j, len;
- cl_git_pass(git_buf_printf(&path, "%s/worktrees/invalid",
- fixture.repo->commondir));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/invalid"));
cl_git_pass(p_mkdir(path.ptr, 0755));
len = path.size;
@@ -145,9 +146,9 @@ void test_worktree_worktree__open_invalid_commondir(void)
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/commondir"));
- cl_git_pass(git_buf_printf(&path,
- "%s/worktrees/testrepo-worktree/commondir",
- fixture.repo->commondir));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/testrepo-worktree/commondir"));
cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
@@ -165,9 +166,9 @@ void test_worktree_worktree__open_invalid_gitdir(void)
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
- cl_git_pass(git_buf_printf(&path,
- "%s/worktrees/testrepo-worktree/gitdir",
- fixture.repo->commondir));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/testrepo-worktree/gitdir"));
cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));