Commit e66545e3 by lhchavez

Merge remote-tracking branch 'origin/main' into multi-pack-index-write

parents 366115e0 fabacb7c
......@@ -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'
......@@ -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 }}
......@@ -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.
......
......@@ -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.
......@@ -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
-------------
......
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
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
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
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
#!/bin/bash -e
useradd --shell /bin/bash libgit2
chown --recursive libgit2:libgit2 /home/libgit2
exec sudo --preserve-env --set-home --user=libgit2 "$@"
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
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
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 ()
......@@ -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()
......
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})
......@@ -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__ */
......@@ -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);
}
......@@ -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__ */
......@@ -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);
}
......@@ -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__ */
......@@ -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));
}
......@@ -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__ */
......@@ -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;
......
......@@ -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`.
......
......@@ -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__ */
......@@ -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);
}
/*
* 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__ */
......@@ -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;
}
/*
* 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__ */
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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.
......
......@@ -43,8 +43,9 @@ GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_reposi
* Note that a commit is not considered a descendant of itself, in contrast
* to `git merge-base --is-ancestor`.
*
* @param commit a previously loaded commit.
* @param ancestor a potential ancestor commit.
* @param repo the repository where the commits exist
* @param commit a previously loaded commit
* @param ancestor a potential ancestor commit
* @return 1 if the given commit is a descendant of the potential ancestor,
* 0 if not, error code otherwise.
*/
......@@ -53,6 +54,23 @@ GIT_EXTERN(int) git_graph_descendant_of(
const git_oid *commit,
const git_oid *ancestor);
/**
* Determine if a commit is reachable from any of a list of commits by
* following parent edges.
*
* @param repo the repository where the commits exist
* @param commit a previously loaded commit
* @param length the number of commits in the provided `descendant_array`
* @param descendant_array oids of the commits
* @return 1 if the given commit is an ancestor of any of the given potential
* descendants, 0 if not, error code otherwise.
*/
GIT_EXTERN(int) git_graph_reachable_from_any(
git_repository *repo,
const git_oid *commit,
const git_oid descendant_array[],
size_t length);
/** @} */
GIT_END_DECL
#endif
......@@ -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.
......
......@@ -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;
......
......@@ -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;
......
......@@ -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
...
}
......@@ -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}")
......
......@@ -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
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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,
......
......@@ -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);
......
......@@ -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};
......
......@@ -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__)
......
......@@ -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 {
......
......@@ -12,6 +12,24 @@
#include "odb.h"
#include "commit.h"
int git_commit_list_generation_cmp(const void *a, const void *b)
{
uint32_t generation_a = ((git_commit_list_node *) a)->generation;
uint32_t generation_b = ((git_commit_list_node *) b)->generation;
if (!generation_a || !generation_b) {
/* Fall back to comparing by timestamps if at least one commit lacks a generation. */
return git_commit_list_time_cmp(a, b);
}
if (generation_a < generation_b)
return 1;
if (generation_a > generation_b)
return -1;
return 0;
}
int git_commit_list_time_cmp(const void *a, const void *b)
{
int64_t time_a = ((git_commit_list_node *) a)->time;
......
......@@ -46,6 +46,7 @@ typedef struct git_commit_list {
} git_commit_list;
git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
int git_commit_list_generation_cmp(const void *a, const void *b);
int git_commit_list_time_cmp(const void *a, const void *b);
void git_commit_list_free(git_commit_list **list_p);
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
......
......@@ -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>
......
......@@ -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);
......
......@@ -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
......
......@@ -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)
......
......@@ -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.
......
......@@ -43,7 +43,7 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
return 0;
}
if (git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp) < 0)
if (git_pqueue_init(&list, 0, 2, git_commit_list_generation_cmp) < 0)
return -1;
if (git_commit_list_parse(walk, one) < 0)
......@@ -176,19 +176,74 @@ on_error:
int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor)
{
git_oid merge_base;
int error;
if (git_oid_equal(commit, ancestor))
return 0;
error = git_merge_base(&merge_base, repo, commit, ancestor);
/* No merge-base found, it's not a descendant */
if (error == GIT_ENOTFOUND)
return git_graph_reachable_from_any(repo, ancestor, commit, 1);
}
int git_graph_reachable_from_any(
git_repository *repo,
const git_oid *commit_id,
const git_oid descendant_array[],
size_t length)
{
git_revwalk *walk = NULL;
git_vector list;
git_commit_list *result = NULL;
git_commit_list_node *commit;
size_t i;
uint32_t minimum_generation = 0xffffffff;
int error = 0;
if (!length)
return 0;
if (error < 0)
for (i = 0; i < length; ++i) {
if (git_oid_equal(commit_id, &descendant_array[i]))
return 1;
}
if ((error = git_vector_init(&list, length + 1, NULL)) < 0)
return error;
return git_oid_equal(&merge_base, ancestor);
if ((error = git_revwalk_new(&walk, repo)) < 0)
goto done;
for (i = 0; i < length; i++) {
commit = git_revwalk__commit_lookup(walk, &descendant_array[i]);
if (commit == NULL) {
error = -1;
goto done;
}
git_vector_insert(&list, commit);
if (minimum_generation > commit->generation)
minimum_generation = commit->generation;
}
commit = git_revwalk__commit_lookup(walk, commit_id);
if (commit == NULL) {
error = -1;
goto done;
}
if (minimum_generation > commit->generation)
minimum_generation = commit->generation;
if ((error = git_merge__bases_many(&result, walk, commit, &list, minimum_generation)) < 0)
goto done;
if (result) {
error = git_oid_equal(commit_id, &result->item->oid);
} else {
/* No merge-base found, it's not a descendant */
error = 0;
}
done:
git_commit_list_free(&result);
git_vector_free(&list);
git_revwalk_free(walk);
return error;
}
......@@ -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 */ ;
}
......
......@@ -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;
......
......@@ -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
......
......@@ -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)
......
......@@ -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;
......
......@@ -112,7 +112,7 @@ static int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_r
if (commit == NULL)
goto on_error;
if (git_merge__bases_many(&result, walk, commit, &list) < 0)
if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0)
goto on_error;
if (!result) {
......@@ -243,7 +243,7 @@ static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_reposi
if (commit == NULL)
goto on_error;
if (git_merge__bases_many(&result, walk, commit, &list) < 0)
if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0)
goto on_error;
if (!result) {
......@@ -378,7 +378,11 @@ static int clear_commit_marks(git_commit_list_node *commit, unsigned int mark)
}
static int paint_down_to_common(
git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
git_commit_list **out,
git_revwalk *walk,
git_commit_list_node *one,
git_vector *twos,
uint32_t minimum_generation)
{
git_pqueue list;
git_commit_list *result = NULL;
......@@ -387,7 +391,7 @@ static int paint_down_to_common(
int error;
unsigned int i;
if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_generation_cmp) < 0)
return -1;
one->flags |= PARENT1;
......@@ -399,7 +403,6 @@ static int paint_down_to_common(
return -1;
two->flags |= PARENT2;
if (git_pqueue_insert(&list, two) < 0)
return -1;
}
......@@ -427,6 +430,8 @@ static int paint_down_to_common(
git_commit_list_node *p = commit->parents[i];
if ((p->flags & flags) == flags)
continue;
if (p->generation < minimum_generation)
continue;
if ((error = git_commit_list_parse(walk, p)) < 0)
return error;
......@@ -442,7 +447,7 @@ static int paint_down_to_common(
return 0;
}
static int remove_redundant(git_revwalk *walk, git_vector *commits)
static int remove_redundant(git_revwalk *walk, git_vector *commits, uint32_t minimum_generation)
{
git_vector work = GIT_VECTOR_INIT;
unsigned char *redundant;
......@@ -478,7 +483,7 @@ static int remove_redundant(git_revwalk *walk, git_vector *commits)
goto done;
}
error = paint_down_to_common(&common, walk, commit, &work);
error = paint_down_to_common(&common, walk, commit, &work, minimum_generation);
if (error < 0)
goto done;
......@@ -510,7 +515,12 @@ done:
return error;
}
int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
int git_merge__bases_many(
git_commit_list **out,
git_revwalk *walk,
git_commit_list_node *one,
git_vector *twos,
uint32_t minimum_generation)
{
int error;
unsigned int i;
......@@ -532,7 +542,7 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
if (git_commit_list_parse(walk, one) < 0)
return -1;
error = paint_down_to_common(&result, walk, one, twos);
error = paint_down_to_common(&result, walk, one, twos, minimum_generation);
if (error < 0)
return error;
......@@ -559,7 +569,7 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
if ((error = clear_commit_marks(one, ALL_FLAGS)) < 0 ||
(error = clear_commit_marks_many(twos, ALL_FLAGS)) < 0 ||
(error = remove_redundant(walk, &redundant)) < 0) {
(error = remove_redundant(walk, &redundant, minimum_generation)) < 0) {
git_vector_free(&redundant);
return error;
}
......
......@@ -129,7 +129,8 @@ int git_merge__bases_many(
git_commit_list **out,
git_revwalk *walk,
git_commit_list_node *one,
git_vector *twos);
git_vector *twos,
uint32_t minimum_generation);
/*
* Three-way tree differencing
......
......@@ -14,7 +14,7 @@
#include "net.h"
#ifdef GIT_OPENSSL
# include <openssl/ssl.h>
# include "streams/openssl.h"
#endif
typedef struct gitno_ssl {
......
......@@ -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) {
......
......@@ -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 */
......
......@@ -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)
......
......@@ -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;
......
......@@ -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:
......
......@@ -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);
......
......@@ -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);
......
......@@ -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];
......
......@@ -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
......
/*
* 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 */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment