diff options
| author | Carlos Martín Nieto <cmn@dwim.me> | 2017-10-29 12:08:00 +0100 |
|---|---|---|
| committer | Carlos Martín Nieto <cmn@dwim.me> | 2017-10-29 12:08:00 +0100 |
| commit | 1b9cc2ece515aba1d11c48c42c7a62d58a61c3f0 (patch) | |
| tree | 65cc0b52e659521377150783f5c03dfad70e9ebb /src | |
| parent | 5efe9d125f09a44e1581cf7d37cf4d36161f5d4d (diff) | |
| parent | 524c1d3c9eef1f63d058ca5d4a61af7d5588ebfb (diff) | |
| download | libgit2-1b9cc2ece515aba1d11c48c42c7a62d58a61c3f0.tar.gz | |
Merge remote-tracking branch 'upstream/master' into diff-indent-heuristic
Diffstat (limited to 'src')
264 files changed, 8990 insertions, 2840 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..3392a4218 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,400 @@ +IF(DEBUG_POOL) + SET(GIT_DEBUG_POOL 1) +ENDIF() + +# This variable will contain the libraries we need to put into +# libgit2.pc's Requires.private. That is, what we're linking to or +# what someone who's statically linking us needs to link to. +SET(LIBGIT2_PC_REQUIRES "") +# This will be set later if we use the system's http-parser library or +# use iconv (OSX) and will be written to the Libs.private field in the +# pc file. +SET(LIBGIT2_PC_LIBS "") + +SET(LIBGIT2_INCLUDES + "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_SOURCE_DIR}/src" + "${CMAKE_SOURCE_DIR}/include") +SET(LIBGIT2_LIBS "") +SET(LIBGIT2_LIBDIRS "") + +# Installation paths +# +SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.") +SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.") +SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.") + +# Set a couple variables to be substituted inside the .pc file. +# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue +# or relative paths is both valid and supported by cmake. +SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX}) + +IF(IS_ABSOLUTE ${LIB_INSTALL_DIR}) + SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR}) +ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR}) + SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}") +ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR}) + +IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR}) +ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}") +ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + +# Enable tracing +IF (ENABLE_TRACE STREQUAL "ON") + SET(GIT_TRACE 1) +ENDIF() + +CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L) +IF (HAVE_REGCOMP_L) + SET(GIT_USE_REGCOMP_L 1) +ENDIF () + +CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS) +IF (HAVE_FUTIMENS) + SET(GIT_USE_FUTIMENS 1) +ENDIF () + +CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R) +IF (HAVE_QSORT_R) + ADD_DEFINITIONS(-DHAVE_QSORT_R) +ENDIF () + +CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S) +IF (HAVE_QSORT_S) + ADD_DEFINITIONS(-DHAVE_QSORT_S) +ENDIF () + +# Find required dependencies + +IF(WIN32) + LIST(APPEND LIBGIT2_LIBS ws2_32) +ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + LIST(APPEND LIBGIT2_LIBS socket nsl) + LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl") +ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku") + LIST(APPEND LIBGIT2_LIBS network) + LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork") +ENDIF() + +CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT) +IF(NEED_LIBRT) + LIST(APPEND LIBGIT2_LIBS rt) + LIST(APPEND LIBGIT2_PC_LIBS "-lrt") +ENDIF() + +IF(THREADSAFE) + LIST(APPEND LIBGIT2_LIBS ${CMAKE_THREAD_LIBS_INIT}) + LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT}) +ENDIF() + +IF (SECURITY_FOUND) + # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there + CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY) + IF (HAVE_NEWER_SECURITY) + MESSAGE("-- Found Security ${SECURITY_DIRS}") + LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") + LIST(APPEND LIBGIT2_LIBS ${SECURITY_DIRS}) + ELSE() + MESSAGE("-- Security framework is too old, falling back to OpenSSL") + SET(SECURITY_FOUND "NO") + SET(USE_OPENSSL "ON") + ENDIF() +ENDIF() + +IF (COREFOUNDATION_FOUND) + MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}") + LIST(APPEND LIBGIT2_PC_LIBS "-framework CoreFoundation") + LIST(APPEND LIBGIT2_LIBS ${COREFOUNDATION_DIRS}) +ENDIF() + + +IF (WIN32 AND EMBED_SSH_PATH) + FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c") + LIST(APPEND LIBGIT2_INCLUDES "${EMBED_SSH_PATH}/include") + FILE(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"") + SET(GIT_SSH 1) +ENDIF() + +IF (WIN32 AND WINHTTP) + SET(GIT_WINHTTP 1) + SET(GIT_HTTPS 1) + + # Since MinGW does not come with headers or an import library for winhttp, + # we have to include a private header and generate our own import library + IF (MINGW) + ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/winhttp" "${CMAKE_BINARY_DIR}/deps/winhttp") + LIST(APPEND LIBGIT2_LIBS winhttp) + LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/winhttp") + LIST(APPEND LIBGIT2_LIBDIRS ${LIBWINHTTP_PATH}) + ELSE() + LIST(APPEND LIBGIT2_LIBS "winhttp") + LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp") + ENDIF () + + LIST(APPEND LIBGIT2_LIBS "rpcrt4" "crypt32" "ole32") + LIST(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32") +ELSE () + IF (CURL) + PKG_CHECK_MODULES(CURL libcurl) + ENDIF () + + IF (NOT AMIGA AND USE_OPENSSL) + FIND_PACKAGE(OpenSSL) + ENDIF () + + IF (CURL_FOUND) + SET(GIT_CURL 1) + LIST(APPEND LIBGIT2_INCLUDES ${CURL_INCLUDE_DIRS}) + LIST(APPEND LIBGIT2_LIBDIRS ${CURL_LIBRARY_DIRS}) + LIST(APPEND LIBGIT2_LIBS ${CURL_LIBRARIES}) + LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS}) + ENDIF() +ENDIF() + +# Specify sha1 implementation +IF (USE_SHA1DC) + SET(GIT_SHA1_COLLISIONDETECT 1) + ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1) + ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\") + ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\") + FILE(GLOB SRC_SHA1 hash/hash_collisiondetect.c hash/sha1dc/*) +ELSEIF (WIN32 AND NOT MINGW) + SET(GIT_SHA1_WIN32 1) + FILE(GLOB SRC_SHA1 hash/hash_win32.c) +ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + SET(GIT_SHA1_COMMON_CRYPTO 1) +ELSEIF (OPENSSL_FOUND) + SET(GIT_SHA1_OPENSSL 1) + IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + LIST(APPEND LIBGIT2_PC_LIBS "-lssl") + ELSE() + SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl") + ENDIF () +ELSE() + FILE(GLOB SRC_SHA1 hash/hash_generic.c) +ENDIF() + + +# Include POSIX regex when it is required +IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/regex" "${CMAKE_BINARY_DIR}/deps/regex") + LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/regex") + LIST(APPEND LIBGIT2_LIBS regex) +ENDIF() + +# Optional external dependency: http-parser +FIND_PACKAGE(HTTP_Parser) +IF (USE_EXT_HTTP_PARSER AND HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2) + LIST(APPEND LIBGIT2_INCLUDES ${HTTP_PARSER_INCLUDE_DIRS}) + LIST(APPEND LIBGIT2_LIBS ${HTTP_PARSER_LIBRARIES}) + LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser") +ELSE() + MESSAGE(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.") + ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/http-parser" "${CMAKE_BINARY_DIR}/deps/http-parser") + LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/http-parser") + LIST(APPEND LIBGIT2_LIBS http-parser) +ENDIF() + +# Optional external dependency: zlib +FIND_PACKAGE(ZLIB) +IF (ZLIB_FOUND) + LIST(APPEND LIBGIT2_INCLUDES ${ZLIB_INCLUDE_DIRS}) + LIST(APPEND LIBGIT2_LIBS ${ZLIB_LIBRARIES}) + IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + LIST(APPEND LIBGIT2_LIBS "z") + LIST(APPEND LIBGIT2_PC_LIBS "-lz") + ELSE() + SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib") + ENDIF() +ELSE() + MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." ) + ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/zlib" "${CMAKE_BINARY_DIR}/deps/zlib") + LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/zlib") + LIST(APPEND LIBGIT2_LIBS zlib) +ENDIF() + +# Optional external dependency: libssh2 +IF (USE_SSH) + PKG_CHECK_MODULES(LIBSSH2 libssh2) +ENDIF() +IF (LIBSSH2_FOUND) + SET(GIT_SSH 1) + LIST(APPEND LIBGIT2_INCLUDES ${LIBSSH2_INCLUDE_DIRS}) + LIST(APPEND LIBGIT2_LIBS ${LIBSSH2_LIBRARIES}) + LIST(APPEND LIBGIT2_LIBDIRS ${LIBSSH2_LIBRARY_DIRS}) + LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS}) + #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}") + + CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS) + IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS) + SET(GIT_SSH_MEMORY_CREDENTIALS 1) + ENDIF() +ELSE() + MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.") +ENDIF() + +# Optional external dependency: libgssapi +IF (USE_GSSAPI) + FIND_PACKAGE(GSSAPI) +ENDIF() +IF (GSSAPI_FOUND) + SET(GIT_GSSAPI 1) + LIST(APPEND LIBGIT2_LIBS ${GSSAPI_LIBRARIES}) +ENDIF() + +# Optional external dependency: iconv +IF (USE_ICONV) + FIND_PACKAGE(Iconv) +ENDIF() +IF (ICONV_FOUND) + SET(GIT_USE_ICONV 1) + LIST(APPEND LIBGIT2_INCLUDES ${ICONV_INCLUDE_DIR}) + LIST(APPEND LIBGIT2_LIBS ${ICONV_LIBRARIES}) + LIST(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES}) +ENDIF() + +IF (SECURITY_FOUND) + SET(GIT_SECURE_TRANSPORT 1) + SET(GIT_HTTPS 1) + LIST(APPEND LIBGIT2_INCLUDES ${SECURITY_INCLUDE_DIR}) +ENDIF () + +IF (OPENSSL_FOUND) + SET(GIT_OPENSSL 1) + SET(GIT_HTTPS 1) + LIST(APPEND LIBGIT2_INCLUDES ${OPENSSL_INCLUDE_DIR}) + LIST(APPEND LIBGIT2_LIBS ${OPENSSL_LIBRARIES}) +ENDIF() + + + +IF (THREADSAFE) + IF (NOT WIN32) + FIND_PACKAGE(Threads REQUIRED) + ENDIF() + + SET(GIT_THREADS 1) +ENDIF() + +IF (USE_NSEC) + SET(GIT_USE_NSEC 1) +ENDIF() + +IF (HAVE_STRUCT_STAT_ST_MTIM) + SET(GIT_USE_STAT_MTIM 1) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC) + SET(GIT_USE_STAT_MTIMESPEC 1) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC) + SET(GIT_USE_STAT_MTIME_NSEC 1) +ENDIF() + +ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) + +# Collect sourcefiles +FILE(GLOB SRC_H + "${CMAKE_SOURCE_DIR}/include/git2.h" + "${CMAKE_SOURCE_DIR}/include/git2/*.h" + "${CMAKE_SOURCE_DIR}/include/git2/sys/*.h") + +# On Windows use specific platform sources +IF (WIN32 AND NOT CYGWIN) + ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501) + + IF(MSVC) + SET(WIN_RC "win32/git2.rc") + ENDIF() + + FILE(GLOB SRC_OS win32/*.c win32/*.h) +ELSEIF (AMIGA) + ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP) +ELSE() + IF (VALGRIND) + ADD_DEFINITIONS(-DNO_MMAP) + ENDIF() + FILE(GLOB SRC_OS unix/*.c unix/*.h) +ENDIF() +FILE(GLOB SRC_GIT2 *.c *.h transports/*.c transports/*.h xdiff/*.c xdiff/*.h) + +# Determine architecture of the machine +IF (CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(GIT_ARCH_64 1) +ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4) + SET(GIT_ARCH_32 1) +ELSEIF (CMAKE_SIZEOF_VOID_P) + MESSAGE(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)") +ELSE() + MESSAGE(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)") +ENDIF() + +CONFIGURE_FILE(features.h.in git2/sys/features.h) + +SET(GIT2INTERNAL_OBJECTS ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_SSH} ${SRC_SHA1}) + +IF (CMAKE_VERSION VERSION_GREATER 2.8.7) + ADD_LIBRARY(git2internal OBJECT ${GIT2INTERNAL_OBJECTS}) + IDE_SPLIT_SOURCES(git2internal) + SET(GIT2INTERNAL_OBJECTS $<TARGET_OBJECTS:git2internal>) + + IF (${CMAKE_VERSION} VERSION_LESS 2.8.12) + INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES}) + ELSE() + TARGET_INCLUDE_DIRECTORIES(git2internal + PRIVATE ${LIBGIT2_INCLUDES} + PUBLIC ${CMAKE_SOURCE_DIR}/include) + ENDIF() +ELSE() + INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES}) +ENDIF() + +SET(GIT2INTERNAL_OBJECTS ${GIT2INTERNAL_OBJECTS} PARENT_SCOPE) +SET(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE) +SET(LIBGIT2_LIBS ${LIBGIT2_LIBS} PARENT_SCOPE) +SET(LIBGIT2_LIBDIRS ${LIBGIT2_LIBDIRS} PARENT_SCOPE) + +# Compile and link libgit2 +LINK_DIRECTORIES(${LIBGIT2_LIBDIRS}) +ADD_LIBRARY(git2 ${WIN_RC} ${GIT2INTERNAL_OBJECTS}) +TARGET_LINK_LIBRARIES(git2 ${LIBGIT2_LIBS}) + +SET_TARGET_PROPERTIES(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +SET_TARGET_PROPERTIES(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +SET_TARGET_PROPERTIES(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240) +# Win64+MSVC+static libs = linker error +IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS) + SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64") +ENDIF() + +IDE_SPLIT_SOURCES(git2) + +IF (SONAME) + SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) + SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION}) + IF (LIBGIT2_FILENAME) + ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\") + SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME}) + ELSEIF (DEFINED LIBGIT2_PREFIX) + SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}") + ENDIF() +ENDIF() +STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}") +CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libgit2.pc.in ${CMAKE_BINARY_DIR}/libgit2.pc @ONLY) + +IF (MSVC_IDE) + # Precompiled headers + SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") + SET_SOURCE_FILES_PROPERTIES(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h") +ENDIF () + +# Install +INSTALL(TARGETS git2 + RUNTIME DESTINATION ${BIN_INSTALL_DIR} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} +) +INSTALL(FILES ${CMAKE_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig ) +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} ) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} ) diff --git a/src/annotated_commit.c b/src/annotated_commit.c index c2c770cba..72ba80a22 100644 --- a/src/annotated_commit.c +++ b/src/annotated_commit.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "annotated_commit.h" + #include "refs.h" #include "cache.h" diff --git a/src/annotated_commit.h b/src/annotated_commit.h index 3ac8b5f69..b390066b2 100644 --- a/src/annotated_commit.h +++ b/src/annotated_commit.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_annotated_commit_h__ #define INCLUDE_annotated_commit_h__ +#include "common.h" + #include "oidarray.h" #include "git2/oid.h" diff --git a/src/apply.c b/src/apply.c index f70172469..7801a0a54 100644 --- a/src/apply.c +++ b/src/apply.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "apply.h" + #include <assert.h> #include "git2/patch.h" @@ -12,7 +14,6 @@ #include "array.h" #include "patch.h" #include "fileops.h" -#include "apply.h" #include "delta.h" #include "zstream.h" @@ -173,7 +174,7 @@ static int apply_hunk( git_diff_line *line = git_array_get(patch->lines, linenum); if (!line) { - error = apply_err("Preimage does not contain line %d", linenum); + error = apply_err("preimage does not contain line %"PRIuZ, linenum); goto done; } @@ -193,7 +194,7 @@ static int apply_hunk( line_num = hunk->hunk.new_start ? hunk->hunk.new_start - 1 : 0; if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) { - error = apply_err("Hunk at line %d did not apply", + error = apply_err("hunk at line %d did not apply", hunk->hunk.new_start); goto done; } diff --git a/src/apply.h b/src/apply.h index 96e0f55b5..b29460c0b 100644 --- a/src/apply.h +++ b/src/apply.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_apply_h__ #define INCLUDE_apply_h__ +#include "common.h" + #include "git2/patch.h" #include "buffer.h" diff --git a/src/attr.c b/src/attr.c index d43a15f50..17309d0eb 100644 --- a/src/attr.c +++ b/src/attr.c @@ -1,4 +1,12 @@ -#include "common.h" +/* + * 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 "attr.h" + #include "repository.h" #include "sysdir.h" #include "config.h" @@ -7,8 +15,6 @@ #include "git2/oid.h" #include <ctype.h> -GIT__USE_STRMAP - const char *git_attr__true = "[internal]__TRUE__"; const char *git_attr__false = "[internal]__FALSE__"; const char *git_attr__unset = "[internal]__UNSET__"; @@ -209,7 +215,7 @@ int git_attr_foreach( if (git_strmap_exists(seen, assign->name)) continue; - git_strmap_insert(seen, assign->name, assign, error); + git_strmap_insert(seen, assign->name, assign, &error); if (error < 0) goto cleanup; @@ -292,7 +298,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) int error = 0; const char *workdir = git_repository_workdir(repo); git_index *idx = NULL; - git_buf sys = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT; if (attr_session && attr_session->init_setup) return 0; @@ -304,40 +310,45 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) * definitions will be available for later file parsing */ - error = system_attr_file(&sys, attr_session); + error = system_attr_file(&path, attr_session); if (error == 0) error = preload_attr_file( - repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); + repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, path.ptr); if (error != GIT_ENOTFOUND) - return error; - - git_buf_free(&sys); + goto out; if ((error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) - return error; + goto out; + + if ((error = git_repository_item_path(&path, + repo, GIT_REPOSITORY_ITEM_INFO)) < 0) + goto out; if ((error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, - git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0) - return error; + path.ptr, GIT_ATTR_FILE_INREPO)) < 0) + goto out; if (workdir != NULL && (error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) - return error; + goto out; if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || (error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) - return error; + goto out; if (attr_session) attr_session->init_setup = 1; +out: + git_buf_free(&path); + return error; } @@ -472,7 +483,7 @@ static int collect_attr_files( git_vector *files) { int error = 0; - git_buf dir = GIT_BUF_INIT; + git_buf dir = GIT_BUF_INIT, attrfile = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; @@ -494,9 +505,13 @@ static int collect_attr_files( * - $GIT_PREFIX/etc/gitattributes */ + error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO); + if (error < 0) + goto cleanup; + error = push_attr_file( repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, - git_repository_path(repo), GIT_ATTR_FILE_INREPO); + attrfile.ptr, GIT_ATTR_FILE_INREPO); if (error < 0) goto cleanup; @@ -538,6 +553,7 @@ static int collect_attr_files( cleanup: if (error < 0) release_attr_files(files); + git_buf_free(&attrfile); git_buf_free(&dir); return error; diff --git a/src/attr.h b/src/attr.h index f9f216d07..977565205 100644 --- a/src/attr.h +++ b/src/attr.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_attr_h__ #define INCLUDE_attr_h__ +#include "common.h" + #include "attr_file.h" #include "attrcache.h" diff --git a/src/attr_file.c b/src/attr_file.c index 11d149358..55d0c3865 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -1,7 +1,14 @@ -#include "common.h" +/* + * 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 "attr_file.h" + #include "repository.h" #include "filebuf.h" -#include "attr_file.h" #include "attrcache.h" #include "git2/blob.h" #include "git2/tree.h" @@ -30,7 +37,7 @@ int git_attr_file__new( GITERR_CHECK_ALLOC(attrs); if (git_mutex_init(&attrs->lock) < 0) { - giterr_set(GITERR_OS, "Failed to initialize lock"); + giterr_set(GITERR_OS, "failed to initialize lock"); git__free(attrs); return -1; } @@ -49,7 +56,7 @@ int git_attr_file__clear_rules(git_attr_file *file, bool need_lock) git_attr_rule *rule; if (need_lock && git_mutex_lock(&file->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock attribute file"); + giterr_set(GITERR_OS, "failed to lock attribute file"); return -1; } @@ -140,7 +147,7 @@ int git_attr_file__load( break; } default: - giterr_set(GITERR_INVALID, "Unknown file source %d", source); + giterr_set(GITERR_INVALID, "unknown file source %d", source); return -1; } @@ -212,7 +219,7 @@ int git_attr_file__out_of_date( } default: - giterr_set(GITERR_INVALID, "Invalid file type %d", file->source); + giterr_set(GITERR_INVALID, "invalid file type %d", file->source); return -1; } } @@ -238,7 +245,7 @@ int git_attr_file__parse_buffer( context = attrs->entry->path; if (git_mutex_lock(&attrs->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock attribute file"); + giterr_set(GITERR_OS, "failed to lock attribute file"); return -1; } @@ -395,9 +402,13 @@ bool git_attr_fnmatch__match( if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) { bool samename; - /* for attribute checks or root ignore checks, fail match */ + /* + * for attribute checks or checks at the root of this match's + * containing_dir (or root of the repository if no containing_dir), + * do not match. + */ if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) || - path->basename == path->path) + path->basename == relpath) return false; flags |= FNM_LEADING_DIR; diff --git a/src/attr_file.h b/src/attr_file.h index 388ecf4c0..fedf55af5 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_attr_file_h__ #define INCLUDE_attr_file_h__ +#include "common.h" + #include "git2/oid.h" #include "git2/attr.h" #include "vector.h" @@ -15,7 +17,7 @@ #include "fileops.h" #define GIT_ATTR_FILE ".gitattributes" -#define GIT_ATTR_FILE_INREPO "info/attributes" +#define GIT_ATTR_FILE_INREPO "attributes" #define GIT_ATTR_FILE_SYSTEM "gitattributes" #define GIT_ATTR_FILE_XDG "attributes" diff --git a/src/attrcache.c b/src/attrcache.c index a57110684..65e7b4655 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -1,18 +1,24 @@ -#include "common.h" +/* + * 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 "attrcache.h" + #include "repository.h" #include "attr_file.h" #include "config.h" #include "sysdir.h" #include "ignore.h" -GIT__USE_STRMAP - GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache) { GIT_UNUSED(cache); /* avoid warning if threading is off */ if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to get attr cache lock"); + giterr_set(GITERR_OS, "unable to get attr cache lock"); return -1; } return 0; @@ -82,7 +88,7 @@ static int attr_cache_make_entry( &entry, git_repository_workdir(repo), path, &cache->pool); if (!error) { - git_strmap_insert(cache->files, entry->path, entry, error); + git_strmap_insert(cache->files, entry->path, entry, &error); if (error > 0) error = 0; } @@ -105,8 +111,11 @@ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) GIT_REFCOUNT_OWN(file, entry); GIT_REFCOUNT_INC(file); - old = git__compare_and_swap( - &entry->file[file->source], entry->file[file->source], file); + /* + * Replace the existing value if another thread has + * created it in the meantime. + */ + old = git__swap(entry->file[file->source], file); if (old) { GIT_REFCOUNT_OWN(old, NULL); @@ -121,20 +130,22 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) { int error = 0; git_attr_file_entry *entry; + git_attr_file *old = NULL; if (!file) return 0; + if ((error = attr_cache_lock(cache)) < 0) return error; if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) - file = git__compare_and_swap(&entry->file[file->source], file, NULL); + old = git__compare_and_swap(&entry->file[file->source], file, NULL); attr_cache_unlock(cache); - if (file) { - GIT_REFCOUNT_OWN(file, NULL); - git_attr_file__free(file); + if (old) { + GIT_REFCOUNT_OWN(old, NULL); + git_attr_file__free(old); } return error; @@ -287,14 +298,16 @@ static int attr_cache__lookup_path( const char *cfgval = entry->value; /* expand leading ~/ as needed */ - if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && - !git_sysdir_find_global_file(&buf, &cfgval[2])) - *out = git_buf_detach(&buf); - else if (cfgval) + if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') { + if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2]))) + *out = git_buf_detach(&buf); + } else if (cfgval) { *out = git__strdup(cfgval); + } } - else if (!git_sysdir_find_xdg_file(&buf, fallback)) + else if (!git_sysdir_find_xdg_file(&buf, fallback)) { *out = git_buf_detach(&buf); + } git_config_entry_free(entry); git_buf_free(&buf); @@ -309,7 +322,7 @@ static void attr_cache__free(git_attr_cache *cache) if (!cache) return; - unlock = (git_mutex_lock(&cache->lock) == 0); + unlock = (attr_cache_lock(cache) == 0); if (cache->files != NULL) { git_attr_file_entry *entry; @@ -345,13 +358,13 @@ static void attr_cache__free(git_attr_cache *cache) cache->cfg_excl_file = NULL; if (unlock) - git_mutex_unlock(&cache->lock); + attr_cache_unlock(cache); git_mutex_free(&cache->lock); git__free(cache); } -int git_attr_cache__do_init(git_repository *repo) +int git_attr_cache__init(git_repository *repo) { int ret = 0; git_attr_cache *cache = git_repository_attr_cache(repo); @@ -365,7 +378,7 @@ int git_attr_cache__do_init(git_repository *repo) /* set up lock */ if (git_mutex_init(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to initialize lock for attr cache"); + giterr_set(GITERR_OS, "unable to initialize lock for attr cache"); git__free(cache); return -1; } @@ -429,11 +442,11 @@ int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) if (macro->assigns.length == 0) return 0; - if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to get attr cache lock"); + if (attr_cache_lock(cache) < 0) { + giterr_set(GITERR_OS, "unable to get attr cache lock"); error = -1; } else { - git_strmap_insert(macros, macro->match.pattern, macro, error); + git_strmap_insert(macros, macro->match.pattern, macro, &error); git_mutex_unlock(&cache->lock); } diff --git a/src/attrcache.h b/src/attrcache.h index 44e1ffdce..f528911ea 100644 --- a/src/attrcache.h +++ b/src/attrcache.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_attrcache_h__ #define INCLUDE_attrcache_h__ +#include "common.h" + #include "attr_file.h" #include "strmap.h" @@ -22,10 +24,7 @@ typedef struct { git_pool pool; } git_attr_cache; -extern int git_attr_cache__do_init(git_repository *repo); - -#define git_attr_cache__init(REPO) \ - (git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO)) +extern int git_attr_cache__init(git_repository *repo); /* get file - loading and reload as needed */ extern int git_attr_cache__get( diff --git a/src/blame.c b/src/blame.c index 2c8584ba5..a923bf003 100644 --- a/src/blame.c +++ b/src/blame.c @@ -6,6 +6,7 @@ */ #include "blame.h" + #include "git2/commit.h" #include "git2/revparse.h" #include "git2/revwalk.h" diff --git a/src/blame.h b/src/blame.h index d8db8d5c1..8fd3ee5b1 100644 --- a/src/blame.h +++ b/src/blame.h @@ -1,8 +1,9 @@ #ifndef INCLUDE_blame_h__ #define INCLUDE_blame_h__ -#include "git2/blame.h" #include "common.h" + +#include "git2/blame.h" #include "vector.h" #include "diff.h" #include "array.h" diff --git a/src/blame_git.c b/src/blame_git.c index 96785c75b..3c221b318 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -6,6 +6,7 @@ */ #include "blame_git.h" + #include "commit.h" #include "blob.h" #include "xdiff/xinclude.h" @@ -478,14 +479,15 @@ cleanup: * The blobs of origin and porigin exactly match, so everything origin is * suspected for can be blamed on the parent. */ -static void pass_whole_blame(git_blame *blame, +static int pass_whole_blame(git_blame *blame, git_blame__origin *origin, git_blame__origin *porigin) { git_blame__entry *e; - if (!porigin->blob) - git_object_lookup((git_object**)&porigin->blob, blame->repository, - git_blob_id(origin->blob), GIT_OBJ_BLOB); + if (!porigin->blob && + git_object_lookup((git_object**)&porigin->blob, blame->repository, + git_blob_id(origin->blob), GIT_OBJ_BLOB) < 0) + return -1; for (e=blame->ent; e; e=e->next) { if (!same_suspect(e->suspect, origin)) continue; @@ -493,6 +495,8 @@ static void pass_whole_blame(git_blame *blame, origin_decref(e->suspect); e->suspect = porigin; } + + return 0; } static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) @@ -514,11 +518,12 @@ static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) if (!num_parents) { git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); goto finish; - } - else if (num_parents < (int)ARRAY_SIZE(sg_buf)) + } else if (num_parents < (int)ARRAY_SIZE(sg_buf)) memset(sg_buf, 0, sizeof(sg_buf)); - else + else { sg_origin = git__calloc(num_parents, sizeof(*sg_origin)); + GITERR_CHECK_ALLOC(sg_origin); + } for (i=0; i<num_parents; i++) { git_commit *p; @@ -543,7 +548,7 @@ static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) } if (porigin->blob && origin->blob && !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) { - pass_whole_blame(blame, origin, porigin); + error = pass_whole_blame(blame, origin, porigin); origin_decref(porigin); goto finish; } diff --git a/src/blame_git.h b/src/blame_git.h index 1891b0e1f..48b85a20d 100644 --- a/src/blame_git.h +++ b/src/blame_git.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_blame_git__ #define INCLUDE_blame_git__ +#include "common.h" + #include "blame.h" int git_blame__get_origin( diff --git a/src/blob.c b/src/blob.c index 1926c9e58..3396fe74f 100644 --- a/src/blob.c +++ b/src/blob.c @@ -5,14 +5,14 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "blob.h" + #include "git2/common.h" #include "git2/object.h" #include "git2/repository.h" #include "git2/odb_backend.h" -#include "common.h" #include "filebuf.h" -#include "blob.h" #include "filter.h" #include "buf_text.h" @@ -96,7 +96,7 @@ static int write_file_stream( p_close(fd); if (written != file_size || read_len < 0) { - giterr_set(GITERR_OS, "Failed to read file into stream"); + giterr_set(GITERR_OS, "failed to read file into stream"); error = -1; } @@ -142,7 +142,7 @@ static int write_symlink( read_len = p_readlink(path, link_data, link_size); if (read_len != (ssize_t)link_size) { - giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path); + giterr_set(GITERR_OS, "failed to create blob: cannot read symlink '%s'", path); git__free(link_data); return -1; } @@ -186,7 +186,7 @@ int git_blob__create_from_paths( goto done; if (S_ISDIR(st.st_mode)) { - giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path); + giterr_set(GITERR_ODB, "cannot create blob from '%s': it is a directory", content_path); error = GIT_EDIRECTORY; goto done; } @@ -326,8 +326,8 @@ int git_blob_create_fromstream(git_writestream **out, git_repository *repo, cons stream->parent.close = blob_writestream_close; stream->parent.free = blob_writestream_free; - if ((error = git_buf_joinpath(&path, - git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0) + if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 + || (error = git_buf_joinpath(&path, path.ptr, "streamed")) < 0) goto cleanup; if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, diff --git a/src/blob.h b/src/blob.h index 4cd9f1e0c..3f1f97719 100644 --- a/src/blob.h +++ b/src/blob.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_blob_h__ #define INCLUDE_blob_h__ +#include "common.h" + #include "git2/blob.h" #include "repository.h" #include "odb.h" diff --git a/src/branch.c b/src/branch.c index 51c35d7ff..0697a77fb 100644 --- a/src/branch.c +++ b/src/branch.c @@ -5,7 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "branch.h" + #include "commit.h" #include "tag.h" #include "config.h" @@ -13,6 +14,7 @@ #include "refs.h" #include "remote.h" #include "annotated_commit.h" +#include "worktree.h" #include "git2/branch.h" @@ -33,7 +35,7 @@ static int retrieve_branch_reference( /* OOM */; else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) giterr_set( - GITERR_REFERENCE, "Cannot locate %s branch '%s'", + GITERR_REFERENCE, "cannot locate %s branch '%s'", is_remote ? "remote-tracking" : "local", branch_name); *branch_reference_out = branch; /* will be NULL on error */ @@ -46,7 +48,7 @@ static int not_a_local_branch(const char *reference_name) { giterr_set( GITERR_INVALID, - "Reference '%s' is not a local branch.", reference_name); + "reference '%s' is not a local branch.", reference_name); return -1; } @@ -58,16 +60,17 @@ static int create_branch( const char *from, int force) { - int is_head = 0; + int is_unmovable_head = 0; git_reference *branch = NULL; git_buf canonical_branch_name = GIT_BUF_INIT, log_message = GIT_BUF_INIT; int error = -1; + int bare = git_repository_is_bare(repository); assert(branch_name && commit && ref_out); assert(git_object_owner((const git_object *)commit) == repository); - if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) { + if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) { error = git_branch_is_head(branch); git_reference_free(branch); branch = NULL; @@ -75,11 +78,11 @@ static int create_branch( if (error < 0) goto cleanup; - is_head = error; + is_unmovable_head = error; } - if (is_head && force) { - giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is " + if (is_unmovable_head && force) { + giterr_set(GITERR_REFERENCE, "cannot force update branch '%s' as it is " "the current HEAD of the repository.", branch_name); error = -1; goto cleanup; @@ -125,6 +128,31 @@ int git_branch_create_from_annotated( repository, branch_name, commit->commit, commit->description, force); } +static int branch_equals(git_repository *repo, const char *path, void *payload) +{ + git_reference *branch = (git_reference *) payload; + git_reference *head = NULL; + int equal = 0; + + if (git_reference__read_head(&head, repo, path) < 0 || + git_reference_type(head) != GIT_REF_SYMBOLIC) + goto done; + + equal = !git__strcmp(head->target.symbolic, branch->name); + +done: + git_reference_free(head); + return equal; +} + +int git_branch_is_checked_out(const git_reference *branch) +{ + assert(branch && git_reference_is_branch(branch)); + + return git_repository_foreach_head(git_reference_owner(branch), + branch_equals, (void *) branch) == 1; +} + int git_branch_delete(git_reference *branch) { int is_head; @@ -134,7 +162,7 @@ int git_branch_delete(git_reference *branch) assert(branch); if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", + giterr_set(GITERR_INVALID, "reference '%s' is not a valid branch.", git_reference_name(branch)); return GIT_ENOTFOUND; } @@ -143,11 +171,17 @@ int git_branch_delete(git_reference *branch) return is_head; if (is_head) { - giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + giterr_set(GITERR_REFERENCE, "cannot delete branch '%s' as it is " "the current HEAD of the repository.", git_reference_name(branch)); return -1; } + if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) { + giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + "the current HEAD of a linked repository.", git_reference_name(branch)); + return -1; + } + if (git_buf_join(&config_section, '.', "branch", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) goto on_error; @@ -305,7 +339,7 @@ int git_branch_name( branch_name += strlen(GIT_REFS_REMOTES_DIR); } else { giterr_set(GITERR_INVALID, - "Reference '%s' is neither a local nor a remote branch.", ref->name); + "reference '%s' is neither a local nor a remote branch.", ref->name); return -1; } *out = branch_name; @@ -435,7 +469,7 @@ int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refna /* Verify that this is a remote branch */ if (!git_reference__is_remote(refname)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.", + giterr_set(GITERR_INVALID, "reference '%s' is not a remote branch.", refname); error = GIT_ERROR; goto cleanup; @@ -462,7 +496,7 @@ int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refna git_remote_free(remote); giterr_set(GITERR_REFERENCE, - "Reference '%s' is ambiguous", refname); + "reference '%s' is ambiguous", refname); error = GIT_EAMBIGUOUS; goto cleanup; } @@ -476,7 +510,7 @@ int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refna error = git_buf_puts(buf, remote_name); } else { giterr_set(GITERR_REFERENCE, - "Could not determine remote for '%s'", refname); + "could not determine remote for '%s'", refname); error = GIT_ENOTFOUND; } @@ -565,7 +599,7 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name) local = 0; else { giterr_set(GITERR_REFERENCE, - "Cannot set upstream for branch '%s'", shortname); + "cannot set upstream for branch '%s'", shortname); return GIT_ENOTFOUND; } diff --git a/src/branch.h b/src/branch.h index d02f2af0d..5ae227c05 100644 --- a/src/branch.h +++ b/src/branch.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_branch_h__ #define INCLUDE_branch_h__ +#include "common.h" + #include "buffer.h" int git_branch_upstream__name( diff --git a/src/buf_text.h b/src/buf_text.h index c9c55af89..163bef1ad 100644 --- a/src/buf_text.h +++ b/src/buf_text.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_buf_text_h__ #define INCLUDE_buf_text_h__ +#include "common.h" + #include "buffer.h" typedef enum { diff --git a/src/buffer.c b/src/buffer.c index d135ebe4a..6dfcbfbe6 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -18,18 +18,19 @@ char git_buf__initbuf[1]; char git_buf__oom[1]; #define ENSURE_SIZE(b, d) \ - if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\ + if ((d) > (b)->asize && git_buf_grow((b), (d)) < 0)\ return -1; -void git_buf_init(git_buf *buf, size_t initial_size) +int git_buf_init(git_buf *buf, size_t initial_size) { buf->asize = 0; buf->size = 0; buf->ptr = git_buf__initbuf; - if (initial_size) - git_buf_grow(buf, initial_size); + ENSURE_SIZE(buf, initial_size); + + return 0; } int git_buf_try_grow( @@ -577,7 +578,7 @@ char *git_buf_detach(git_buf *buf) return data; } -void git_buf_attach(git_buf *buf, char *ptr, size_t asize) +int git_buf_attach(git_buf *buf, char *ptr, size_t asize) { git_buf_free(buf); @@ -588,9 +589,10 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize) buf->asize = (asize < buf->size) ? buf->size + 1 : asize; else /* pass 0 to fall back on strlen + 1 */ buf->asize = buf->size + 1; - } else { - git_buf_grow(buf, asize); } + + ENSURE_SIZE(buf, asize); + return 0; } void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size) @@ -724,9 +726,7 @@ int git_buf_join( GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); - if (git_buf_grow(buf, alloc_len) < 0) - return -1; - assert(buf->ptr); + ENSURE_SIZE(buf, alloc_len); /* fix up internal pointers */ if (offset_a >= 0) @@ -780,8 +780,7 @@ int git_buf_join3( GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b); GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c); GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1); - if (git_buf_grow(buf, len_total) < 0) - return -1; + ENSURE_SIZE(buf, len_total); tgt = buf->ptr; @@ -962,14 +961,14 @@ int git_buf_unquote(git_buf *buf) case '0': case '1': case '2': case '3': if (j == buf->size-3) { giterr_set(GITERR_INVALID, - "Truncated quoted character \\%c", ch); + "truncated quoted character \\%c", ch); return -1; } if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' || buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') { giterr_set(GITERR_INVALID, - "Truncated quoted character \\%c%c%c", + "truncated quoted character \\%c%c%c", buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]); return -1; } @@ -981,7 +980,7 @@ int git_buf_unquote(git_buf *buf) break; default: - giterr_set(GITERR_INVALID, "Invalid quoted character \\%c", ch); + giterr_set(GITERR_INVALID, "invalid quoted character \\%c", ch); return -1; } } @@ -995,6 +994,6 @@ int git_buf_unquote(git_buf *buf) return 0; invalid: - giterr_set(GITERR_INVALID, "Invalid quoted line"); + giterr_set(GITERR_INVALID, "invalid quoted line"); return -1; } diff --git a/src/buffer.h b/src/buffer.h index cdfca6d99..b0aece488 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -34,7 +34,7 @@ GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf) * For the cases where GIT_BUF_INIT cannot be used to do static * initialization. */ -extern void git_buf_init(git_buf *buf, size_t initial_size); +extern int git_buf_init(git_buf *buf, size_t initial_size); /** * Resize the buffer allocation to make more space. @@ -66,13 +66,14 @@ extern int git_buf_try_grow( * library, when providing git_buf's, may wish to provide a NULL ptr for * ease of handling. The buffer routines, however, expect a non-NULL ptr * always. This helper method simply handles NULL input, converting to a - * git_buf__initbuf. + * git_buf__initbuf. If a buffer with a non-NULL ptr is passed in, this method + * assures that the buffer is '\0'-terminated. */ extern void git_buf_sanitize(git_buf *buf); extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b); extern char *git_buf_detach(git_buf *buf); -extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize); +extern int git_buf_attach(git_buf *buf, char *ptr, size_t asize); /* Populates a `git_buf` where the contents are not "owned" by the * buffer, and calls to `git_buf_free` will not free the given buf. diff --git a/src/cache.c b/src/cache.c index ca5173c0d..cdd12979f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -5,18 +5,16 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "cache.h" + #include "repository.h" #include "commit.h" #include "thread-utils.h" #include "util.h" -#include "cache.h" #include "odb.h" #include "object.h" #include "git2/oid.h" -GIT__USE_OIDMAP - bool git_cache__enabled = true; ssize_t git_cache__max_storage = (256 * 1024 * 1024); git_atomic_ssize git_cache__current_storage = {0}; @@ -47,13 +45,13 @@ void git_cache_dump_stats(git_cache *cache) { git_cached_obj *object; - if (kh_size(cache->map) == 0) + if (git_cache_size(cache) == 0) return; - printf("Cache %p: %d items cached, %"PRIdZ" bytes\n", - cache, kh_size(cache->map), cache->used_memory); + printf("Cache %p: %"PRIuZ" items cached, %"PRIdZ" bytes\n", + cache, git_cache_size(cache), cache->used_memory); - kh_foreach_value(cache->map, object, { + git_oidmap_foreach_value(cache->map, object, { char oid_str[9]; printf(" %s%c %s (%"PRIuZ")\n", git_object_type2string(object->type), @@ -70,7 +68,7 @@ int git_cache_init(git_cache *cache) cache->map = git_oidmap_alloc(); GITERR_CHECK_ALLOC(cache->map); if (git_rwlock_init(&cache->lock)) { - giterr_set(GITERR_OS, "Failed to initialize cache rwlock"); + giterr_set(GITERR_OS, "failed to initialize cache rwlock"); return -1; } return 0; @@ -81,14 +79,14 @@ static void clear_cache(git_cache *cache) { git_cached_obj *evict = NULL; - if (kh_size(cache->map) == 0) + if (git_cache_size(cache) == 0) return; - kh_foreach_value(cache->map, evict, { + git_oidmap_foreach_value(cache->map, evict, { git_cached_obj_decref(evict); }); - kh_clear(oid, cache->map); + git_oidmap_clear(cache->map); git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory); cache->used_memory = 0; } @@ -119,22 +117,22 @@ static void cache_evict_entries(git_cache *cache) ssize_t evicted_memory = 0; /* do not infinite loop if there's not enough entries to evict */ - if (evict_count > kh_size(cache->map)) { + if (evict_count > git_cache_size(cache)) { clear_cache(cache); return; } while (evict_count > 0) { - khiter_t pos = seed++ % kh_end(cache->map); + khiter_t pos = seed++ % git_oidmap_end(cache->map); - if (kh_exist(cache->map, pos)) { - git_cached_obj *evict = kh_val(cache->map, pos); + if (git_oidmap_has_data(cache->map, pos)) { + git_cached_obj *evict = git_oidmap_value_at(cache->map, pos); evict_count--; evicted_memory += evict->size; git_cached_obj_decref(evict); - kh_del(oid, cache->map, pos); + git_oidmap_delete_at(cache->map, pos); } } @@ -156,9 +154,9 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0) return NULL; - pos = kh_get(oid, cache->map, oid); - if (pos != kh_end(cache->map)) { - entry = kh_val(cache->map, pos); + pos = git_oidmap_lookup_index(cache->map, oid); + if (git_oidmap_valid_index(cache->map, pos)) { + entry = git_oidmap_value_at(cache->map, pos); if (flags && entry->flags != flags) { entry = NULL; @@ -193,16 +191,14 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) if (git_cache__current_storage.val > git_cache__max_storage) cache_evict_entries(cache); - pos = kh_get(oid, cache->map, &entry->oid); + pos = git_oidmap_lookup_index(cache->map, &entry->oid); /* not found */ - if (pos == kh_end(cache->map)) { + if (!git_oidmap_valid_index(cache->map, pos)) { int rval; - pos = kh_put(oid, cache->map, &entry->oid, &rval); + git_oidmap_insert(cache->map, &entry->oid, entry, &rval); if (rval >= 0) { - kh_key(cache->map, pos) = &entry->oid; - kh_val(cache->map, pos) = entry; git_cached_obj_incref(entry); cache->used_memory += entry->size; git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size); @@ -210,7 +206,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) } /* found */ else { - git_cached_obj *stored_entry = kh_val(cache->map, pos); + git_cached_obj *stored_entry = git_oidmap_value_at(cache->map, pos); if (stored_entry->flags == entry->flags) { git_cached_obj_decref(entry); @@ -221,8 +217,8 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) git_cached_obj_decref(stored_entry); git_cached_obj_incref(entry); - kh_key(cache->map, pos) = &entry->oid; - kh_val(cache->map, pos) = entry; + git_oidmap_set_key_at(cache->map, pos, &entry->oid); + git_oidmap_set_value_at(cache->map, pos, entry); } else { /* NO OP */ } diff --git a/src/cache.h b/src/cache.h index 697123739..9c09954ae 100644 --- a/src/cache.h +++ b/src/cache.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_cache_h__ #define INCLUDE_cache_h__ +#include "common.h" + #include "git2/common.h" #include "git2/oid.h" #include "git2/odb.h" @@ -53,7 +55,7 @@ void *git_cache_get_any(git_cache *cache, const git_oid *oid); GIT_INLINE(size_t) git_cache_size(git_cache *cache) { - return (size_t)kh_size(cache->map); + return (size_t)git_oidmap_size(cache->map); } GIT_INLINE(void) git_cached_obj_incref(void *_obj) diff --git a/src/checkout.c b/src/checkout.c index f11102c8b..6c7a94441 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -5,10 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include <assert.h> - #include "checkout.h" +#include <assert.h> + #include "git2/repository.h" #include "git2/refs.h" #include "git2/tree.h" @@ -35,8 +35,6 @@ #include "pool.h" #include "strmap.h" -GIT__USE_STRMAP - /* See docs/checkout-internals.md for more information */ enum { @@ -212,6 +210,10 @@ static bool checkout_is_workdir_modified( if (baseitem->size && wditem->file_size != baseitem->size) return true; + /* if the workdir item is a directory, it cannot be a modified file */ + if (S_ISDIR(wditem->mode)) + return false; + if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0) return false; @@ -368,10 +370,8 @@ static int checkout_action_wd_only( */ const git_index_entry *e = git_index_get_byindex(data->index, pos); - if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) { - notify = GIT_CHECKOUT_NOTIFY_DIRTY; - remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); - } + if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) + return git_iterator_advance_into(wditem, workdir); } } @@ -1017,13 +1017,13 @@ static int checkout_conflicts_load_byname_entry( *theirs_out = NULL; if (!name_entry->ancestor) { - giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor"); + giterr_set(GITERR_INDEX, "a NAME entry exists without an ancestor"); error = -1; goto done; } if (!name_entry->ours && !name_entry->theirs) { - giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs"); + giterr_set(GITERR_INDEX, "a NAME entry exists without an ours or theirs"); error = -1; goto done; } @@ -1031,7 +1031,7 @@ static int checkout_conflicts_load_byname_entry( if ((ancestor = checkout_conflicts_search_ancestor(data, name_entry->ancestor)) == NULL) { giterr_set(GITERR_INDEX, - "A NAME entry referenced ancestor entry '%s' which does not exist in the main index", + "a NAME entry referenced ancestor entry '%s' which does not exist in the main index", name_entry->ancestor); error = -1; goto done; @@ -1043,7 +1043,7 @@ static int checkout_conflicts_load_byname_entry( else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL || ours->ours == NULL) { giterr_set(GITERR_INDEX, - "A NAME entry referenced our entry '%s' which does not exist in the main index", + "a NAME entry referenced our entry '%s' which does not exist in the main index", name_entry->ours); error = -1; goto done; @@ -1058,7 +1058,7 @@ static int checkout_conflicts_load_byname_entry( else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL || theirs->theirs == NULL) { giterr_set(GITERR_INDEX, - "A NAME entry referenced their entry '%s' which does not exist in the main index", + "a NAME entry referenced their entry '%s' which does not exist in the main index", name_entry->theirs); error = -1; goto done; @@ -1157,7 +1157,7 @@ static int checkout_conflicts_mark_directoryfile( if ((error = git_index_find(&j, index, path)) < 0) { if (error == GIT_ENOTFOUND) giterr_set(GITERR_INDEX, - "Index inconsistency, could not find entry for expected conflict '%s'", path); + "index inconsistency, could not find entry for expected conflict '%s'", path); goto done; } @@ -1165,7 +1165,7 @@ static int checkout_conflicts_mark_directoryfile( for (; j < len; j++) { if ((entry = git_index_get_byindex(index, j)) == NULL) { giterr_set(GITERR_INDEX, - "Index inconsistency, truncated index while loading expected conflict '%s'", path); + "index inconsistency, truncated index while loading expected conflict '%s'", path); error = -1; goto done; } @@ -1250,14 +1250,14 @@ static int checkout_verify_paths( if (action & CHECKOUT_ACTION__REMOVE) { if (!git_path_isvalid(repo, delta->old_file.path, flags)) { - giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path); + giterr_set(GITERR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path); return -1; } } if (action & ~CHECKOUT_ACTION__REMOVE) { if (!git_path_isvalid(repo, delta->new_file.path, flags)) { - giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->new_file.path); + giterr_set(GITERR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path); return -1; } } @@ -1426,7 +1426,7 @@ static int mkpath2file( */ error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES); } else if (errno != ENOENT) { - giterr_set(GITERR_OS, "Failed to stat file '%s'", path); + giterr_set(GITERR_OS, "failed to stat '%s'", path); return GIT_EEXISTS; } else { giterr_clear(); @@ -1450,7 +1450,7 @@ static int checkout_stream_write( int ret; if ((ret = p_write(stream->fd, buffer, len)) < 0) - giterr_set(GITERR_OS, "Could not write to '%s'", stream->path); + giterr_set(GITERR_OS, "could not write to '%s'", stream->path); return ret; } @@ -1499,7 +1499,7 @@ static int blob_content_to_file( mode = GIT_FILEMODE_BLOB; if ((fd = p_open(path, flags, mode)) < 0) { - giterr_set(GITERR_OS, "Could not open '%s' for writing", path); + giterr_set(GITERR_OS, "could not open '%s' for writing", path); return fd; } @@ -1536,7 +1536,7 @@ static int blob_content_to_file( data->perfdata.stat_calls++; if ((error = p_stat(path, st)) < 0) { - giterr_set(GITERR_OS, "Error statting '%s'", path); + giterr_set(GITERR_OS, "failed to stat '%s'", path); return error; } @@ -1563,7 +1563,7 @@ static int blob_content_to_link( if (data->can_symlink) { if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0) - giterr_set(GITERR_OS, "Could not create symlink %s", path); + giterr_set(GITERR_OS, "could not create symlink %s", path); } else { error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path); } @@ -1572,7 +1572,7 @@ static int blob_content_to_link( data->perfdata.stat_calls++; if ((error = p_lstat(path, st)) < 0) - giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path); + giterr_set(GITERR_CHECKOUT, "could not stat symlink %s", path); st->st_mode = GIT_FILEMODE_LINK; } @@ -1617,7 +1617,7 @@ static int checkout_submodule_update_index( data->perfdata.stat_calls++; if (p_stat(fullpath->ptr, &st) < 0) { giterr_set( - GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path); + GITERR_CHECKOUT, "could not stat submodule %s\n", file->path); return GIT_ENOTFOUND; } @@ -1690,7 +1690,7 @@ static int checkout_safe_for_update_only( return 0; /* otherwise, stat error and no update */ - giterr_set(GITERR_OS, "Failed to stat file '%s'", path); + giterr_set(GITERR_OS, "failed to stat '%s'", path); return -1; } @@ -1962,7 +1962,7 @@ static int checkout_path_suffixed(git_buf *path, const char *suffix) if (i == INT_MAX) { git_buf_truncate(path, path_len); - giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path); + giterr_set(GITERR_CHECKOUT, "could not write '%s': working directory file exists", path->ptr); return GIT_EEXISTS; } @@ -2093,7 +2093,7 @@ static int checkout_write_merge( goto done; if (result.path == NULL || result.mode == 0) { - giterr_set(GITERR_CHECKOUT, "Could not merge contents of file"); + giterr_set(GITERR_CHECKOUT, "could not merge contents of file"); error = GIT_ECONFLICT; goto done; } @@ -2317,8 +2317,6 @@ static void checkout_data_clear(checkout_data *data) git__free(data->pfx); data->pfx = NULL; - git_strmap_free(data->mkdir_map); - git_buf_free(&data->target_path); git_buf_free(&data->tmp); @@ -2326,6 +2324,7 @@ static void checkout_data_clear(checkout_data *data) data->index = NULL; git_strmap_free(data->mkdir_map); + data->mkdir_map = NULL; git_attr_session__free(&data->attr_session); } @@ -2341,7 +2340,7 @@ static int checkout_data_init( memset(data, 0, sizeof(*data)); if (!repo) { - giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing"); + giterr_set(GITERR_CHECKOUT, "cannot checkout nothing"); return -1; } @@ -2465,7 +2464,7 @@ static int checkout_data_init( data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; else { giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'", - conflict_style); + conflict_style->value); error = -1; git_config_entry_free(conflict_style); goto cleanup; @@ -2549,6 +2548,10 @@ int git_checkout_iterator( GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; baseline_opts.start = data.pfx; baseline_opts.end = data.pfx; + if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) { + baseline_opts.pathlist.count = opts->paths.count; + baseline_opts.pathlist.strings = opts->paths.strings; + } if (data.opts.baseline_index) { if ((error = git_iterator_for_index( @@ -2643,7 +2646,7 @@ int git_checkout_index( if (!index && !repo) { giterr_set(GITERR_CHECKOUT, - "Must provide either repository or index to checkout"); + "must provide either repository or index to checkout"); return -1; } @@ -2651,7 +2654,7 @@ int git_checkout_index( git_index_owner(index) && git_index_owner(index) != repo) { giterr_set(GITERR_CHECKOUT, - "Index to checkout does not match repository"); + "index to checkout does not match repository"); return -1; } else if(index && repo && !git_index_owner(index)) { GIT_REFCOUNT_OWN(index, repo); @@ -2690,12 +2693,12 @@ int git_checkout_tree( if (!treeish && !repo) { giterr_set(GITERR_CHECKOUT, - "Must provide either repository or tree to checkout"); + "must provide either repository or tree to checkout"); return -1; } if (treeish && repo && git_object_owner(treeish) != repo) { giterr_set(GITERR_CHECKOUT, - "Object to checkout does not match repository"); + "object to checkout does not match repository"); return -1; } @@ -2705,7 +2708,7 @@ int git_checkout_tree( if (treeish) { if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) { giterr_set( - GITERR_CHECKOUT, "Provided object cannot be peeled to a tree"); + GITERR_CHECKOUT, "provided object cannot be peeled to a tree"); return -1; } } diff --git a/src/checkout.h b/src/checkout.h index 60aa29b26..517fbf3b1 100644 --- a/src/checkout.h +++ b/src/checkout.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_checkout_h__ #define INCLUDE_checkout_h__ +#include "common.h" + #include "git2/checkout.h" #include "iterator.h" diff --git a/src/cherrypick.c b/src/cherrypick.c index c92975194..e42b74815 100644 --- a/src/cherrypick.c +++ b/src/cherrypick.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "repository.h" #include "filebuf.h" #include "merge.h" @@ -28,7 +29,7 @@ static int write_cherrypick_head( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 && + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_CHERRYPICK_HEAD_FILE)) >= 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 && (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) error = git_filebuf_commit(&file); @@ -49,7 +50,7 @@ static int write_merge_msg( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 || (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0) goto cleanup; @@ -130,13 +131,13 @@ int git_cherrypick_commit( if (git_commit_parentcount(cherrypick_commit) > 1) { if (!mainline) return cherrypick_seterr(cherrypick_commit, - "Mainline branch is not specified but %s is a merge commit"); + "mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return cherrypick_seterr(cherrypick_commit, - "Mainline branch specified but %s is not a merge commit"); + "mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(cherrypick_commit); } diff --git a/src/clone.c b/src/clone.c index 0d4756e28..8764bb728 100644 --- a/src/clone.c +++ b/src/clone.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "clone.h" + #include <assert.h> #include "git2/clone.h" @@ -16,7 +18,6 @@ #include "git2/commit.h" #include "git2/tree.h" -#include "common.h" #include "remote.h" #include "fileops.h" #include "refs.h" @@ -513,9 +514,8 @@ static int clone_local_into(git_repository *repo, git_remote *remote, const git_ return error; } - git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR); - git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR); - if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) { + if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0 + || git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) { error = -1; goto cleanup; } diff --git a/src/clone.h b/src/clone.h index 14ca5d44c..864b59029 100644 --- a/src/clone.h +++ b/src/clone.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_clone_h__ #define INCLUDE_clone_h__ +#include "common.h" + +#include "git2/clone.h" + extern int git_clone__should_clone_local(const char *url, git_clone_local_t local); #endif diff --git a/src/commit.c b/src/commit.c index 99a80855c..838688bb8 100644 --- a/src/commit.c +++ b/src/commit.c @@ -5,13 +5,14 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "commit.h" + #include "git2/common.h" #include "git2/object.h" #include "git2/repository.h" #include "git2/signature.h" #include "git2/sys/commit.h" -#include "common.h" #include "odb.h" #include "commit.h" #include "signature.h" @@ -159,6 +160,9 @@ static int git_commit__create_internal( if (git_repository_odb__weakptr(&odb, repo) < 0) goto cleanup; + if (git_odb__freshen(odb, tree) < 0) + goto cleanup; + if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0) goto cleanup; @@ -459,15 +463,16 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj) buffer = buffer_start + header_len + 1; /* extract commit message */ - if (buffer <= buffer_end) { + if (buffer <= buffer_end) commit->raw_message = git__strndup(buffer, buffer_end - buffer); - GITERR_CHECK_ALLOC(commit->raw_message); - } + else + commit->raw_message = git__strdup(""); + GITERR_CHECK_ALLOC(commit->raw_message); return 0; bad_buffer: - giterr_set(GITERR_OBJECT, "Failed to parse bad commit object"); + giterr_set(GITERR_OBJECT, "failed to parse bad commit object"); return -1; } @@ -597,7 +602,7 @@ int git_commit_parent( parent_id = git_commit_parent_id(commit, n); if (parent_id == NULL) { - giterr_set(GITERR_INVALID, "Parent %u does not exist", n); + giterr_set(GITERR_INVALID, "parent %u does not exist", n); return GIT_ENOTFOUND; } @@ -641,7 +646,7 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char * { const char *eol, *buf = commit->raw_header; - git_buf_sanitize(out); + git_buf_clear(out); while ((eol = strchr(buf, '\n'))) { /* We can skip continuations here */ @@ -705,8 +710,8 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r const char *h, *eol; int error; - git_buf_sanitize(signature); - git_buf_sanitize(signed_data); + git_buf_clear(signature); + git_buf_clear(signed_data); if (!field) field = "gpgsig"; @@ -765,8 +770,9 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r if (git_buf_oom(signature)) goto oom; + error = git_buf_puts(signed_data, eol+1); git_odb_object_free(obj); - return git_buf_puts(signed_data, eol+1); + return error; } giterr_set(GITERR_OBJECT, "this commit is not signed"); diff --git a/src/commit.h b/src/commit.h index d01ac2b2f..781809d70 100644 --- a/src/commit.h +++ b/src/commit.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_commit_h__ #define INCLUDE_commit_h__ +#include "common.h" + #include "git2/commit.h" #include "tree.h" #include "repository.h" diff --git a/src/commit_list.c b/src/commit_list.c index 28948c88b..96bd9dc15 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -6,17 +6,22 @@ */ #include "commit_list.h" -#include "common.h" + #include "revwalk.h" #include "pool.h" #include "odb.h" int git_commit_list_time_cmp(const void *a, const void *b) { - const git_commit_list_node *commit_a = a; - const git_commit_list_node *commit_b = b; + int64_t time_a = ((git_commit_list_node *) a)->time; + int64_t time_b = ((git_commit_list_node *) b)->time; + + if (time_a < time_b) + return 1; + if (time_a > time_b) + return -1; - return (commit_a->time < commit_b->time); + return 0; } git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p) @@ -56,7 +61,7 @@ static int commit_error(git_commit_list_node *commit, const char *msg) git_oid_fmt(commit_oid, &commit->oid); commit_oid[GIT_OID_HEXSZ] = '\0'; - giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg); + giterr_set(GITERR_ODB, "failed to parse commit %s - %s", commit_oid, msg); return -1; } @@ -186,7 +191,7 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit) return error; if (obj->cached.type != GIT_OBJ_COMMIT) { - giterr_set(GITERR_INVALID, "Object is no commit object"); + giterr_set(GITERR_INVALID, "object is no commit object"); error = -1; } else error = commit_quick_parse( diff --git a/src/commit_list.h b/src/commit_list.h index a6967bcef..a7551a2bc 100644 --- a/src/commit_list.h +++ b/src/commit_list.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_commit_list_h__ #define INCLUDE_commit_list_h__ +#include "common.h" + #include "git2/oid.h" #define PARENT1 (1 << 0) @@ -28,6 +30,7 @@ typedef struct git_commit_list_node { uninteresting:1, topo_delay:1, parsed:1, + added:1, flags : FLAG_BITS; unsigned short in_degree; diff --git a/src/common.h b/src/common.h index 51fb9186e..d3622d3eb 100644 --- a/src/common.h +++ b/src/common.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_common_h__ #define INCLUDE_common_h__ +#ifndef LIBGIT2_NO_FEATURES_H +# include "git2/sys/features.h" +#endif + #include "git2/common.h" #include "cc-compat.h" @@ -47,10 +51,6 @@ # ifdef GIT_THREADS # include "win32/thread.h" # endif -# if defined(GIT_MSVC_CRTDBG) -# include "win32/w32_stack.h" -# include "win32/w32_crtdbg_stacktrace.h" -# endif #else @@ -103,7 +103,8 @@ /** * Set the error message for this thread, formatting as needed. */ -void giterr_set(int error_class, const char *string, ...); + +void giterr_set(int error_class, const char *string, ...) GIT_FORMAT_PRINTF(2, 3); /** * Set the error message for a regex failure, using the internal regex @@ -187,7 +188,7 @@ GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expect if (actual > 0 && actual <= expected_max) return 0; - giterr_set(GITERR_INVALID, "Invalid version %d on %s", actual, name); + giterr_set(GITERR_INVALID, "invalid version %d on %s", actual, name); return -1; } #define GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) return -1 diff --git a/src/config.c b/src/config.c index f4d4cb2b9..602e0e827 100644 --- a/src/config.c +++ b/src/config.c @@ -5,9 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" -#include "sysdir.h" #include "config.h" + +#include "sysdir.h" #include "git2/config.h" #include "git2/sys/config.h" #include "vector.h" @@ -109,7 +109,7 @@ int git_config_add_file_ondisk( res = p_stat(path, &st); if (res < 0 && errno != ENOENT) { - giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path); + giterr_set(GITERR_CONFIG, "failed to stat '%s'", path); return -1; } @@ -203,7 +203,7 @@ static int find_internal_file_by_level( if (pos == -1) { giterr_set(GITERR_CONFIG, - "No config file exists for the given level '%i'", (int)level); + "no config file exists for the given level '%i'", (int)level); return GIT_ENOTFOUND; } @@ -218,7 +218,7 @@ static int duplicate_level(void **old_raw, void *new_raw) GIT_UNUSED(new_raw); - giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level); + giterr_set(GITERR_CONFIG, "a file with the same level (%i) has already been added to the config", (int)(*old)->level); return GIT_EEXISTS; } @@ -478,7 +478,7 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf iter = git__calloc(1, sizeof(all_iter)); GITERR_CHECK_ALLOC(iter); - if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) { + if ((result = p_regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) { giterr_set_regex(&iter->regex, result); git__free(iter); return -1; @@ -512,7 +512,7 @@ int git_config_backend_foreach_match( int error = 0; if (regexp != NULL) { - if ((error = regcomp(®ex, regexp, REG_EXTENDED)) != 0) { + if ((error = p_regcomp(®ex, regexp, REG_EXTENDED)) != 0) { giterr_set_regex(®ex, error); regfree(®ex); return -1; @@ -576,22 +576,50 @@ int git_config_foreach_match( * Setters **************/ -static int config_error_nofiles(const char *name) +typedef enum { + BACKEND_USE_SET, + BACKEND_USE_DELETE +} backend_use; + +static const char *uses[] = { + "set", + "delete" +}; + +static int get_backend_for_use(git_config_backend **out, + git_config *cfg, const char *name, backend_use use) { + size_t i; + file_internal *f; + + *out = NULL; + + if (git_vector_length(&cfg->files) == 0) { + giterr_set(GITERR_CONFIG, + "cannot %s value for '%s' when no config files exist", + uses[use], name); + return GIT_ENOTFOUND; + } + + git_vector_foreach(&cfg->files, i, f) { + if (!f->file->readonly) { + *out = f->file; + return 0; + } + } + giterr_set(GITERR_CONFIG, - "Cannot set value for '%s' when no config files exist", name); + "cannot %s value for '%s' when all config files are readonly", + uses[use], name); return GIT_ENOTFOUND; } int git_config_delete_entry(git_config *cfg, const char *name) { git_config_backend *file; - file_internal *internal; - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0) + return GIT_ENOTFOUND; return file->del(file, name); } @@ -617,17 +645,14 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) { int error; git_config_backend *file; - file_internal *internal; if (!value) { - giterr_set(GITERR_CONFIG, "The value to set cannot be NULL"); + giterr_set(GITERR_CONFIG, "the value to set cannot be NULL"); return -1; } - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_SET) < 0) + return GIT_ENOTFOUND; error = file->set(file, name, value); @@ -674,7 +699,7 @@ int git_config__update_entry( static int config_error_notfound(const char *name) { - giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); + giterr_set(GITERR_CONFIG, "config value '%s' was not found", name); return GIT_ENOTFOUND; } @@ -1003,7 +1028,7 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config goto on_error; if (regexp != NULL) { - error = regcomp(&iter->regex, regexp, REG_EXTENDED); + error = p_regcomp(&iter->regex, regexp, REG_EXTENDED); if (error != 0) { giterr_set_regex(&iter->regex, error); error = -1; @@ -1032,12 +1057,9 @@ on_error: int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) { git_config_backend *file; - file_internal *internal; - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0) + return GIT_ENOTFOUND; return file->set_multivar(file, name, regexp, value); } @@ -1045,12 +1067,9 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp) { git_config_backend *file; - file_internal *internal; - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0) + return GIT_ENOTFOUND; return file->del_multivar(file, name, regexp); } @@ -1236,7 +1255,7 @@ int git_config_lookup_map_value( } fail_parse: - giterr_set(GITERR_CONFIG, "Failed to map '%s'", value); + giterr_set(GITERR_CONFIG, "failed to map '%s'", value); return -1; } @@ -1270,7 +1289,7 @@ int git_config_parse_bool(int *out, const char *value) return 0; } - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value); + giterr_set(GITERR_CONFIG, "failed to parse '%s' as a boolean value", value); return -1; } @@ -1313,7 +1332,7 @@ int git_config_parse_int64(int64_t *out, const char *value) } fail_parse: - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)"); + giterr_set(GITERR_CONFIG, "failed to parse '%s' as an integer", value ? value : "(null)"); return -1; } @@ -1333,15 +1352,12 @@ int git_config_parse_int32(int32_t *out, const char *value) return 0; fail_parse: - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)"); + giterr_set(GITERR_CONFIG, "failed to parse '%s' as a 32-bit integer", value ? value : "(null)"); return -1; } int git_config_parse_path(git_buf *out, const char *value) { - int error = 0; - const git_buf *home; - assert(out && value); git_buf_sanitize(out); @@ -1352,16 +1368,7 @@ int git_config_parse_path(git_buf *out, const char *value) return -1; } - if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0) - return error; - - git_buf_sets(out, home->ptr); - git_buf_puts(out, value + 1); - - if (git_buf_oom(out)) - return -1; - - return 0; + return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL); } return git_buf_sets(out, value); @@ -1398,7 +1405,7 @@ int git_config__normalize_name(const char *in, char **out) invalid: git__free(name); - giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in); + giterr_set(GITERR_CONFIG, "invalid config item name '%s'", in); return GIT_EINVALIDSPEC; } @@ -1461,7 +1468,7 @@ int git_config_rename_section( replace.ptr, strchr(replace.ptr, '.'))) < 0) { giterr_set( - GITERR_CONFIG, "Invalid config section '%s'", new_section_name); + GITERR_CONFIG, "invalid config section '%s'", new_section_name); goto cleanup; } diff --git a/src/config.h b/src/config.h index 00c12b50d..a5fcf2e84 100644 --- a/src/config.h +++ b/src/config.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_config_h__ #define INCLUDE_config_h__ +#include "common.h" + #include "git2.h" #include "git2/config.h" #include "vector.h" diff --git a/src/config_cache.c b/src/config_cache.c index dbea871b9..0efb1a789 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "fileops.h" #include "repository.h" #include "config.h" @@ -78,6 +79,7 @@ static struct map_data _cvar_maps[] = { {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT }, {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT }, {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT }, + {"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT }, }; int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar) diff --git a/src/config_file.c b/src/config_file.c index e33b83738..00d6d9e13 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -5,7 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "config_file.h" + #include "config.h" #include "filebuf.h" #include "sysdir.h" @@ -21,8 +22,6 @@ #include <sys/types.h> #include <regex.h> -GIT__USE_STRMAP - typedef struct cvar_t { struct cvar_t *next; git_config_entry *entry; @@ -76,9 +75,14 @@ typedef struct git_config_file_iter { (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\ (iter) = (tmp)) -struct reader { +struct config_file { git_oid checksum; - char *file_path; + char *path; + git_array_t(struct config_file) includes; +}; + +struct reader { + struct config_file *file; git_buf buffer; char *read_ptr; int line_number; @@ -102,13 +106,11 @@ typedef struct { git_config_level_t level; - git_array_t(struct reader) readers; - bool locked; git_filebuf locked_buf; git_buf locked_content; - char *file_path; + struct config_file file; } diskfile_backend; typedef struct { @@ -117,7 +119,7 @@ typedef struct { diskfile_backend *snapshot_from; } diskfile_readonly_backend; -static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth); +static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth); static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value); static char *escape_value(const char *ptr); @@ -126,8 +128,8 @@ static int config_snapshot(git_config_backend **out, git_config_backend *in); static void set_parse_error(struct reader *reader, int col, const char *error_str) { - giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)", - error_str, reader->file_path, reader->line_number, col); + giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)", + error_str, reader->file->path, reader->line_number, col); } static int config_error_readonly(void) @@ -179,7 +181,7 @@ static int append_entry(git_strmap *values, cvar_t *var) pos = git_strmap_lookup_index(values, var->entry->name); if (!git_strmap_valid_index(values, pos)) { - git_strmap_insert(values, var->entry->name, var, error); + git_strmap_insert(values, var->entry->name, var, &error); } else { existing = git_strmap_value_at(values, pos); while (existing->next != NULL) { @@ -233,7 +235,7 @@ static refcounted_strmap *refcounted_strmap_take(diskfile_header *h) refcounted_strmap *map; if (git_mutex_lock(&h->values_mutex) < 0) { - giterr_set(GITERR_OS, "Failed to lock config backend"); + giterr_set(GITERR_OS, "failed to lock config backend"); return NULL; } @@ -263,10 +265,25 @@ static int refcounted_strmap_alloc(refcounted_strmap **out) return error; } +static void config_file_clear(struct config_file *file) +{ + struct config_file *include; + uint32_t i; + + if (file == NULL) + return; + + git_array_foreach(file->includes, i, include) { + config_file_clear(include); + } + git_array_clear(file->includes); + + git__free(file->path); +} + static int config_open(git_config_backend *cfg, git_config_level_t level) { int res; - struct reader *reader; diskfile_backend *b = (diskfile_backend *)cfg; b->level = level; @@ -274,112 +291,102 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) if ((res = refcounted_strmap_alloc(&b->header.values)) < 0) return res; - git_array_init(b->readers); - reader = git_array_alloc(b->readers); - if (!reader) { - refcounted_strmap_free(b->header.values); - return -1; - } - memset(reader, 0, sizeof(struct reader)); - - reader->file_path = git__strdup(b->file_path); - GITERR_CHECK_ALLOC(reader->file_path); - - git_buf_init(&reader->buffer, 0); - res = git_futils_readbuffer_updated( - &reader->buffer, b->file_path, &reader->checksum, NULL); - /* It's fine if the file doesn't exist */ - if (res == GIT_ENOTFOUND) + if (!git_path_exists(b->file.path)) return 0; - if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) { + if (res < 0 || (res = config_read(b->header.values->values, &b->file, level, 0)) < 0) { refcounted_strmap_free(b->header.values); b->header.values = NULL; } - reader = git_array_get(b->readers, 0); - git_buf_free(&reader->buffer); - return res; } -/* The meat of the refresh, as we want to use it in different places */ -static int config__refresh(git_config_backend *cfg) +static int config_is_modified(int *modified, struct config_file *file) { - refcounted_strmap *values = NULL, *tmp; - diskfile_backend *b = (diskfile_backend *)cfg; - struct reader *reader = NULL; - int error = 0; + struct config_file *include; + git_buf buf = GIT_BUF_INIT; + git_oid hash; + uint32_t i; + int error; - if ((error = refcounted_strmap_alloc(&values)) < 0) - goto out; + *modified = 0; - reader = git_array_get(b->readers, git_array_size(b->readers) - 1); - GITERR_CHECK_ALLOC(reader); + if ((error = git_futils_readbuffer(&buf, file->path)) < 0) + goto out; - if ((error = config_read(values->values, b, reader, b->level, 0)) < 0) + if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0) goto out; - if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) { - giterr_set(GITERR_OS, "Failed to lock config backend"); + if (!git_oid_equal(&hash, &file->checksum)) { + *modified = 1; goto out; } - tmp = b->header.values; - b->header.values = values; - values = tmp; - - git_mutex_unlock(&b->header.values_mutex); + git_array_foreach(file->includes, i, include) { + if ((error = config_is_modified(modified, include)) < 0 || *modified) + goto out; + } out: - refcounted_strmap_free(values); - if (reader) - git_buf_free(&reader->buffer); + git_buf_free(&buf); + return error; } static int config_refresh(git_config_backend *cfg) { - int error = 0, updated = 0, any_updated = 0; diskfile_backend *b = (diskfile_backend *)cfg; - struct reader *reader = NULL; + refcounted_strmap *values = NULL, *tmp; + struct config_file *include; + int error, modified; uint32_t i; - for (i = 0; i < git_array_size(b->readers); i++) { - reader = git_array_get(b->readers, i); - error = git_futils_readbuffer_updated( - &reader->buffer, reader->file_path, - &reader->checksum, &updated); + error = config_is_modified(&modified, &b->file); + if (error < 0 && error != GIT_ENOTFOUND) + goto out; - if (error < 0 && error != GIT_ENOTFOUND) - return error; + if (!modified) + return 0; - if (updated) - any_updated = 1; + if ((error = refcounted_strmap_alloc(&values)) < 0) + goto out; + + /* Reparse the current configuration */ + git_array_foreach(b->file.includes, i, include) { + config_file_clear(include); } + git_array_clear(b->file.includes); - if (!any_updated) - return (error == GIT_ENOTFOUND) ? 0 : error; + if ((error = config_read(values->values, &b->file, b->level, 0)) < 0) + goto out; - return config__refresh(cfg); + if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) { + giterr_set(GITERR_OS, "failed to lock config backend"); + goto out; + } + + tmp = b->header.values; + b->header.values = values; + values = tmp; + + git_mutex_unlock(&b->header.values_mutex); + +out: + refcounted_strmap_free(values); + + return (error == GIT_ENOTFOUND) ? 0 : error; } static void backend_free(git_config_backend *_backend) { diskfile_backend *backend = (diskfile_backend *)_backend; - uint32_t i; if (backend == NULL) return; - for (i = 0; i < git_array_size(backend->readers); i++) { - struct reader *r = git_array_get(backend->readers, i); - git__free(r->file_path); - } - git_array_clear(backend->readers); - - git__free(backend->file_path); + config_file_clear(&backend->file); refcounted_strmap_free(backend->header.values); git_mutex_free(&backend->header.values_mutex); git__free(backend); @@ -479,7 +486,13 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val cvar_t *existing = git_strmap_value_at(values, pos); if (existing->next != NULL) { - giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set"); + giterr_set(GITERR_CONFIG, "multivar incompatible with simple set"); + ret = -1; + goto out; + } + + if (existing->included) { + giterr_set(GITERR_CONFIG, "modifying included variable is not supported"); ret = -1; goto out; } @@ -570,7 +583,7 @@ static int config_set_multivar( if ((result = git_config__normalize_name(name, &key)) < 0) return result; - result = regcomp(&preg, regexp, REG_EXTENDED); + result = p_regcomp(&preg, regexp, REG_EXTENDED); if (result != 0) { giterr_set_regex(&preg, result); result = -1; @@ -611,15 +624,20 @@ static int config_delete(git_config_backend *cfg, const char *name) if (!git_strmap_valid_index(values, pos)) { refcounted_strmap_free(map); - giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); + giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name); return GIT_ENOTFOUND; } var = git_strmap_value_at(values, pos); refcounted_strmap_free(map); + if (var->included) { + giterr_set(GITERR_CONFIG, "cannot delete included variable"); + return -1; + } + if (var->next != NULL) { - giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete"); + giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete"); return -1; } @@ -651,13 +669,13 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con if (!git_strmap_valid_index(values, pos)) { refcounted_strmap_free(map); git__free(key); - giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); + giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name); return GIT_ENOTFOUND; } refcounted_strmap_free(map); - result = regcomp(&preg, regexp, REG_EXTENDED); + result = p_regcomp(&preg, regexp, REG_EXTENDED); if (result != 0) { giterr_set_regex(&preg, result); result = -1; @@ -687,10 +705,10 @@ static int config_lock(git_config_backend *_cfg) diskfile_backend *cfg = (diskfile_backend *) _cfg; int error; - if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) + if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0) return error; - error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path); + error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path); if (error < 0 && error != GIT_ENOTFOUND) { git_filebuf_cleanup(&cfg->locked_buf); return error; @@ -728,8 +746,9 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; git_mutex_init(&backend->header.values_mutex); - backend->file_path = git__strdup(path); - GITERR_CHECK_ALLOC(backend->file_path); + backend->file.path = git__strdup(path); + GITERR_CHECK_ALLOC(backend->file.path); + git_array_init(backend->file.includes); backend->header.parent.open = config_open; backend->header.parent.get = config_get; @@ -1029,7 +1048,7 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con first_quote = strchr(line, '"'); if (first_quote == NULL) { set_parse_error(reader, 0, "Missing quotation marks in section header"); - return -1; + goto end_error; } last_quote = strrchr(line, '"'); @@ -1037,14 +1056,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con if (quoted_len == 0) { set_parse_error(reader, 0, "Missing closing quotation mark in section header"); - return -1; + goto end_error; } GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); - git_buf_grow(&buf, alloc_len); - git_buf_printf(&buf, "%s.", base_name); + if (git_buf_grow(&buf, alloc_len) < 0 || + git_buf_printf(&buf, "%s.", base_name) < 0) + goto end_error; rpos = 0; @@ -1060,8 +1080,7 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con switch (c) { case 0: set_parse_error(reader, 0, "Unexpected end-of-line in section header"); - git_buf_free(&buf); - return -1; + goto end_error; case '"': goto end_parse; @@ -1071,8 +1090,7 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con if (c == 0) { set_parse_error(reader, rpos, "Unexpected end-of-line in section header"); - git_buf_free(&buf); - return -1; + goto end_error; } default: @@ -1084,6 +1102,9 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con } while (line + rpos < last_quote); end_parse: + if (git_buf_oom(&buf)) + goto end_error; + if (line[rpos] != '"' || line[rpos + 1] != ']') { set_parse_error(reader, rpos, "Unexpected text after closing quotes"); git_buf_free(&buf); @@ -1092,6 +1113,11 @@ end_parse: *section_name = git_buf_detach(&buf); return 0; + +end_error: + git_buf_free(&buf); + + return -1; } static int parse_section_header(struct reader *reader, char **section_out) @@ -1251,7 +1277,7 @@ static int included_path(git_buf *out, const char *dir, const char *path) { /* From the user's home */ if (path[0] == '~' && path[1] == '/') - return git_sysdir_find_global_file(out, &path[1]); + return git_sysdir_expand_global_file(out, &path[1]); return git_path_join_unrooted(out, path, dir, NULL); } @@ -1262,7 +1288,7 @@ static const char *escaped = "\n\t\b\"\\"; /* Escape the values to write them to the file */ static char *escape_value(const char *ptr) { - git_buf buf = GIT_BUF_INIT; + git_buf buf; size_t len; const char *esc; @@ -1272,7 +1298,8 @@ static char *escape_value(const char *ptr) if (!len) return git__calloc(1, sizeof(char)); - git_buf_grow(&buf, len); + if (git_buf_init(&buf, len) < 0) + return NULL; while (*ptr != '\0') { if ((esc = strchr(escaped, *ptr)) != NULL) { @@ -1325,7 +1352,7 @@ static int unescape_line( *fixed++ = escaped[esc - escapes]; } else { git__free(str); - giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr); + giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr); return -1; } } @@ -1480,10 +1507,10 @@ on_error: static int config_parse( struct reader *reader, - int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data), - int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data), - int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data), - int (*on_eof)(struct reader **reader, const char *current_section, void *data), + int (*on_section)(struct reader *reader, const char *current_section, const char *line, size_t line_len, void *data), + int (*on_variable)(struct reader *reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data), + int (*on_comment)(struct reader *reader, const char *line, size_t line_len, void *data), + int (*on_eof)(struct reader *reader, const char *current_section, void *data), void *data) { char *current_section = NULL, *var_name, *var_value, *line_start; @@ -1509,7 +1536,7 @@ static int config_parse( if ((result = parse_section_header(reader, ¤t_section)) == 0 && on_section) { line_len = reader->read_ptr - line_start; - result = on_section(&reader, current_section, line_start, line_len, data); + result = on_section(reader, current_section, line_start, line_len, data); } break; @@ -1520,21 +1547,21 @@ static int config_parse( if (on_comment) { line_len = reader->read_ptr - line_start; - result = on_comment(&reader, line_start, line_len, data); + result = on_comment(reader, line_start, line_len, data); } break; default: /* assume variable declaration */ if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) { line_len = reader->read_ptr - line_start; - result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data); + result = on_variable(reader, current_section, var_name, var_value, line_start, line_len, data); } break; } } if (on_eof) - result = on_eof(&reader, current_section, data); + result = on_eof(reader, current_section, data); git__free(current_section); return result; @@ -1542,14 +1569,12 @@ static int config_parse( struct parse_data { git_strmap *values; - diskfile_backend *cfg_file; - uint32_t reader_idx; git_config_level_t level; int depth; }; static int read_on_variable( - struct reader **reader, + struct reader *reader, const char *current_section, char *var_name, char *var_value, @@ -1591,21 +1616,13 @@ static int read_on_variable( /* Add or append the new config option */ if (!git__strcmp(var->entry->name, "include.path")) { - struct reader *r; + struct config_file *include; git_buf path = GIT_BUF_INIT; char *dir; - uint32_t index; - r = git_array_alloc(parse_data->cfg_file->readers); - /* The reader may have been reallocated */ - *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx); - memset(r, 0, sizeof(struct reader)); - - if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0) + if ((result = git_path_dirname_r(&path, reader->file->path)) < 0) return result; - /* We need to know our index in the array, as the next config_parse call may realloc */ - index = git_array_size(parse_data->cfg_file->readers) - 1; dir = git_buf_detach(&path); result = included_path(&path, dir, var->entry->value); git__free(dir); @@ -1613,51 +1630,60 @@ static int read_on_variable( if (result < 0) return result; - r->file_path = git_buf_detach(&path); - git_buf_init(&r->buffer, 0); + include = git_array_alloc(reader->file->includes); + memset(include, 0, sizeof(*include)); + git_array_init(include->includes); + include->path = git_buf_detach(&path); - result = git_futils_readbuffer_updated( - &r->buffer, r->file_path, &r->checksum, NULL); + result = config_read(parse_data->values, include, parse_data->level, parse_data->depth+1); - if (result == 0) { - result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1); - r = git_array_get(parse_data->cfg_file->readers, index); - *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx); - } else if (result == GIT_ENOTFOUND) { + if (result == GIT_ENOTFOUND) { giterr_clear(); result = 0; } - - git_buf_free(&r->buffer); } return result; } -static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth) +static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth) { struct parse_data parse_data; + struct reader reader; + int error; if (depth >= MAX_INCLUDE_DEPTH) { - giterr_set(GITERR_CONFIG, "Maximum config include depth reached"); + giterr_set(GITERR_CONFIG, "maximum config include depth reached"); return -1; } + git_buf_init(&reader.buffer, 0); + + if ((error = git_futils_readbuffer(&reader.buffer, file->path)) < 0) + goto out; + + if ((error = git_hash_buf(&file->checksum, reader.buffer.ptr, reader.buffer.size)) < 0) + goto out; + /* Initialize the reading position */ - reader->read_ptr = reader->buffer.ptr; - reader->eof = 0; + reader.file = file; + reader.line_number = 0; + reader.read_ptr = reader.buffer.ptr; + reader.eof = 0; /* If the file is empty, there's nothing for us to do */ - if (*reader->read_ptr == '\0') - return 0; + if (*reader.read_ptr == '\0') + goto out; parse_data.values = values; - parse_data.cfg_file = cfg_file; - parse_data.reader_idx = git_array_size(cfg_file->readers) - 1; parse_data.level = level; parse_data.depth = depth; - return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data); + error = config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data); + +out: + git_buf_free(&reader.buffer); + return error; } static int write_section(git_buf *fbuf, const char *key) @@ -1754,7 +1780,7 @@ static int write_value(struct write_data *write_data) } static int write_on_section( - struct reader **reader, + struct reader *reader, const char *current_section, const char *line, size_t line_len, @@ -1790,7 +1816,7 @@ static int write_on_section( } static int write_on_variable( - struct reader **reader, + struct reader *reader, const char *current_section, char *var_name, char *var_value, @@ -1840,7 +1866,7 @@ static int write_on_variable( return write_value(write_data); } -static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data) +static int write_on_comment(struct reader *reader, const char *line, size_t line_len, void *data) { struct write_data *write_data; @@ -1851,7 +1877,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin } static int write_on_eof( - struct reader **reader, const char *current_section, void *data) + struct reader *reader, const char *current_section, void *data) { struct write_data *write_data = (struct write_data *)data; int result = 0; @@ -1890,31 +1916,35 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p char *section, *name, *ldot; git_filebuf file = GIT_FILEBUF_INIT; git_buf buf = GIT_BUF_INIT; - struct reader *reader = git_array_get(cfg->readers, 0); + struct reader reader; struct write_data write_data; + memset(&reader, 0, sizeof(reader)); + git_buf_init(&reader.buffer, 0); + reader.file = &cfg->file; + if (cfg->locked) { - result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content)); + result = git_buf_puts(&reader.buffer, git_buf_cstr(&cfg->locked_content)); } else { /* Lock the file */ if ((result = git_filebuf_open( - &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) { - git_buf_free(&reader->buffer); + &file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) { + git_buf_free(&reader.buffer); return result; } /* We need to read in our own config file */ - result = git_futils_readbuffer(&reader->buffer, cfg->file_path); + result = git_futils_readbuffer(&reader.buffer, cfg->file.path); } /* Initialise the reading position */ if (result == GIT_ENOTFOUND) { - reader->read_ptr = NULL; - reader->eof = 1; - git_buf_clear(&reader->buffer); + reader.read_ptr = NULL; + reader.eof = 1; + git_buf_clear(&reader.buffer); } else if (result == 0) { - reader->read_ptr = reader->buffer.ptr; - reader->eof = 0; + reader.read_ptr = reader.buffer.ptr; + reader.eof = 0; } else { git_filebuf_cleanup(&file); return -1; /* OS error when reading the file */ @@ -1933,7 +1963,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p write_data.preg = preg; write_data.value = value; - result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data); + result = config_parse(&reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data); git__free(section); git_buf_free(&write_data.buffered_comment); @@ -1954,7 +1984,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p done: git_buf_free(&buf); - git_buf_free(&reader->buffer); + git_buf_free(&reader.buffer); return result; } - diff --git a/src/config_file.h b/src/config_file.h index 1c52892c3..11b8118f5 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -7,6 +7,9 @@ #ifndef INCLUDE_config_file_h__ #define INCLUDE_config_file_h__ +#include "common.h" + +#include "git2/sys/config.h" #include "git2/config.h" GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level) diff --git a/src/crlf.c b/src/crlf.c index 11895b19f..9af60076d 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -5,12 +5,13 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #include "git2/attr.h" #include "git2/blob.h" #include "git2/index.h" #include "git2/sys/filter.h" -#include "common.h" #include "fileops.h" #include "hash.h" #include "filter.h" @@ -218,7 +219,7 @@ static const char *line_ending(struct crlf_attrs *ca) return "\r\n"; line_ending_error: - giterr_set(GITERR_INVALID, "Invalid input to line ending filter"); + giterr_set(GITERR_INVALID, "invalid input to line ending filter"); return NULL; } diff --git a/src/curl_stream.c b/src/curl_stream.c index 98de187dd..24bf62635 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "curl_stream.h" + #ifdef GIT_CURL #include <curl/curl.h> @@ -15,6 +17,16 @@ #include "vector.h" #include "proxy.h" +/* This is for backwards compatibility with curl<7.45.0. */ +#ifndef CURLINFO_ACTIVESOCKET +# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET +# define GIT_CURL_BADSOCKET -1 +# define git_activesocket_t long +#else +# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD +# define git_activesocket_t curl_socket_t +#endif + typedef struct { git_stream parent; CURL *handle; @@ -87,7 +99,8 @@ static int ask_and_apply_proxy_creds(curl_stream *s) static int curls_connect(git_stream *stream) { curl_stream *s = (curl_stream *) stream; - long sockextr, connect_last = 0; + git_activesocket_t sockextr; + long connect_last = 0; int failed_cert = 0, error; bool retry_connect; CURLcode res; @@ -117,10 +130,15 @@ static int curls_connect(git_stream *stream) if (res == CURLE_PEER_FAILED_VERIFICATION) failed_cert = 1; - if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) { + if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) { return seterr_curl(s); } + if (sockextr == GIT_CURL_BADSOCKET) { + giterr_set(GITERR_NET, "curl socket is no longer valid"); + return -1; + } + s->socket = sockextr; if (s->parent.encrypted && failed_cert) @@ -198,6 +216,7 @@ static int wait_for(curl_socket_t fd, bool reading) FD_ZERO(&outfd); FD_ZERO(&errfd); + assert(fd >= 0); FD_SET(fd, &errfd); if (reading) FD_SET(fd, &infd); diff --git a/src/curl_stream.h b/src/curl_stream.h index 283f0fe40..debade7e2 100644 --- a/src/curl_stream.h +++ b/src/curl_stream.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_curl_stream_h__ #define INCLUDE_curl_stream_h__ +#include "common.h" + #include "git2/sys/stream.h" extern int git_curl_stream_new(git_stream **out, const char *host, const char *port); diff --git a/src/delta.c b/src/delta.c index dc45697b6..073cba7c6 100644 --- a/src/delta.c +++ b/src/delta.c @@ -131,7 +131,7 @@ static int lookup_index_alloc( GITERR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len); if (!git__is_ulong(index_len)) { - giterr_set(GITERR_NOMEMORY, "Overly large delta"); + giterr_set(GITERR_NOMEMORY, "overly large delta"); return -1; } @@ -544,12 +544,12 @@ int git_delta_apply( * base object, resulting in data corruption or segfault. */ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) { - giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data"); + giterr_set(GITERR_INVALID, "failed to apply delta: base size does not match given data"); return -1; } if (hdr_sz(&res_sz, &delta, delta_end) < 0) { - giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data"); + giterr_set(GITERR_INVALID, "failed to apply delta: base size does not match given data"); return -1; } @@ -614,6 +614,6 @@ fail: *out = NULL; *out_len = 0; - giterr_set(GITERR_INVALID, "Failed to apply delta"); + giterr_set(GITERR_INVALID, "failed to apply delta"); return -1; } diff --git a/src/delta.h b/src/delta.h index cc9372922..f61987304 100644 --- a/src/delta.h +++ b/src/delta.h @@ -6,6 +6,7 @@ #define INCLUDE_git_delta_h__ #include "common.h" + #include "pack.h" typedef struct git_delta_index git_delta_index; diff --git a/src/describe.c b/src/describe.c index fc48fbde4..edf8edfd1 100644 --- a/src/describe.c +++ b/src/describe.c @@ -4,12 +4,14 @@ * 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 "common.h" + #include "git2/describe.h" #include "git2/strarray.h" #include "git2/diff.h" #include "git2/status.h" -#include "common.h" #include "commit.h" #include "commit_list.h" #include "oidmap.h" @@ -19,8 +21,6 @@ #include "vector.h" #include "repository.h" -GIT__USE_OIDMAP - /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ struct commit_name { @@ -127,7 +127,7 @@ static int add_to_known_names( if (!found) { int ret; - git_oidmap_insert(names, &e->peeled, e, ret); + git_oidmap_insert(names, &e->peeled, e, &ret); if (ret < 0) return -1; } @@ -335,14 +335,14 @@ static int display_name(git_buf *buf, git_repository *repo, struct commit_name * { if (n->prio == 2 && !n->tag) { if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) { - giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path); + giterr_set(GITERR_TAG, "annotated tag '%s' not available", n->path); return -1; } } if (n->tag && !n->name_checked) { if (!git_tag_name(n->tag)) { - giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path); + giterr_set(GITERR_TAG, "annotated tag '%s' has no embedded name", n->path); return -1; } @@ -471,7 +471,7 @@ static int describe( if (!data->opts->max_candidates_tags) { error = describe_not_found( git_commit_id(commit), - "Cannot describe - no tag exactly matches '%s'"); + "cannot describe - no tag exactly matches '%s'"); goto cleanup; } @@ -564,15 +564,15 @@ static int describe( } if (unannotated_cnt) { error = describe_not_found(git_commit_id(commit), - "Cannot describe - " - "No annotated tags can describe '%s'." - "However, there were unannotated tags."); + "cannot describe - " + "no annotated tags can describe '%s'; " + "however, there were unannotated tags."); goto cleanup; } else { error = describe_not_found(git_commit_id(commit), - "Cannot describe - " - "No tags can describe '%s'."); + "cannot describe - " + "no tags can describe '%s'."); goto cleanup; } } @@ -695,8 +695,8 @@ int git_describe_commit( goto cleanup; if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) { - giterr_set(GITERR_DESCRIBE, "Cannot describe - " - "No reference found, cannot describe anything."); + giterr_set(GITERR_DESCRIBE, "cannot describe - " + "no reference found, cannot describe anything."); error = -1; goto cleanup; } @@ -793,7 +793,7 @@ int git_describe_format(git_buf *out, const git_describe_result *result, const g if (opts.always_use_long_format && opts.abbreviated_size == 0) { - giterr_set(GITERR_DESCRIBE, "Cannot describe - " + giterr_set(GITERR_DESCRIBE, "cannot describe - " "'always_use_long_format' is incompatible with a zero" "'abbreviated_size'"); return -1; diff --git a/src/diff.c b/src/diff.c index 317d49597..b2a5ff947 100644 --- a/src/diff.c +++ b/src/diff.c @@ -4,9 +4,10 @@ * 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 "git2/version.h" -#include "common.h" + #include "diff.h" + +#include "git2/version.h" #include "diff_generate.h" #include "patch.h" #include "commit.h" @@ -19,6 +20,12 @@ #define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \ (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL)) +struct patch_id_args { + git_hash_ctx ctx; + git_oid result; + int first_file; +}; + GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta) { const char *str = delta->old_file.path; @@ -120,6 +127,41 @@ int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff) return 0; } +int git_diff_foreach( + git_diff *diff, + git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, + git_diff_hunk_cb hunk_cb, + git_diff_line_cb data_cb, + void *payload) +{ + int error = 0; + git_diff_delta *delta; + size_t idx; + + assert(diff); + + git_vector_foreach(&diff->deltas, idx, delta) { + git_patch *patch; + + /* check flags against patch status */ + if (git_diff_delta__should_skip(&diff->opts, delta)) + continue; + + if ((error = git_patch_from_diff(&patch, diff, idx)) != 0) + break; + + error = git_patch__invoke_callbacks(patch, file_cb, binary_cb, + hunk_cb, data_cb, payload); + git_patch_free(patch); + + if (error) + break; + } + + return error; +} + int git_diff_format_email__append_header_tobuf( git_buf *out, const git_oid *id, @@ -339,3 +381,142 @@ int git_diff_format_email_init_options( return 0; } +static int flush_hunk(git_oid *result, git_hash_ctx *ctx) +{ + git_oid hash; + unsigned short carry = 0; + int error, i; + + if ((error = git_hash_final(&hash, ctx)) < 0 || + (error = git_hash_init(ctx)) < 0) + return error; + + for (i = 0; i < GIT_OID_RAWSZ; i++) { + carry += result->id[i] + hash.id[i]; + result->id[i] = carry; + carry >>= 8; + } + + return 0; +} + +static void strip_spaces(git_buf *buf) +{ + char *src = buf->ptr, *dst = buf->ptr; + char c; + size_t len = 0; + + while ((c = *src++) != '\0') { + if (!git__isspace(c)) { + *dst++ = c; + len++; + } + } + + git_buf_truncate(buf, len); +} + +static int file_cb( + const git_diff_delta *delta, + float progress, + void *payload) +{ + struct patch_id_args *args = (struct patch_id_args *) payload; + git_buf buf = GIT_BUF_INIT; + int error; + + GIT_UNUSED(progress); + + if (!args->first_file && + (error = flush_hunk(&args->result, &args->ctx)) < 0) + goto out; + args->first_file = 0; + + if ((error = git_buf_printf(&buf, + "diff--gita/%sb/%s---a/%s+++b/%s", + delta->old_file.path, + delta->new_file.path, + delta->old_file.path, + delta->new_file.path)) < 0) + goto out; + + strip_spaces(&buf); + + if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0) + goto out; + +out: + git_buf_free(&buf); + return error; +} + +static int line_cb( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload) +{ + struct patch_id_args *args = (struct patch_id_args *) payload; + git_buf buf = GIT_BUF_INIT; + int error; + + GIT_UNUSED(delta); + GIT_UNUSED(hunk); + + switch (line->origin) { + case GIT_DIFF_LINE_ADDITION: + git_buf_putc(&buf, '+'); + break; + case GIT_DIFF_LINE_DELETION: + git_buf_putc(&buf, '-'); + break; + case GIT_DIFF_LINE_CONTEXT: + break; + default: + giterr_set(GITERR_PATCH, "invalid line origin for patch"); + return -1; + } + + git_buf_put(&buf, line->content, line->content_len); + strip_spaces(&buf); + + if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0) + goto out; + +out: + git_buf_free(&buf); + return error; +} + +int git_diff_patchid_init_options(git_diff_patchid_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_INIT); + return 0; +} + +int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts) +{ + struct patch_id_args args; + int error; + + GITERR_CHECK_VERSION( + opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options"); + + memset(&args, 0, sizeof(args)); + args.first_file = 1; + if ((error = git_hash_ctx_init(&args.ctx)) < 0) + goto out; + + if ((error = git_diff_foreach(diff, file_cb, NULL, NULL, line_cb, &args)) < 0) + goto out; + + if ((error = (flush_hunk(&args.result, &args.ctx))) < 0) + goto out; + + git_oid_cpy(out, &args.result); + +out: + git_hash_ctx_cleanup(&args.ctx); + return error; +} diff --git a/src/diff.h b/src/diff.h index 5750d2a09..4e5dd93da 100644 --- a/src/diff.h +++ b/src/diff.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_diff_h__ #define INCLUDE_diff_h__ +#include "common.h" + #include "git2/diff.h" #include "git2/patch.h" #include "git2/sys/diff.h" diff --git a/src/diff_driver.c b/src/diff_driver.c index 14a898c4f..8cd57cdf6 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -4,20 +4,18 @@ * 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 "common.h" + +#include "diff_driver.h" #include "git2/attr.h" #include "diff.h" -#include "diff_driver.h" #include "strmap.h" #include "map.h" #include "buf_text.h" #include "config.h" #include "repository.h" -GIT__USE_STRMAP - typedef enum { DIFF_DRIVER_AUTO = 0, DIFF_DRIVER_BINARY = 1, @@ -114,7 +112,7 @@ static int diff_driver_add_patterns( if (error < 0) break; - if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) != 0) { + if ((error = p_regcomp(&pat->re, buf.ptr, regex_flags)) != 0) { /* * TODO: issue a warning */ @@ -151,7 +149,7 @@ static git_diff_driver_registry *git_repository_driver_registry( } if (!repo->diff_drivers) - giterr_set(GITERR_REPOSITORY, "Unable to create diff driver registry"); + giterr_set(GITERR_REPOSITORY, "unable to create diff driver registry"); return repo->diff_drivers; } @@ -210,14 +208,14 @@ static int git_diff_driver_builtin( goto done; if (ddef->words && - (error = regcomp( + (error = p_regcomp( &drv->word_pattern, ddef->words, ddef->flags | REG_EXTENDED))) { error = giterr_set_regex(&drv->word_pattern, error); goto done; } - git_strmap_insert(reg->drivers, drv->name, drv, error); + git_strmap_insert(reg->drivers, drv->name, drv, &error); if (error > 0) error = 0; @@ -314,7 +312,7 @@ static int git_diff_driver_load( goto done; if (!ce || !ce->value) /* no diff.<driver>.wordregex, so just continue */; - else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) + else if (!(error = p_regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) found_driver = true; else { /* TODO: warn about bad regex instead of failure */ @@ -331,7 +329,7 @@ static int git_diff_driver_load( goto done; /* store driver in registry */ - git_strmap_insert(reg->drivers, drv->name, drv, error); + git_strmap_insert(reg->drivers, drv->name, drv, &error); if (error < 0) goto done; error = 0; @@ -519,4 +517,3 @@ void git_diff_find_context_clear(git_diff_find_context_payload *payload) payload->driver = NULL; } } - diff --git a/src/diff_driver.h b/src/diff_driver.h index 0706dcfc5..5691cac30 100644 --- a/src/diff_driver.h +++ b/src/diff_driver.h @@ -8,6 +8,7 @@ #define INCLUDE_diff_driver_h__ #include "common.h" + #include "buffer.h" typedef struct git_diff_driver_registry git_diff_driver_registry; diff --git a/src/diff_file.c b/src/diff_file.c index cc1029038..270d59bbb 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -4,12 +4,13 @@ * 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 "common.h" + +#include "diff_file.h" + #include "git2/blob.h" #include "git2/submodule.h" #include "diff.h" #include "diff_generate.h" -#include "diff_file.h" #include "odb.h" #include "fileops.h" #include "filter.h" @@ -304,7 +305,7 @@ static int diff_file_content_load_workdir_symlink( read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len); if (read_len < 0) { - giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path); + giterr_set(GITERR_OS, "failed to read symlink '%s'", fc->file->path); return -1; } diff --git a/src/diff_file.h b/src/diff_file.h index 0d54b6d33..5da7a7bb8 100644 --- a/src/diff_file.h +++ b/src/diff_file.h @@ -8,6 +8,7 @@ #define INCLUDE_diff_file_h__ #include "common.h" + #include "diff.h" #include "diff_driver.h" #include "map.h" diff --git a/src/diff_generate.c b/src/diff_generate.c index 06f9b19c7..6436ab930 100644 --- a/src/diff_generate.c +++ b/src/diff_generate.c @@ -4,9 +4,10 @@ * 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 "common.h" -#include "diff.h" + #include "diff_generate.h" + +#include "diff.h" #include "patch_generate.h" #include "fileops.h" #include "config.h" @@ -624,7 +625,7 @@ int git_diff__oid_for_entry( error = git_odb__hashlink(out, full_path.ptr); diff->base.perf.oid_calculations++; } else if (!git__is_sizet(entry.file_size)) { - giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", + giterr_set(GITERR_OS, "file size overflow (for 32-bits) on '%s'", entry.path); error = -1; } else if (!(error = git_filter_list_load(&fl, @@ -1587,7 +1588,7 @@ int git_diff__commit( char commit_oidstr[GIT_OID_HEXSZ + 1]; error = -1; - giterr_set(GITERR_INVALID, "Commit %s is a merge commit", + giterr_set(GITERR_INVALID, "commit %s is a merge commit", git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); goto on_error; } diff --git a/src/diff_generate.h b/src/diff_generate.h index 63e7f0532..8de2a0644 100644 --- a/src/diff_generate.h +++ b/src/diff_generate.h @@ -7,6 +7,12 @@ #ifndef INCLUDE_diff_generate_h__ #define INCLUDE_diff_generate_h__ +#include "common.h" + +#include "diff.h" +#include "pool.h" +#include "index.h" + enum { GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */ GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */ diff --git a/src/diff_parse.c b/src/diff_parse.c index e640063af..78da3b675 100644 --- a/src/diff_parse.c +++ b/src/diff_parse.c @@ -4,9 +4,10 @@ * 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 "common.h" -#include "diff.h" + #include "diff_parse.h" + +#include "diff.h" #include "patch.h" #include "patch_parse.h" @@ -37,7 +38,6 @@ static git_diff_parsed *diff_parsed_alloc(void) GIT_REFCOUNT_INC(diff); diff->base.type = GIT_DIFF_TYPE_PARSED; - diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE; diff->base.strcomp = git__strcmp; diff->base.strncomp = git__strncmp; diff->base.pfxcomp = git__prefixcmp; @@ -45,6 +45,13 @@ static git_diff_parsed *diff_parsed_alloc(void) diff->base.patch_fn = git_patch_parsed_from_diff; diff->base.free_fn = diff_parsed_free; + if (git_diff_init_options(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) { + git__free(diff); + return NULL; + } + + diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE; + git_pool_init(&diff->base.pool, 1); if (git_vector_init(&diff->patches, 0, NULL) < 0 || diff --git a/src/diff_parse.h b/src/diff_parse.h index c47d4cbc9..876782128 100644 --- a/src/diff_parse.h +++ b/src/diff_parse.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_diff_parse_h__ #define INCLUDE_diff_parse_h__ +#include "common.h" + #include "diff.h" typedef struct { diff --git a/src/diff_print.c b/src/diff_print.c index fd1a186c1..28ae38424 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -4,7 +4,9 @@ * 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 "common.h" + #include "diff.h" #include "diff_file.h" #include "patch_generate.h" @@ -222,7 +224,7 @@ static int diff_print_one_raw( if (pi->id_strlen > id_abbrev) { giterr_set(GITERR_PATCH, - "The patch input contains %d id characters (cannot print %d)", + "the patch input contains %d id characters (cannot print %d)", id_abbrev, pi->id_strlen); return -1; } @@ -273,7 +275,7 @@ static int diff_print_oid_range( if (delta->old_file.mode && id_strlen > delta->old_file.id_abbrev) { giterr_set(GITERR_PATCH, - "The patch input contains %d id characters (cannot print %d)", + "the patch input contains %d id characters (cannot print %d)", delta->old_file.id_abbrev, id_strlen); return -1; } @@ -281,7 +283,7 @@ static int diff_print_oid_range( if ((delta->new_file.mode && id_strlen > delta->new_file.id_abbrev)) { giterr_set(GITERR_PATCH, - "The patch input contains %d id characters (cannot print %d)", + "the patch input contains %d id characters (cannot print %d)", delta->new_file.id_abbrev, id_strlen); return -1; } @@ -680,7 +682,7 @@ int git_diff_print( print_file = diff_print_one_name_status; break; default: - giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format); + giterr_set(GITERR_INVALID, "unknown diff output format (%d)", format); return -1; } @@ -708,7 +710,7 @@ int git_diff_print_callback__to_buf( GIT_UNUSED(delta); GIT_UNUSED(hunk); if (!output) { - giterr_set(GITERR_INVALID, "Buffer pointer must be provided"); + giterr_set(GITERR_INVALID, "buffer pointer must be provided"); return -1; } diff --git a/src/diff_stats.c b/src/diff_stats.c index 9c186d793..583c68459 100644 --- a/src/diff_stats.c +++ b/src/diff_stats.c @@ -4,7 +4,9 @@ * 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 "common.h" + #include "vector.h" #include "diff.h" #include "patch_generate.h" @@ -300,15 +302,24 @@ int git_diff_stats_to_buf( } if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) { - error = git_buf_printf( - out, " %" PRIuZ " file%s changed, %" PRIuZ - " insertion%s(+), %" PRIuZ " deletion%s(-)\n", - stats->files_changed, stats->files_changed != 1 ? "s" : "", - stats->insertions, stats->insertions != 1 ? "s" : "", - stats->deletions, stats->deletions != 1 ? "s" : ""); - - if (error < 0) - return error; + git_buf_printf( + out, " %" PRIuZ " file%s changed", + stats->files_changed, stats->files_changed != 1 ? "s" : ""); + + if (stats->insertions || stats->deletions == 0) + git_buf_printf( + out, ", %" PRIuZ " insertion%s(+)", + stats->insertions, stats->insertions != 1 ? "s" : ""); + + if (stats->deletions || stats->insertions == 0) + git_buf_printf( + out, ", %" PRIuZ " deletion%s(-)", + stats->deletions, stats->deletions != 1 ? "s" : ""); + + git_buf_putc(out, '\n'); + + if (git_buf_oom(out)) + return -1; } if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) { @@ -334,4 +345,3 @@ void git_diff_stats_free(git_diff_stats *stats) git__free(stats->filestats); git__free(stats); } - diff --git a/src/diff_tform.c b/src/diff_tform.c index e8848bd45..c0461a3a3 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -4,7 +4,8 @@ * 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 "common.h" + +#include "diff_tform.h" #include "git2/config.h" #include "git2/blob.h" @@ -131,7 +132,7 @@ int git_diff__merge( if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) || reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) { giterr_set(GITERR_INVALID, - "Attempt to merge diffs created with conflicting options"); + "attempt to merge diffs created with conflicting options"); return -1; } @@ -553,8 +554,8 @@ static int similarity_measure( *score = -1; - /* don't try to compare files of different types */ - if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode)) + /* don't try to compare things that aren't files */ + if (!GIT_MODE_ISBLOB(a_file->mode) || !GIT_MODE_ISBLOB(b_file->mode)) return 0; /* if exact match is requested, force calculation of missing OIDs now */ diff --git a/src/diff_tform.h b/src/diff_tform.h index 5bd9712d9..a31c40f8e 100644 --- a/src/diff_tform.h +++ b/src/diff_tform.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_diff_tform_h__ #define INCLUDE_diff_tform_h__ +#include "common.h" + +#include "diff_file.h" + extern int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p); diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index 5bd6381b5..5e10db13d 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -4,11 +4,12 @@ * 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 "diff_xdiff.h" + #include "git2/errors.h" -#include "common.h" #include "diff.h" #include "diff_driver.h" -#include "diff_xdiff.h" #include "patch_generate.h" static int git_xdiff_scan_int(const char **str, int *value) @@ -50,7 +51,7 @@ static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header) return 0; fail: - giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff"); + giterr_set(GITERR_INVALID, "malformed hunk header from xdiff"); return -1; } @@ -99,7 +100,7 @@ static int diff_update_lines( info->new_lineno += (int)line->num_lines; break; default: - giterr_set(GITERR_INVALID, "Unknown diff line origin %02x", + giterr_set(GITERR_INVALID, "unknown diff line origin %02x", (unsigned int)line->origin); return -1; } diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h index 88375986b..aca80b131 100644 --- a/src/diff_xdiff.h +++ b/src/diff_xdiff.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_diff_xdiff_h__ #define INCLUDE_diff_xdiff_h__ +#include "common.h" + #include "diff.h" #include "xdiff/xdiff.h" #include "patch_generate.h" diff --git a/src/errors.c b/src/errors.c index 91acc3541..a874163b0 100644 --- a/src/errors.c +++ b/src/errors.c @@ -4,7 +4,9 @@ * 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 "common.h" + #include "global.h" #include "posix.h" #include "buffer.h" diff --git a/src/features.h.in b/src/features.h.in new file mode 100644 index 000000000..e03b7a251 --- /dev/null +++ b/src/features.h.in @@ -0,0 +1,36 @@ +#ifndef INCLUDE_features_h__ +#define INCLUDE_features_h__ + +#cmakedefine GIT_DEBUG_POOL 1 +#cmakedefine GIT_TRACE 1 +#cmakedefine GIT_THREADS 1 +#cmakedefine GIT_MSVC_CRTDBG 1 + +#cmakedefine GIT_ARCH_64 1 +#cmakedefine GIT_ARCH_32 1 + +#cmakedefine GIT_USE_ICONV 1 +#cmakedefine GIT_USE_NSEC 1 +#cmakedefine GIT_USE_STAT_MTIM 1 +#cmakedefine GIT_USE_STAT_MTIMESPEC 1 +#cmakedefine GIT_USE_STAT_MTIME_NSEC 1 +#cmakedefine GIT_USE_FUTIMENS 1 +#cmakedefine GIT_USE_REGCOMP_L 1 + +#cmakedefine GIT_SSH 1 +#cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1 + +#cmakedefine GIT_GSSAPI 1 +#cmakedefine GIT_WINHTTP 1 +#cmakedefine GIT_CURL 1 + +#cmakedefine GIT_HTTPS 1 +#cmakedefine GIT_OPENSSL 1 +#cmakedefine GIT_SECURE_TRANSPORT 1 + +#cmakedefine GIT_SHA1_COLLISIONDETECT 1 +#cmakedefine GIT_SHA1_WIN32 1 +#cmakedefine GIT_SHA1_COMMON_CRYPTO 1 +#cmakedefine GIT_SHA1_OPENSSL 1 + +#endif diff --git a/src/fetch.c b/src/fetch.c index 4d895752c..0b22b3673 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -5,16 +5,16 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "fetch.h" + #include "git2/oid.h" #include "git2/refs.h" #include "git2/revwalk.h" #include "git2/transport.h" -#include "common.h" #include "remote.h" #include "refspec.h" #include "pack.h" -#include "fetch.h" #include "netops.h" #include "repository.h" #include "refs.h" @@ -113,7 +113,7 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts) remote->need_pack = 0; if (filter_wants(remote, opts) < 0) { - giterr_set(GITERR_NET, "Failed to filter the reference list for wants"); + giterr_set(GITERR_NET, "failed to filter the reference list for wants"); return -1; } diff --git a/src/fetch.h b/src/fetch.h index 0412d4e44..1c75af9c3 100644 --- a/src/fetch.h +++ b/src/fetch.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_fetch_h__ #define INCLUDE_fetch_h__ +#include "common.h" + +#include "git2/remote.h" + #include "netops.h" int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts); diff --git a/src/fetchhead.c b/src/fetchhead.c index a95ea4ca4..ac25723d3 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -5,11 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "fetchhead.h" + #include "git2/types.h" #include "git2/oid.h" -#include "fetchhead.h" -#include "common.h" #include "buffer.h" #include "fileops.h" #include "filebuf.h" @@ -115,7 +115,7 @@ int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs) assert(repo && fetchhead_refs); - if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) { @@ -149,7 +149,7 @@ static int fetchhead_ref_parse( if (!*line) { giterr_set(GITERR_FETCHHEAD, - "Empty line in FETCH_HEAD line %d", line_num); + "empty line in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -163,15 +163,15 @@ static int fetchhead_ref_parse( if (strlen(oid_str) != GIT_OID_HEXSZ) { giterr_set(GITERR_FETCHHEAD, - "Invalid object ID in FETCH_HEAD line %d", line_num); + "invalid object ID in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if (git_oid_fromstr(oid, oid_str) < 0) { const git_error *oid_err = giterr_last(); - const char *err_msg = oid_err ? oid_err->message : "Invalid object ID"; + const char *err_msg = oid_err ? oid_err->message : "invalid object ID"; - giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %d", + giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ, err_msg, line_num); return -1; } @@ -180,7 +180,7 @@ static int fetchhead_ref_parse( if (*line) { if ((is_merge_str = git__strsep(&line, "\t")) == NULL) { giterr_set(GITERR_FETCHHEAD, - "Invalid description data in FETCH_HEAD line %d", line_num); + "invalid description data in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -190,13 +190,13 @@ static int fetchhead_ref_parse( *is_merge = 0; else { giterr_set(GITERR_FETCHHEAD, - "Invalid for-merge entry in FETCH_HEAD line %d", line_num); + "invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if ((desc = line) == NULL) { giterr_set(GITERR_FETCHHEAD, - "Invalid description in FETCH_HEAD line %d", line_num); + "invalid description in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -213,7 +213,7 @@ static int fetchhead_ref_parse( if ((desc = strstr(name, "' ")) == NULL || git__prefixcmp(desc, "' of ") != 0) { giterr_set(GITERR_FETCHHEAD, - "Invalid description in FETCH_HEAD line %d", line_num); + "invalid description in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -249,7 +249,7 @@ int git_repository_fetchhead_foreach(git_repository *repo, assert(repo && cb); - if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0) @@ -277,7 +277,7 @@ int git_repository_fetchhead_foreach(git_repository *repo, } if (*buffer) { - giterr_set(GITERR_FETCHHEAD, "No EOL at line %d", line_num+1); + giterr_set(GITERR_FETCHHEAD, "no EOL at line %"PRIuZ, line_num+1); error = -1; goto done; } diff --git a/src/fetchhead.h b/src/fetchhead.h index b03bd0f74..9e5171010 100644 --- a/src/fetchhead.h +++ b/src/fetchhead.h @@ -7,6 +7,9 @@ #ifndef INCLUDE_fetchhead_h__ #define INCLUDE_fetchhead_h__ +#include "common.h" + +#include "oid.h" #include "vector.h" typedef struct git_fetchhead_ref { diff --git a/src/filebuf.c b/src/filebuf.c index 582399470..8b7e489da 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -4,8 +4,9 @@ * 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 "common.h" + #include "filebuf.h" + #include "fileops.h" static const size_t WRITE_BUFFER_SIZE = (4096 * 2); @@ -23,7 +24,7 @@ static int verify_last_error(git_filebuf *file) { switch (file->last_error) { case BUFERR_WRITE: - giterr_set(GITERR_OS, "Failed to write out file"); + giterr_set(GITERR_OS, "failed to write out file"); return -1; case BUFERR_MEM: @@ -48,7 +49,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) else { giterr_clear(); /* actual OS error code just confuses */ giterr_set(GITERR_OS, - "Failed to lock file '%s' for writing", file->path_lock); + "failed to lock file '%s' for writing", file->path_lock); return GIT_ELOCKED; } } @@ -75,7 +76,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) source = p_open(file->path_original, O_RDONLY); if (source < 0) { giterr_set(GITERR_OS, - "Failed to open file '%s' for reading", + "failed to open file '%s' for reading", file->path_original); return -1; } @@ -90,10 +91,10 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) p_close(source); if (read_bytes < 0) { - giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original); + giterr_set(GITERR_OS, "failed to read file '%s'", file->path_original); return -1; } else if (error < 0) { - giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock); + giterr_set(GITERR_OS, "failed to write file '%s'", file->path_lock); return -1; } } @@ -246,7 +247,7 @@ static int resolve_symlink(git_buf *out, const char *path) root = git_path_root(target.ptr); if (root >= 0) { - if ((error = git_buf_puts(&curpath, target.ptr)) < 0) + if ((error = git_buf_sets(&curpath, target.ptr)) < 0) goto cleanup; } else { git_buf dir = GIT_BUF_INIT; @@ -291,6 +292,9 @@ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mo if (flags & GIT_FILEBUF_DO_NOT_BUFFER) file->do_not_buffer = true; + if (flags & GIT_FILEBUF_FSYNC) + file->do_fsync = true; + file->buf_size = size; file->buf_pos = 0; file->fd = -1; @@ -316,7 +320,7 @@ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mo if (compression != 0) { /* Initialize the ZLib stream */ if (deflateInit(&file->zs, compression) != Z_OK) { - giterr_set(GITERR_ZLIB, "Failed to initialize zlib"); + giterr_set(GITERR_ZLIB, "failed to initialize zlib"); goto cleanup; } @@ -425,18 +429,26 @@ int git_filebuf_commit(git_filebuf *file) file->fd_is_open = false; + if (file->do_fsync && p_fsync(file->fd) < 0) { + giterr_set(GITERR_OS, "failed to fsync '%s'", file->path_lock); + goto on_error; + } + if (p_close(file->fd) < 0) { - giterr_set(GITERR_OS, "Failed to close file at '%s'", file->path_lock); + giterr_set(GITERR_OS, "failed to close file at '%s'", file->path_lock); goto on_error; } file->fd = -1; if (p_rename(file->path_lock, file->path_original) < 0) { - giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original); + giterr_set(GITERR_OS, "failed to rename lockfile to '%s'", file->path_original); goto on_error; } + if (file->do_fsync && git_futils_fsync_parent(file->path_original) < 0) + goto on_error; + file->did_rename = true; git_filebuf_cleanup(file); @@ -571,7 +583,7 @@ int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file) res = p_stat(file->path_original, &st); if (res < 0) { - giterr_set(GITERR_OS, "Could not get stat info for '%s'", + giterr_set(GITERR_OS, "could not get stat info for '%s'", file->path_original); return res; } diff --git a/src/filebuf.h b/src/filebuf.h index 467708d45..f51ff230f 100644 --- a/src/filebuf.h +++ b/src/filebuf.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_filebuf_h__ #define INCLUDE_filebuf_h__ +#include "common.h" + #include "fileops.h" #include "hash.h" #include <zlib.h> @@ -20,7 +22,8 @@ #define GIT_FILEBUF_FORCE (1 << 3) #define GIT_FILEBUF_TEMPORARY (1 << 4) #define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5) -#define GIT_FILEBUF_DEFLATE_SHIFT (6) +#define GIT_FILEBUF_FSYNC (1 << 6) +#define GIT_FILEBUF_DEFLATE_SHIFT (7) #define GIT_FILELOCK_EXTENSION ".lock\0" #define GIT_FILELOCK_EXTLENGTH 6 @@ -47,6 +50,7 @@ struct git_filebuf { bool created_lock; bool did_rename; bool do_not_buffer; + bool do_fsync; int last_error; }; diff --git a/src/fileops.c b/src/fileops.c index fcc0301f9..ad3f67e2b 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -4,8 +4,9 @@ * 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 "common.h" + #include "fileops.h" + #include "global.h" #include "strmap.h" #include <ctype.h> @@ -13,8 +14,6 @@ #include "win32/findfile.h" #endif -GIT__USE_STRMAP - int git_futils_mkpath2file(const char *file_path, const mode_t mode) { return git_futils_mkdir( @@ -37,13 +36,13 @@ int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode) if ((fd = p_mkstemp(path_out->ptr)) < 0) { giterr_set(GITERR_OS, - "Failed to create temporary file '%s'", path_out->ptr); + "failed to create temporary file '%s'", path_out->ptr); return -1; } if (p_chmod(path_out->ptr, (mode & ~mask))) { giterr_set(GITERR_OS, - "Failed to set permissions on file '%s'", path_out->ptr); + "failed to set permissions on file '%s'", path_out->ptr); return -1; } @@ -59,7 +58,7 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode fd = p_creat(path, mode); if (fd < 0) { - giterr_set(GITERR_OS, "Failed to create file '%s'", path); + giterr_set(GITERR_OS, "failed to create file '%s'", path); return -1; } @@ -68,12 +67,20 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode int git_futils_creat_locked(const char *path, const mode_t mode) { - int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | - O_EXCL | O_BINARY | O_CLOEXEC, mode); + int fd = p_open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, + mode); if (fd < 0) { - giterr_set(GITERR_OS, "Failed to create locked file '%s'", path); - return errno == EEXIST ? GIT_ELOCKED : -1; + int error = errno; + giterr_set(GITERR_OS, "failed to create locked file '%s'", path); + switch (error) { + case EEXIST: + return GIT_ELOCKED; + case ENOENT: + return GIT_ENOTFOUND; + default: + return -1; + } } return fd; @@ -100,7 +107,7 @@ git_off_t git_futils_filesize(git_file fd) struct stat sb; if (p_fstat(fd, &sb)) { - giterr_set(GITERR_OS, "Failed to stat file descriptor"); + giterr_set(GITERR_OS, "failed to stat file descriptor"); return -1; } @@ -129,7 +136,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) git_buf_clear(buf); if (!git__is_ssizet(len)) { - giterr_set(GITERR_INVALID, "Read too large."); + giterr_set(GITERR_INVALID, "read too large"); return -1; } @@ -141,7 +148,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) read_size = p_read(fd, buf->ptr, len); if (read_size != (ssize_t)len) { - giterr_set(GITERR_OS, "Failed to read descriptor"); + giterr_set(GITERR_OS, "failed to read descriptor"); git_buf_free(buf); return -1; } @@ -176,7 +183,7 @@ int git_futils_readbuffer_updated( } if (!git__is_sizet(st.st_size+1)) { - giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path); + giterr_set(GITERR_OS, "invalid regular file stat for '%s'", path); return -1; } @@ -190,28 +197,29 @@ int git_futils_readbuffer_updated( p_close(fd); - if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { - git_buf_free(&buf); - return error; - } + if (checksum) { + if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { + git_buf_free(&buf); + return error; + } - /* - * If we were given a checksum, we only want to use it if it's different - */ - if (checksum && !git_oid__cmp(checksum, &checksum_new)) { - git_buf_free(&buf); - if (updated) - *updated = 0; + /* + * If we were given a checksum, we only want to use it if it's different + */ + if (!git_oid__cmp(checksum, &checksum_new)) { + git_buf_free(&buf); + if (updated) + *updated = 0; - return 0; + return 0; + } + + git_oid_cpy(checksum, &checksum_new); } /* * If we're here, the file did change, or the user didn't have an old version */ - if (checksum) - git_oid_cpy(checksum, &checksum_new); - if (updated != NULL) *updated = 1; @@ -229,26 +237,43 @@ int git_futils_readbuffer(git_buf *buf, const char *path) int git_futils_writebuffer( const git_buf *buf, const char *path, int flags, mode_t mode) { - int fd, error = 0; + int fd, do_fsync = 0, error = 0; - if (flags <= 0) + if (!flags) flags = O_CREAT | O_TRUNC | O_WRONLY; + + if ((flags & O_FSYNC) != 0) + do_fsync = 1; + + flags &= ~O_FSYNC; + if (!mode) mode = GIT_FILEMODE_BLOB; if ((fd = p_open(path, flags, mode)) < 0) { - giterr_set(GITERR_OS, "Could not open '%s' for writing", path); + giterr_set(GITERR_OS, "could not open '%s' for writing", path); return fd; } if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) { - giterr_set(GITERR_OS, "Could not write to '%s'", path); + giterr_set(GITERR_OS, "could not write to '%s'", path); (void)p_close(fd); return error; } - if ((error = p_close(fd)) < 0) - giterr_set(GITERR_OS, "Error while closing '%s'", path); + if (do_fsync && (error = p_fsync(fd)) < 0) { + giterr_set(GITERR_OS, "could not fsync '%s'", path); + p_close(fd); + return error; + } + + if ((error = p_close(fd)) < 0) { + giterr_set(GITERR_OS, "error while closing '%s'", path); + return error; + } + + if (do_fsync && (flags & O_CREAT)) + error = git_futils_fsync_parent(path); return error; } @@ -259,7 +284,7 @@ int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmod return -1; if (p_rename(from, to) < 0) { - giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to); + giterr_set(GITERR_OS, "failed to rename '%s' to '%s'", from, to); return -1; } @@ -280,13 +305,19 @@ int git_futils_mmap_ro_file(git_map *out, const char *path) if (fd < 0) return fd; - len = git_futils_filesize(fd); + if ((len = git_futils_filesize(fd)) < 0) { + result = -1; + goto out; + } + if (!git__is_sizet(len)) { - giterr_set(GITERR_OS, "File `%s` too large to mmap", path); - return -1; + giterr_set(GITERR_OS, "file `%s` too large to mmap", path); + result = -1; + goto out; } result = git_futils_mmap_ro(out, fd, 0, (size_t)len); +out: p_close(fd); return result; } @@ -306,14 +337,14 @@ GIT_INLINE(int) mkdir_validate_dir( /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { giterr_set(GITERR_FILESYSTEM, - "Failed to make directory '%s': directory exists", path); + "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { if (p_unlink(path) < 0) { - giterr_set(GITERR_OS, "Failed to remove %s '%s'", + giterr_set(GITERR_OS, "failed to remove %s '%s'", S_ISLNK(st->st_mode) ? "symlink" : "file", path); return GIT_EEXISTS; } @@ -321,7 +352,7 @@ GIT_INLINE(int) mkdir_validate_dir( opts->perfdata.mkdir_calls++; if (p_mkdir(path, mode) < 0) { - giterr_set(GITERR_OS, "Failed to make directory '%s'", path); + giterr_set(GITERR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } @@ -331,14 +362,14 @@ GIT_INLINE(int) mkdir_validate_dir( opts->perfdata.stat_calls++; if (p_stat(path, st) < 0) { - giterr_set(GITERR_OS, "Failed to make directory '%s'", path); + giterr_set(GITERR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (!S_ISDIR(st->st_mode)) { giterr_set(GITERR_FILESYSTEM, - "Failed to make directory '%s': directory exists", path); + "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } @@ -561,7 +592,7 @@ int git_futils_mkdir_relative( retry_lstat: if (p_lstat(make_path.ptr, &st) < 0) { if (mkdir_attempted || errno != ENOENT) { - giterr_set(GITERR_OS, "Cannot access component in path '%s'", make_path.ptr); + giterr_set(GITERR_OS, "cannot access component in path '%s'", make_path.ptr); error = -1; goto done; } @@ -572,7 +603,7 @@ retry_lstat: if (p_mkdir(make_path.ptr, mode) < 0) { if (errno == EEXIST) goto retry_lstat; - giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); + giterr_set(GITERR_OS, "failed to make directory '%s'", make_path.ptr); error = -1; goto done; } @@ -599,7 +630,7 @@ retry_lstat: memcpy(cache_path, make_path.ptr, make_path.size + 1); - git_strmap_insert(opts->dir_map, cache_path, cache_path, error); + git_strmap_insert(opts->dir_map, cache_path, cache_path, &error); if (error < 0) goto done; } @@ -613,7 +644,7 @@ retry_lstat: opts->perfdata.stat_calls++; if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { - giterr_set(GITERR_OS, "Path is not a directory '%s'", + giterr_set(GITERR_OS, "path is not a directory '%s'", make_path.ptr); error = GIT_ENOTFOUND; } @@ -636,10 +667,10 @@ typedef struct { static int futils__error_cannot_rmdir(const char *path, const char *filemsg) { if (filemsg) - giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s", + giterr_set(GITERR_OS, "could not remove directory '%s': %s", path, filemsg); else - giterr_set(GITERR_OS, "Could not remove directory '%s'", path); + giterr_set(GITERR_OS, "could not remove directory '%s'", path); return -1; } @@ -740,6 +771,9 @@ static int futils__rmdir_empty_parent(void *opaque, const char *path) if (en == ENOENT || en == ENOTDIR) { /* do nothing */ + } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 && + en == EBUSY) { + error = git_path_set_error(errno, path, "rmdir"); } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { error = GIT_ITEROVER; } else { @@ -807,7 +841,7 @@ static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done) error = p_write(ofd, buffer, len); if (len < 0) { - giterr_set(GITERR_OS, "Read error while copying file"); + giterr_set(GITERR_OS, "read error while copying file"); error = (int)len; } @@ -863,14 +897,14 @@ static int cp_link(const char *from, const char *to, size_t link_size) read_len = p_readlink(from, link_data, link_size); if (read_len != (ssize_t)link_size) { - giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from); + giterr_set(GITERR_OS, "failed to read symlink data for '%s'", from); error = -1; } else { link_data[read_len] = '\0'; if (p_symlink(link_data, to) < 0) { - giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'", + giterr_set(GITERR_OS, "could not symlink '%s' as '%s'", link_data, to); error = -1; } @@ -966,7 +1000,7 @@ static int _cp_r_callback(void *ref, git_buf *from) return 0; if (p_unlink(info->to.ptr) < 0) { - giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", + giterr_set(GITERR_OS, "cannot overwrite existing file '%s'", info->to.ptr); return GIT_EEXISTS; } @@ -1101,3 +1135,37 @@ void git_futils_filestamp_set_from_stat( memset(stamp, 0, sizeof(*stamp)); } } + +int git_futils_fsync_dir(const char *path) +{ +#ifdef GIT_WIN32 + GIT_UNUSED(path); + return 0; +#else + int fd, error = -1; + + if ((fd = p_open(path, O_RDONLY)) < 0) { + giterr_set(GITERR_OS, "failed to open directory '%s' for fsync", path); + return -1; + } + + if ((error = p_fsync(fd)) < 0) + giterr_set(GITERR_OS, "failed to fsync directory '%s'", path); + + p_close(fd); + return error; +#endif +} + +int git_futils_fsync_parent(const char *path) +{ + char *parent; + int error; + + if ((parent = git_path_dirname(path)) == NULL) + return -1; + + error = git_futils_fsync_dir(parent); + git__free(parent); + return error; +} diff --git a/src/fileops.h b/src/fileops.h index 54e3bd4e4..fd5441243 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -8,6 +8,7 @@ #define INCLUDE_fileops_h__ #include "common.h" + #include "map.h" #include "posix.h" #include "path.h" @@ -25,6 +26,13 @@ extern int git_futils_readbuffer_updated( git_buf *obj, const char *path, git_oid *checksum, int *updated); extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len); +/* Additional constants for `git_futils_writebuffer`'s `open_flags`. We + * support these internally and they will be removed before the `open` call. + */ +#ifndef O_FSYNC +# define O_FSYNC (1 << 31) +#endif + extern int git_futils_writebuffer( const git_buf *buf, const char *path, int open_flags, mode_t mode); @@ -45,12 +53,12 @@ extern int git_futils_writebuffer( extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode); /** - * Create an open a process-locked file + * Create and open a process-locked file */ extern int git_futils_creat_locked(const char *path, const mode_t mode); /** - * Create an open a process-locked file, while + * Create and open a process-locked file, while * also creating all the folders in its path */ extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode); @@ -356,4 +364,22 @@ extern void git_futils_filestamp_set( extern void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st); +/** + * `fsync` the parent directory of the given path, if `fsync` is + * supported for directories on this platform. + * + * @param path Path of the directory to sync. + * @return 0 on success, -1 on error + */ +extern int git_futils_fsync_dir(const char *path); + +/** + * `fsync` the parent directory of the given path, if `fsync` is + * supported for directories on this platform. + * + * @param path Path of the file whose parent directory should be synced. + * @return 0 on success, -1 on error + */ +extern int git_futils_fsync_parent(const char *path); + #endif /* INCLUDE_fileops_h__ */ diff --git a/src/filter.c b/src/filter.c index a0628d779..6ab09790b 100644 --- a/src/filter.c +++ b/src/filter.c @@ -5,10 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "filter.h" + #include "common.h" #include "fileops.h" #include "hash.h" -#include "filter.h" #include "repository.h" #include "global.h" #include "git2/sys/filter.h" @@ -296,7 +297,7 @@ int git_filter_unregister(const char *name) /* cannot unregister default filters */ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { - giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name); + giterr_set(GITERR_FILTER, "cannot unregister filter '%s'", name); return -1; } @@ -306,7 +307,7 @@ int git_filter_unregister(const char *name) } if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { - giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); + giterr_set(GITERR_FILTER, "cannot find filter '%s' to unregister", name); error = GIT_ENOTFOUND; goto done; } @@ -645,7 +646,7 @@ int git_filter_list_push( git_rwlock_rdunlock(&filter_registry.lock); if (fdef == NULL) { - giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); + giterr_set(GITERR_FILTER, "cannot use an unregistered filter"); return -1; } @@ -758,7 +759,7 @@ static int buf_from_blob(git_buf *out, git_blob *blob) git_off_t rawsize = git_blob_rawsize(blob); if (!git__is_sizet(rawsize)) { - giterr_set(GITERR_OS, "Blob is too large to filter"); + giterr_set(GITERR_OS, "blob is too large to filter"); return -1; } @@ -895,7 +896,7 @@ static int stream_list_init( git_array_size(filters->filters) - 1 - i : i; git_filter_entry *fe = git_array_get(filters->filters, filter_idx); git_writestream *filter_stream; - + assert(fe->filter->stream || fe->filter->apply); /* If necessary, create a stream that proxies the traditional @@ -911,14 +912,19 @@ static int stream_list_init( last_stream); if (error < 0) - return error; + goto out; git_vector_insert(streams, filter_stream); last_stream = filter_stream; } - *out = last_stream; - return 0; +out: + if (error) + last_stream->close(last_stream); + else + *out = last_stream; + + return error; } void stream_list_free(git_vector *streams) @@ -943,12 +949,13 @@ int git_filter_list_stream_file( git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; ssize_t readlen; - int fd = -1, error; + int fd = -1, error, initialized = 0; if ((error = stream_list_init( &stream_start, &filter_streams, filters, target)) < 0 || (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0) goto done; + initialized = 1; if ((fd = git_futils_open_ro(abspath.ptr)) < 0) { error = fd; @@ -960,13 +967,13 @@ int git_filter_list_stream_file( goto done; } - if (!readlen) - error = stream_start->close(stream_start); - else if (readlen < 0) + if (readlen < 0) error = readlen; - done: + if (initialized) + error |= stream_start->close(stream_start); + if (fd >= 0) p_close(fd); stream_list_free(&filter_streams); @@ -981,20 +988,24 @@ int git_filter_list_stream_data( { git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; - int error = 0, close_error; + int error, initialized = 0; git_buf_sanitize(data); if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0) goto out; + initialized = 1; - error = stream_start->write(stream_start, data->ptr, data->size); + if ((error = stream_start->write( + stream_start, data->ptr, data->size)) < 0) + goto out; out: - close_error = stream_start->close(stream_start); + if (initialized) + error |= stream_start->close(stream_start); + stream_list_free(&filter_streams); - /* propagate the stream init or write error */ - return error < 0 ? error : close_error; + return error; } int git_filter_list_stream_blob( @@ -1012,3 +1023,9 @@ int git_filter_list_stream_blob( return git_filter_list_stream_data(filters, &in, target); } + +int git_filter_init(git_filter *filter, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT); + return 0; +} diff --git a/src/filter.h b/src/filter.h index 9bd835f94..b1c403ba9 100644 --- a/src/filter.h +++ b/src/filter.h @@ -8,6 +8,7 @@ #define INCLUDE_filter_h__ #include "common.h" + #include "attr_file.h" #include "git2/filter.h" diff --git a/src/fnmatch.c b/src/fnmatch.c index 33c8a2512..3cc2a27ba 100644 --- a/src/fnmatch.c +++ b/src/fnmatch.c @@ -44,12 +44,12 @@ * Compares a filename or pathname to a pattern. */ +#include "fnmatch.h" + #include <ctype.h> #include <stdio.h> #include <string.h> -#include "fnmatch.h" - #define EOS '\0' #define RANGE_MATCH 1 diff --git a/src/global.c b/src/global.c index 45b1ab8f6..c6847a4d2 100644 --- a/src/global.c +++ b/src/global.c @@ -4,8 +4,9 @@ * 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 "common.h" + #include "global.h" + #include "hash.h" #include "sysdir.h" #include "filter.h" @@ -22,7 +23,7 @@ git_mutex git__mwindow_mutex; -#define MAX_SHUTDOWN_CB 8 +#define MAX_SHUTDOWN_CB 9 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; @@ -247,6 +248,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) static pthread_key_t _tls_key; +static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_once_t _once_init = PTHREAD_ONCE_INIT; int init_error = 0; @@ -268,12 +270,19 @@ static void init_once(void) int git_libgit2_init(void) { - int ret; + int ret, err; ret = git_atomic_inc(&git__n_inits); - pthread_once(&_once_init, init_once); - return init_error ? init_error : ret; + if ((err = pthread_mutex_lock(&_init_mutex)) != 0) + return err; + err = pthread_once(&_once_init, init_once); + err |= pthread_mutex_unlock(&_init_mutex); + + if (err || init_error) + return err | init_error; + + return ret; } int git_libgit2_shutdown(void) @@ -285,6 +294,9 @@ int git_libgit2_shutdown(void) if ((ret = git_atomic_dec(&git__n_inits)) != 0) return ret; + if ((ret = pthread_mutex_lock(&_init_mutex)) != 0) + return ret; + /* Shut down any subsystems that have global state */ shutdown_common(); @@ -298,6 +310,9 @@ int git_libgit2_shutdown(void) git_mutex_free(&git__mwindow_mutex); _once_init = new_once; + if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0) + return ret; + return 0; } @@ -327,7 +342,7 @@ int git_libgit2_init(void) { int ret; - /* Only init SSL the first time */ + /* Only init subsystems the first time */ if ((ret = git_atomic_inc(&git__n_inits)) != 1) return ret; @@ -345,6 +360,7 @@ int git_libgit2_shutdown(void) if ((ret = git_atomic_dec(&git__n_inits)) == 0) { shutdown_common(); git__global_state_cleanup(&__state); + memset(&__state, 0, sizeof(__state)); } return ret; diff --git a/src/global.h b/src/global.h index 219951525..b75ad6f56 100644 --- a/src/global.h +++ b/src/global.h @@ -8,6 +8,7 @@ #define INCLUDE_global_h__ #include "common.h" + #include "mwindow.h" #include "hash.h" @@ -16,6 +17,12 @@ typedef struct { git_error error_t; git_buf error_buf; char oid_fmt[GIT_OID_HEXSZ+1]; + + /* On Windows, this is the current child thread that was started by + * `git_thread_create`. This is used to set the thread's exit code + * when terminated by `git_thread_exit`. It is unused on POSIX. + */ + git_thread *current_thread; } git_global_st; #ifdef GIT_OPENSSL diff --git a/src/graph.c b/src/graph.c index 8accd808c..df82f0f71 100644 --- a/src/graph.c +++ b/src/graph.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #include "revwalk.h" #include "merge.h" #include "git2/graph.h" @@ -59,7 +61,7 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one, /* as long as there are non-STALE commits */ while (interesting(&list, roots)) { git_commit_list_node *commit = git_pqueue_pop(&list); - int flags; + unsigned int flags; if (commit == NULL) break; diff --git a/src/hash.c b/src/hash.c index f3645a913..cc6676d4d 100644 --- a/src/hash.c +++ b/src/hash.c @@ -5,7 +5,6 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "hash.h" int git_hash_buf(git_oid *out, const void *data, size_t len) diff --git a/src/hash.h b/src/hash.h index 0bc02a8a9..cce3a7f30 100644 --- a/src/hash.h +++ b/src/hash.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_hash_h__ #define INCLUDE_hash_h__ +#include "common.h" + #include "git2/oid.h" typedef struct git_hash_prov git_hash_prov; @@ -16,11 +18,13 @@ int git_hash_global_init(void); int git_hash_ctx_init(git_hash_ctx *ctx); void git_hash_ctx_cleanup(git_hash_ctx *ctx); -#if defined(GIT_COMMON_CRYPTO) +#if defined(GIT_SHA1_COLLISIONDETECT) +# include "hash/hash_collisiondetect.h" +#elif defined(GIT_SHA1_COMMON_CRYPTO) # include "hash/hash_common_crypto.h" -#elif defined(OPENSSL_SHA1) +#elif defined(GIT_SHA1_OPENSSL) # include "hash/hash_openssl.h" -#elif defined(WIN32_SHA1) +#elif defined(GIT_SHA1_WIN32) # include "hash/hash_win32.h" #else # include "hash/hash_generic.h" diff --git a/src/hash/hash_collisiondetect.h b/src/hash/hash_collisiondetect.h new file mode 100644 index 000000000..5fdae8df6 --- /dev/null +++ b/src/hash/hash_collisiondetect.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_collisiondetect_h__ +#define INCLUDE_hash_collisiondetect_h__ + +#include "hash.h" +#include "sha1dc/sha1.h" + +struct git_hash_ctx { + SHA1_CTX c; +}; + +#define git_hash_global_init() 0 +#define git_hash_ctx_init(ctx) git_hash_init(ctx) +#define git_hash_ctx_cleanup(ctx) + +GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx) +{ + assert(ctx); + SHA1DCInit(&ctx->c); + return 0; +} + +GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + assert(ctx); + SHA1DCUpdate(&ctx->c, data, len); + return 0; +} + +GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + assert(ctx); + if (SHA1DCFinal(out->id, &ctx->c)) { + giterr_set(GITERR_SHA1, "SHA1 collision attack detected"); + return -1; + } + + return 0; +} + +#endif /* INCLUDE_hash_collisiondetect_h__ */ diff --git a/src/hash/hash_generic.c b/src/hash/hash_generic.c index 472a7a696..7b33b6194 100644 --- a/src/hash/hash_generic.c +++ b/src/hash/hash_generic.c @@ -5,9 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "hash_generic.h" + #include "hash.h" -#include "hash/hash_generic.h" #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h index daeb1cda8..114b60781 100644 --- a/src/hash/hash_generic.h +++ b/src/hash/hash_generic.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_hash_generic_h__ #define INCLUDE_hash_generic_h__ +#include "common.h" + #include "hash.h" struct git_hash_ctx { diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c index 6bae53e55..4d53a57bd 100644 --- a/src/hash/hash_win32.c +++ b/src/hash/hash_win32.c @@ -5,10 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "hash_win32.h" + #include "global.h" #include "hash.h" -#include "hash/hash_win32.h" #include <wincrypt.h> #include <strsafe.h> diff --git a/src/hash/hash_win32.h b/src/hash/hash_win32.h index 2eee5ca79..187c07267 100644 --- a/src/hash/hash_win32.h +++ b/src/hash/hash_win32.h @@ -9,6 +9,7 @@ #define INCLUDE_hash_win32_h__ #include "common.h" + #include "hash.h" #include <wincrypt.h> diff --git a/src/hash/sha1dc/sha1.c b/src/hash/sha1dc/sha1.c new file mode 100644 index 000000000..facea1bb5 --- /dev/null +++ b/src/hash/sha1dc/sha1.c @@ -0,0 +1,1856 @@ +/*** +* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow (danshu@microsoft.com) +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include <string.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#endif + +#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C +#include SHA1DC_CUSTOM_INCLUDE_SHA1_C +#endif + +#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT +#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1 +#endif + +#include "sha1.h" +#include "ubc_check.h" + + +/* + Because Little-Endian architectures are most common, + we only set SHA1DC_BIGENDIAN if one of these conditions is met. + Note that all MSFT platforms are little endian, + so none of these will be defined under the MSC compiler. + If you are compiling on a big endian platform and your compiler does not define one of these, + you will have to add whatever macros your tool chain defines to indicate Big-Endianness. + */ +#ifdef SHA1DC_BIGENDIAN +#undef SHA1DC_BIGENDIAN +#endif + +#if (defined(_BYTE_ORDER) || defined(__BYTE_ORDER) || defined(__BYTE_ORDER__)) + +#if ((defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) ) +#define SHA1DC_BIGENDIAN +#endif + +#else + +#if (defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \ + defined(__sparc)) +#define SHA1DC_BIGENDIAN +#endif + +#endif + +#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN)) +#undef SHA1DC_BIGENDIAN +#endif +#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN)) +#define SHA1DC_BIGENDIAN +#endif +/*ENDIANNESS SELECTION*/ + +#if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \ + defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \ + defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \ + defined(__386) || defined(_M_X64) || defined(_M_AMD64)) + +#define SHA1DC_ALLOW_UNALIGNED_ACCESS + +#endif /*UNALIGNMENT DETECTION*/ + + +#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n)))) +#define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n)))) + +#define sha1_bswap32(x) \ + {x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); x = (x << 16) | (x >> 16);} + +#define sha1_mix(W, t) (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1)) + +#ifdef SHA1DC_BIGENDIAN + #define sha1_load(m, t, temp) { temp = m[t]; } +#else + #define sha1_load(m, t, temp) { temp = m[t]; sha1_bswap32(temp); } +#endif + +#define sha1_store(W, t, x) *(volatile uint32_t *)&W[t] = x + +#define sha1_f1(b,c,d) ((d)^((b)&((c)^(d)))) +#define sha1_f2(b,c,d) ((b)^(c)^(d)) +#define sha1_f3(b,c,d) (((b)&(c))+((d)&((b)^(c)))) +#define sha1_f4(b,c,d) ((b)^(c)^(d)) + +#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); } +#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); } +#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); } +#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); } + +#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; } +#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; } +#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; } +#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; } + +#define SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \ + {sha1_load(m, t, temp); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30);} + +#define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30); } + +#define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1; b = rotate_left(b, 30); } + +#define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC; b = rotate_left(b, 30); } + +#define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6; b = rotate_left(b, 30); } + + +#define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e; + +#ifdef BUILDNOCOLLDETECTSHA1COMPRESSION +void sha1_compression(uint32_t ihv[5], const uint32_t m[16]) +{ + uint32_t W[80]; + uint32_t a,b,c,d,e; + unsigned i; + + memcpy(W, m, 16 * 4); + for (i = 16; i < 80; ++i) + W[i] = sha1_mix(W, i); + + a = ihv[0]; b = ihv[1]; c = ihv[2]; d = ihv[3]; e = ihv[4]; + + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); + + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); + + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); + + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); + + ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; +} +#endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/ + + +static void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]) +{ + uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; + + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); + + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); + + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); + + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); + + ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; +} + + + +void sha1_compression_states(uint32_t ihv[5], const uint32_t m[16], uint32_t W[80], uint32_t states[80][5]) +{ + uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; + uint32_t temp; + +#ifdef DOSTORESTATE00 + SHA1_STORE_STATE(0) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp); + +#ifdef DOSTORESTATE01 + SHA1_STORE_STATE(1) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp); + +#ifdef DOSTORESTATE02 + SHA1_STORE_STATE(2) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp); + +#ifdef DOSTORESTATE03 + SHA1_STORE_STATE(3) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp); + +#ifdef DOSTORESTATE04 + SHA1_STORE_STATE(4) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp); + +#ifdef DOSTORESTATE05 + SHA1_STORE_STATE(5) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp); + +#ifdef DOSTORESTATE06 + SHA1_STORE_STATE(6) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp); + +#ifdef DOSTORESTATE07 + SHA1_STORE_STATE(7) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp); + +#ifdef DOSTORESTATE08 + SHA1_STORE_STATE(8) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp); + +#ifdef DOSTORESTATE09 + SHA1_STORE_STATE(9) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp); + +#ifdef DOSTORESTATE10 + SHA1_STORE_STATE(10) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp); + +#ifdef DOSTORESTATE11 + SHA1_STORE_STATE(11) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp); + +#ifdef DOSTORESTATE12 + SHA1_STORE_STATE(12) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp); + +#ifdef DOSTORESTATE13 + SHA1_STORE_STATE(13) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp); + +#ifdef DOSTORESTATE14 + SHA1_STORE_STATE(14) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp); + +#ifdef DOSTORESTATE15 + SHA1_STORE_STATE(15) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp); + +#ifdef DOSTORESTATE16 + SHA1_STORE_STATE(16) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp); + +#ifdef DOSTORESTATE17 + SHA1_STORE_STATE(17) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp); + +#ifdef DOSTORESTATE18 + SHA1_STORE_STATE(18) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp); + +#ifdef DOSTORESTATE19 + SHA1_STORE_STATE(19) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp); + + + +#ifdef DOSTORESTATE20 + SHA1_STORE_STATE(20) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp); + +#ifdef DOSTORESTATE21 + SHA1_STORE_STATE(21) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp); + +#ifdef DOSTORESTATE22 + SHA1_STORE_STATE(22) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp); + +#ifdef DOSTORESTATE23 + SHA1_STORE_STATE(23) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp); + +#ifdef DOSTORESTATE24 + SHA1_STORE_STATE(24) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp); + +#ifdef DOSTORESTATE25 + SHA1_STORE_STATE(25) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp); + +#ifdef DOSTORESTATE26 + SHA1_STORE_STATE(26) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp); + +#ifdef DOSTORESTATE27 + SHA1_STORE_STATE(27) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp); + +#ifdef DOSTORESTATE28 + SHA1_STORE_STATE(28) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp); + +#ifdef DOSTORESTATE29 + SHA1_STORE_STATE(29) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp); + +#ifdef DOSTORESTATE30 + SHA1_STORE_STATE(30) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp); + +#ifdef DOSTORESTATE31 + SHA1_STORE_STATE(31) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp); + +#ifdef DOSTORESTATE32 + SHA1_STORE_STATE(32) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp); + +#ifdef DOSTORESTATE33 + SHA1_STORE_STATE(33) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp); + +#ifdef DOSTORESTATE34 + SHA1_STORE_STATE(34) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp); + +#ifdef DOSTORESTATE35 + SHA1_STORE_STATE(35) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp); + +#ifdef DOSTORESTATE36 + SHA1_STORE_STATE(36) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp); + +#ifdef DOSTORESTATE37 + SHA1_STORE_STATE(37) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp); + +#ifdef DOSTORESTATE38 + SHA1_STORE_STATE(38) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp); + +#ifdef DOSTORESTATE39 + SHA1_STORE_STATE(39) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp); + + + +#ifdef DOSTORESTATE40 + SHA1_STORE_STATE(40) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp); + +#ifdef DOSTORESTATE41 + SHA1_STORE_STATE(41) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp); + +#ifdef DOSTORESTATE42 + SHA1_STORE_STATE(42) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp); + +#ifdef DOSTORESTATE43 + SHA1_STORE_STATE(43) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp); + +#ifdef DOSTORESTATE44 + SHA1_STORE_STATE(44) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp); + +#ifdef DOSTORESTATE45 + SHA1_STORE_STATE(45) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp); + +#ifdef DOSTORESTATE46 + SHA1_STORE_STATE(46) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp); + +#ifdef DOSTORESTATE47 + SHA1_STORE_STATE(47) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp); + +#ifdef DOSTORESTATE48 + SHA1_STORE_STATE(48) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp); + +#ifdef DOSTORESTATE49 + SHA1_STORE_STATE(49) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp); + +#ifdef DOSTORESTATE50 + SHA1_STORE_STATE(50) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp); + +#ifdef DOSTORESTATE51 + SHA1_STORE_STATE(51) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp); + +#ifdef DOSTORESTATE52 + SHA1_STORE_STATE(52) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp); + +#ifdef DOSTORESTATE53 + SHA1_STORE_STATE(53) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp); + +#ifdef DOSTORESTATE54 + SHA1_STORE_STATE(54) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp); + +#ifdef DOSTORESTATE55 + SHA1_STORE_STATE(55) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp); + +#ifdef DOSTORESTATE56 + SHA1_STORE_STATE(56) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp); + +#ifdef DOSTORESTATE57 + SHA1_STORE_STATE(57) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp); + +#ifdef DOSTORESTATE58 + SHA1_STORE_STATE(58) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp); + +#ifdef DOSTORESTATE59 + SHA1_STORE_STATE(59) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp); + + + + +#ifdef DOSTORESTATE60 + SHA1_STORE_STATE(60) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp); + +#ifdef DOSTORESTATE61 + SHA1_STORE_STATE(61) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp); + +#ifdef DOSTORESTATE62 + SHA1_STORE_STATE(62) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp); + +#ifdef DOSTORESTATE63 + SHA1_STORE_STATE(63) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp); + +#ifdef DOSTORESTATE64 + SHA1_STORE_STATE(64) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp); + +#ifdef DOSTORESTATE65 + SHA1_STORE_STATE(65) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp); + +#ifdef DOSTORESTATE66 + SHA1_STORE_STATE(66) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp); + +#ifdef DOSTORESTATE67 + SHA1_STORE_STATE(67) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp); + +#ifdef DOSTORESTATE68 + SHA1_STORE_STATE(68) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp); + +#ifdef DOSTORESTATE69 + SHA1_STORE_STATE(69) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp); + +#ifdef DOSTORESTATE70 + SHA1_STORE_STATE(70) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp); + +#ifdef DOSTORESTATE71 + SHA1_STORE_STATE(71) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp); + +#ifdef DOSTORESTATE72 + SHA1_STORE_STATE(72) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp); + +#ifdef DOSTORESTATE73 + SHA1_STORE_STATE(73) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp); + +#ifdef DOSTORESTATE74 + SHA1_STORE_STATE(74) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp); + +#ifdef DOSTORESTATE75 + SHA1_STORE_STATE(75) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp); + +#ifdef DOSTORESTATE76 + SHA1_STORE_STATE(76) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp); + +#ifdef DOSTORESTATE77 + SHA1_STORE_STATE(77) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp); + +#ifdef DOSTORESTATE78 + SHA1_STORE_STATE(78) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp); + +#ifdef DOSTORESTATE79 + SHA1_STORE_STATE(79) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp); + + + + ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; +} + + + + +#define SHA1_RECOMPRESS(t) \ +static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \ +{ \ + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \ + if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \ + if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \ + if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \ + if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \ + if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \ + if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \ + if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \ + if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \ + if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \ + if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \ + if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \ + if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \ + if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \ + if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \ + if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \ + if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \ + if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \ + if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \ + if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \ + if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \ + if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \ + if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \ + if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \ + if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \ + if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \ + if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \ + if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \ + if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \ + if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \ + if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \ + if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \ + if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \ + if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \ + if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \ + if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \ + if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \ + if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \ + if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \ + if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \ + if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \ + if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \ + if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \ + if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \ + if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \ + if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \ + if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \ + if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \ + if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \ + if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \ + if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \ + if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \ + if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \ + if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \ + if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \ + if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \ + if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \ + if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \ + if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \ + if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \ + if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \ + if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \ + if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \ + if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \ + if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \ + if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \ + if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \ + if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \ + if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \ + if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \ + if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \ + if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \ + if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \ + if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \ + if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \ + if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \ + if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \ + if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \ + if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \ + if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \ + if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \ + ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \ + a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \ + if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \ + if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \ + if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \ + if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \ + if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \ + if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \ + if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \ + if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \ + if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \ + if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \ + if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \ + if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \ + if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \ + if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \ + if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \ + if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \ + if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \ + if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \ + if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \ + if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \ + if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \ + if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \ + if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \ + if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \ + if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \ + if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \ + if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \ + if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \ + if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \ + if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \ + if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \ + if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \ + if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \ + if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \ + if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \ + if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \ + if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \ + if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \ + if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \ + if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \ + if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \ + if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \ + if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \ + if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \ + if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \ + if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \ + if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \ + if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \ + if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \ + if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \ + if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \ + if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \ + if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \ + if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \ + if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \ + if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \ + if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \ + if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \ + if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \ + if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \ + if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \ + if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \ + if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \ + if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \ + if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \ + if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \ + if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \ + if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \ + if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \ + if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \ + if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \ + if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \ + if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \ + if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \ + if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \ + if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \ + if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \ + if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \ + if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \ + if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \ + ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \ +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4127) /* Complier complains about the checks in the above macro being constant. */ +#endif + +#ifdef DOSTORESTATE0 +SHA1_RECOMPRESS(0) +#endif + +#ifdef DOSTORESTATE1 +SHA1_RECOMPRESS(1) +#endif + +#ifdef DOSTORESTATE2 +SHA1_RECOMPRESS(2) +#endif + +#ifdef DOSTORESTATE3 +SHA1_RECOMPRESS(3) +#endif + +#ifdef DOSTORESTATE4 +SHA1_RECOMPRESS(4) +#endif + +#ifdef DOSTORESTATE5 +SHA1_RECOMPRESS(5) +#endif + +#ifdef DOSTORESTATE6 +SHA1_RECOMPRESS(6) +#endif + +#ifdef DOSTORESTATE7 +SHA1_RECOMPRESS(7) +#endif + +#ifdef DOSTORESTATE8 +SHA1_RECOMPRESS(8) +#endif + +#ifdef DOSTORESTATE9 +SHA1_RECOMPRESS(9) +#endif + +#ifdef DOSTORESTATE10 +SHA1_RECOMPRESS(10) +#endif + +#ifdef DOSTORESTATE11 +SHA1_RECOMPRESS(11) +#endif + +#ifdef DOSTORESTATE12 +SHA1_RECOMPRESS(12) +#endif + +#ifdef DOSTORESTATE13 +SHA1_RECOMPRESS(13) +#endif + +#ifdef DOSTORESTATE14 +SHA1_RECOMPRESS(14) +#endif + +#ifdef DOSTORESTATE15 +SHA1_RECOMPRESS(15) +#endif + +#ifdef DOSTORESTATE16 +SHA1_RECOMPRESS(16) +#endif + +#ifdef DOSTORESTATE17 +SHA1_RECOMPRESS(17) +#endif + +#ifdef DOSTORESTATE18 +SHA1_RECOMPRESS(18) +#endif + +#ifdef DOSTORESTATE19 +SHA1_RECOMPRESS(19) +#endif + +#ifdef DOSTORESTATE20 +SHA1_RECOMPRESS(20) +#endif + +#ifdef DOSTORESTATE21 +SHA1_RECOMPRESS(21) +#endif + +#ifdef DOSTORESTATE22 +SHA1_RECOMPRESS(22) +#endif + +#ifdef DOSTORESTATE23 +SHA1_RECOMPRESS(23) +#endif + +#ifdef DOSTORESTATE24 +SHA1_RECOMPRESS(24) +#endif + +#ifdef DOSTORESTATE25 +SHA1_RECOMPRESS(25) +#endif + +#ifdef DOSTORESTATE26 +SHA1_RECOMPRESS(26) +#endif + +#ifdef DOSTORESTATE27 +SHA1_RECOMPRESS(27) +#endif + +#ifdef DOSTORESTATE28 +SHA1_RECOMPRESS(28) +#endif + +#ifdef DOSTORESTATE29 +SHA1_RECOMPRESS(29) +#endif + +#ifdef DOSTORESTATE30 +SHA1_RECOMPRESS(30) +#endif + +#ifdef DOSTORESTATE31 +SHA1_RECOMPRESS(31) +#endif + +#ifdef DOSTORESTATE32 +SHA1_RECOMPRESS(32) +#endif + +#ifdef DOSTORESTATE33 +SHA1_RECOMPRESS(33) +#endif + +#ifdef DOSTORESTATE34 +SHA1_RECOMPRESS(34) +#endif + +#ifdef DOSTORESTATE35 +SHA1_RECOMPRESS(35) +#endif + +#ifdef DOSTORESTATE36 +SHA1_RECOMPRESS(36) +#endif + +#ifdef DOSTORESTATE37 +SHA1_RECOMPRESS(37) +#endif + +#ifdef DOSTORESTATE38 +SHA1_RECOMPRESS(38) +#endif + +#ifdef DOSTORESTATE39 +SHA1_RECOMPRESS(39) +#endif + +#ifdef DOSTORESTATE40 +SHA1_RECOMPRESS(40) +#endif + +#ifdef DOSTORESTATE41 +SHA1_RECOMPRESS(41) +#endif + +#ifdef DOSTORESTATE42 +SHA1_RECOMPRESS(42) +#endif + +#ifdef DOSTORESTATE43 +SHA1_RECOMPRESS(43) +#endif + +#ifdef DOSTORESTATE44 +SHA1_RECOMPRESS(44) +#endif + +#ifdef DOSTORESTATE45 +SHA1_RECOMPRESS(45) +#endif + +#ifdef DOSTORESTATE46 +SHA1_RECOMPRESS(46) +#endif + +#ifdef DOSTORESTATE47 +SHA1_RECOMPRESS(47) +#endif + +#ifdef DOSTORESTATE48 +SHA1_RECOMPRESS(48) +#endif + +#ifdef DOSTORESTATE49 +SHA1_RECOMPRESS(49) +#endif + +#ifdef DOSTORESTATE50 +SHA1_RECOMPRESS(50) +#endif + +#ifdef DOSTORESTATE51 +SHA1_RECOMPRESS(51) +#endif + +#ifdef DOSTORESTATE52 +SHA1_RECOMPRESS(52) +#endif + +#ifdef DOSTORESTATE53 +SHA1_RECOMPRESS(53) +#endif + +#ifdef DOSTORESTATE54 +SHA1_RECOMPRESS(54) +#endif + +#ifdef DOSTORESTATE55 +SHA1_RECOMPRESS(55) +#endif + +#ifdef DOSTORESTATE56 +SHA1_RECOMPRESS(56) +#endif + +#ifdef DOSTORESTATE57 +SHA1_RECOMPRESS(57) +#endif + +#ifdef DOSTORESTATE58 +SHA1_RECOMPRESS(58) +#endif + +#ifdef DOSTORESTATE59 +SHA1_RECOMPRESS(59) +#endif + +#ifdef DOSTORESTATE60 +SHA1_RECOMPRESS(60) +#endif + +#ifdef DOSTORESTATE61 +SHA1_RECOMPRESS(61) +#endif + +#ifdef DOSTORESTATE62 +SHA1_RECOMPRESS(62) +#endif + +#ifdef DOSTORESTATE63 +SHA1_RECOMPRESS(63) +#endif + +#ifdef DOSTORESTATE64 +SHA1_RECOMPRESS(64) +#endif + +#ifdef DOSTORESTATE65 +SHA1_RECOMPRESS(65) +#endif + +#ifdef DOSTORESTATE66 +SHA1_RECOMPRESS(66) +#endif + +#ifdef DOSTORESTATE67 +SHA1_RECOMPRESS(67) +#endif + +#ifdef DOSTORESTATE68 +SHA1_RECOMPRESS(68) +#endif + +#ifdef DOSTORESTATE69 +SHA1_RECOMPRESS(69) +#endif + +#ifdef DOSTORESTATE70 +SHA1_RECOMPRESS(70) +#endif + +#ifdef DOSTORESTATE71 +SHA1_RECOMPRESS(71) +#endif + +#ifdef DOSTORESTATE72 +SHA1_RECOMPRESS(72) +#endif + +#ifdef DOSTORESTATE73 +SHA1_RECOMPRESS(73) +#endif + +#ifdef DOSTORESTATE74 +SHA1_RECOMPRESS(74) +#endif + +#ifdef DOSTORESTATE75 +SHA1_RECOMPRESS(75) +#endif + +#ifdef DOSTORESTATE76 +SHA1_RECOMPRESS(76) +#endif + +#ifdef DOSTORESTATE77 +SHA1_RECOMPRESS(77) +#endif + +#ifdef DOSTORESTATE78 +SHA1_RECOMPRESS(78) +#endif + +#ifdef DOSTORESTATE79 +SHA1_RECOMPRESS(79) +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) +{ + switch (step) + { +#ifdef DOSTORESTATE0 + case 0: + sha1recompress_fast_0(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE1 + case 1: + sha1recompress_fast_1(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE2 + case 2: + sha1recompress_fast_2(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE3 + case 3: + sha1recompress_fast_3(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE4 + case 4: + sha1recompress_fast_4(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE5 + case 5: + sha1recompress_fast_5(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE6 + case 6: + sha1recompress_fast_6(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE7 + case 7: + sha1recompress_fast_7(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE8 + case 8: + sha1recompress_fast_8(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE9 + case 9: + sha1recompress_fast_9(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE10 + case 10: + sha1recompress_fast_10(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE11 + case 11: + sha1recompress_fast_11(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE12 + case 12: + sha1recompress_fast_12(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE13 + case 13: + sha1recompress_fast_13(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE14 + case 14: + sha1recompress_fast_14(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE15 + case 15: + sha1recompress_fast_15(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE16 + case 16: + sha1recompress_fast_16(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE17 + case 17: + sha1recompress_fast_17(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE18 + case 18: + sha1recompress_fast_18(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE19 + case 19: + sha1recompress_fast_19(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE20 + case 20: + sha1recompress_fast_20(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE21 + case 21: + sha1recompress_fast_21(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE22 + case 22: + sha1recompress_fast_22(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE23 + case 23: + sha1recompress_fast_23(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE24 + case 24: + sha1recompress_fast_24(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE25 + case 25: + sha1recompress_fast_25(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE26 + case 26: + sha1recompress_fast_26(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE27 + case 27: + sha1recompress_fast_27(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE28 + case 28: + sha1recompress_fast_28(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE29 + case 29: + sha1recompress_fast_29(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE30 + case 30: + sha1recompress_fast_30(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE31 + case 31: + sha1recompress_fast_31(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE32 + case 32: + sha1recompress_fast_32(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE33 + case 33: + sha1recompress_fast_33(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE34 + case 34: + sha1recompress_fast_34(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE35 + case 35: + sha1recompress_fast_35(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE36 + case 36: + sha1recompress_fast_36(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE37 + case 37: + sha1recompress_fast_37(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE38 + case 38: + sha1recompress_fast_38(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE39 + case 39: + sha1recompress_fast_39(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE40 + case 40: + sha1recompress_fast_40(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE41 + case 41: + sha1recompress_fast_41(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE42 + case 42: + sha1recompress_fast_42(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE43 + case 43: + sha1recompress_fast_43(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE44 + case 44: + sha1recompress_fast_44(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE45 + case 45: + sha1recompress_fast_45(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE46 + case 46: + sha1recompress_fast_46(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE47 + case 47: + sha1recompress_fast_47(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE48 + case 48: + sha1recompress_fast_48(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE49 + case 49: + sha1recompress_fast_49(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE50 + case 50: + sha1recompress_fast_50(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE51 + case 51: + sha1recompress_fast_51(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE52 + case 52: + sha1recompress_fast_52(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE53 + case 53: + sha1recompress_fast_53(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE54 + case 54: + sha1recompress_fast_54(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE55 + case 55: + sha1recompress_fast_55(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE56 + case 56: + sha1recompress_fast_56(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE57 + case 57: + sha1recompress_fast_57(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE58 + case 58: + sha1recompress_fast_58(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE59 + case 59: + sha1recompress_fast_59(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE60 + case 60: + sha1recompress_fast_60(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE61 + case 61: + sha1recompress_fast_61(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE62 + case 62: + sha1recompress_fast_62(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE63 + case 63: + sha1recompress_fast_63(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE64 + case 64: + sha1recompress_fast_64(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE65 + case 65: + sha1recompress_fast_65(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE66 + case 66: + sha1recompress_fast_66(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE67 + case 67: + sha1recompress_fast_67(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE68 + case 68: + sha1recompress_fast_68(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE69 + case 69: + sha1recompress_fast_69(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE70 + case 70: + sha1recompress_fast_70(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE71 + case 71: + sha1recompress_fast_71(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE72 + case 72: + sha1recompress_fast_72(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE73 + case 73: + sha1recompress_fast_73(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE74 + case 74: + sha1recompress_fast_74(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE75 + case 75: + sha1recompress_fast_75(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE76 + case 76: + sha1recompress_fast_76(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE77 + case 77: + sha1recompress_fast_77(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE78 + case 78: + sha1recompress_fast_78(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE79 + case 79: + sha1recompress_fast_79(ihvin, ihvout, me2, state); + break; +#endif + default: + abort(); + } + +} + + + +static void sha1_process(SHA1_CTX* ctx, const uint32_t block[16]) +{ + unsigned i, j; + uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF }; + uint32_t ihvtmp[5]; + + ctx->ihv1[0] = ctx->ihv[0]; + ctx->ihv1[1] = ctx->ihv[1]; + ctx->ihv1[2] = ctx->ihv[2]; + ctx->ihv1[3] = ctx->ihv[3]; + ctx->ihv1[4] = ctx->ihv[4]; + + sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states); + + if (ctx->detect_coll) + { + if (ctx->ubc_check) + { + ubc_check(ctx->m1, ubc_dv_mask); + } + + if (ubc_dv_mask[0] != 0) + { + for (i = 0; sha1_dvs[i].dvType != 0; ++i) + { + if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb)) + { + for (j = 0; j < 80; ++j) + ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j]; + + sha1_recompression_step(sha1_dvs[i].testt, ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]); + + /* to verify SHA-1 collision detection code with collisions for reduced-step SHA-1 */ + if ((0 == ((ihvtmp[0] ^ ctx->ihv[0]) | (ihvtmp[1] ^ ctx->ihv[1]) | (ihvtmp[2] ^ ctx->ihv[2]) | (ihvtmp[3] ^ ctx->ihv[3]) | (ihvtmp[4] ^ ctx->ihv[4]))) + || (ctx->reduced_round_coll && 0==((ctx->ihv1[0] ^ ctx->ihv2[0]) | (ctx->ihv1[1] ^ ctx->ihv2[1]) | (ctx->ihv1[2] ^ ctx->ihv2[2]) | (ctx->ihv1[3] ^ ctx->ihv2[3]) | (ctx->ihv1[4] ^ ctx->ihv2[4])))) + { + ctx->found_collision = 1; + + if (ctx->safe_hash) + { + sha1_compression_W(ctx->ihv, ctx->m1); + sha1_compression_W(ctx->ihv, ctx->m1); + } + + break; + } + } + } + } + } +} + +void SHA1DCInit(SHA1_CTX* ctx) +{ + ctx->total = 0; + ctx->ihv[0] = 0x67452301; + ctx->ihv[1] = 0xEFCDAB89; + ctx->ihv[2] = 0x98BADCFE; + ctx->ihv[3] = 0x10325476; + ctx->ihv[4] = 0xC3D2E1F0; + ctx->found_collision = 0; + ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT; + ctx->ubc_check = 1; + ctx->detect_coll = 1; + ctx->reduced_round_coll = 0; + ctx->callback = NULL; +} + +void SHA1DCSetSafeHash(SHA1_CTX* ctx, int safehash) +{ + if (safehash) + ctx->safe_hash = 1; + else + ctx->safe_hash = 0; +} + + +void SHA1DCSetUseUBC(SHA1_CTX* ctx, int ubc_check) +{ + if (ubc_check) + ctx->ubc_check = 1; + else + ctx->ubc_check = 0; +} + +void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll) +{ + if (detect_coll) + ctx->detect_coll = 1; + else + ctx->detect_coll = 0; +} + +void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX* ctx, int reduced_round_coll) +{ + if (reduced_round_coll) + ctx->reduced_round_coll = 1; + else + ctx->reduced_round_coll = 0; +} + +void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback) +{ + ctx->callback = callback; +} + +void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len) +{ + unsigned left, fill; + + if (len == 0) + return; + + left = ctx->total & 63; + fill = 64 - left; + + if (left && len >= fill) + { + ctx->total += fill; + memcpy(ctx->buffer + left, buf, fill); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); + buf += fill; + len -= fill; + left = 0; + } + while (len >= 64) + { + ctx->total += 64; + +#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) + sha1_process(ctx, (uint32_t*)(buf)); +#else + memcpy(ctx->buffer, buf, 64); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); +#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */ + buf += 64; + len -= 64; + } + if (len > 0) + { + ctx->total += len; + memcpy(ctx->buffer + left, buf, len); + } +} + +static const unsigned char sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx) +{ + uint32_t last = ctx->total & 63; + uint32_t padn = (last < 56) ? (56 - last) : (120 - last); + uint64_t total; + SHA1DCUpdate(ctx, (const char*)(sha1_padding), padn); + + total = ctx->total - padn; + total <<= 3; + ctx->buffer[56] = (unsigned char)(total >> 56); + ctx->buffer[57] = (unsigned char)(total >> 48); + ctx->buffer[58] = (unsigned char)(total >> 40); + ctx->buffer[59] = (unsigned char)(total >> 32); + ctx->buffer[60] = (unsigned char)(total >> 24); + ctx->buffer[61] = (unsigned char)(total >> 16); + ctx->buffer[62] = (unsigned char)(total >> 8); + ctx->buffer[63] = (unsigned char)(total); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); + output[0] = (unsigned char)(ctx->ihv[0] >> 24); + output[1] = (unsigned char)(ctx->ihv[0] >> 16); + output[2] = (unsigned char)(ctx->ihv[0] >> 8); + output[3] = (unsigned char)(ctx->ihv[0]); + output[4] = (unsigned char)(ctx->ihv[1] >> 24); + output[5] = (unsigned char)(ctx->ihv[1] >> 16); + output[6] = (unsigned char)(ctx->ihv[1] >> 8); + output[7] = (unsigned char)(ctx->ihv[1]); + output[8] = (unsigned char)(ctx->ihv[2] >> 24); + output[9] = (unsigned char)(ctx->ihv[2] >> 16); + output[10] = (unsigned char)(ctx->ihv[2] >> 8); + output[11] = (unsigned char)(ctx->ihv[2]); + output[12] = (unsigned char)(ctx->ihv[3] >> 24); + output[13] = (unsigned char)(ctx->ihv[3] >> 16); + output[14] = (unsigned char)(ctx->ihv[3] >> 8); + output[15] = (unsigned char)(ctx->ihv[3]); + output[16] = (unsigned char)(ctx->ihv[4] >> 24); + output[17] = (unsigned char)(ctx->ihv[4] >> 16); + output[18] = (unsigned char)(ctx->ihv[4] >> 8); + output[19] = (unsigned char)(ctx->ihv[4]); + return ctx->found_collision; +} + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C +#endif diff --git a/src/hash/sha1dc/sha1.h b/src/hash/sha1dc/sha1.h new file mode 100644 index 000000000..1e4e94be5 --- /dev/null +++ b/src/hash/sha1dc/sha1.h @@ -0,0 +1,110 @@ +/*** +* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com> +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +#ifndef SHA1DC_SHA1_H +#define SHA1DC_SHA1_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include <stdint.h> +#endif + +/* sha-1 compression function that takes an already expanded message, and additionally store intermediate states */ +/* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h */ +void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]); + +/* +// Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]). +// Where 0 <= T < 80 +// me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference.) +// state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block. +// The function will return: +// ihvin: The reconstructed input chaining value. +// ihvout: The reconstructed output chaining value. +*/ +typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*); + +/* A callback function type that can be set to be called when a collision block has been found: */ +/* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */ +typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); + +/* The SHA-1 context. */ +typedef struct { + uint64_t total; + uint32_t ihv[5]; + unsigned char buffer[64]; + int found_collision; + int safe_hash; + int detect_coll; + int ubc_check; + int reduced_round_coll; + collision_block_callback callback; + + uint32_t ihv1[5]; + uint32_t ihv2[5]; + uint32_t m1[80]; + uint32_t m2[80]; + uint32_t states[80][5]; +} SHA1_CTX; + +/* Initialize SHA-1 context. */ +void SHA1DCInit(SHA1_CTX*); + +/* + Function to enable safe SHA-1 hashing: + Collision attacks are thwarted by hashing a detected near-collision block 3 times. + Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: + The best collision attacks against SHA-1 have complexity about 2^60, + thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. + An attacker would be better off using a generic birthday search of complexity 2^80. + + Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected, + but it will result in a different SHA-1 hash for messages where a collision attack was detected. + This will automatically invalidate SHA-1 based digital signature forgeries. + Enabled by default. +*/ +void SHA1DCSetSafeHash(SHA1_CTX*, int); + +/* + Function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up). + Enabled by default + */ +void SHA1DCSetUseUBC(SHA1_CTX*, int); + +/* + Function to disable or enable the use of Collision Detection. + Enabled by default. + */ +void SHA1DCSetUseDetectColl(SHA1_CTX*, int); + +/* function to disable or enable the detection of reduced-round SHA-1 collisions */ +/* disabled by default */ +void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX*, int); + +/* function to set a callback function, pass NULL to disable */ +/* by default no callback set */ +void SHA1DCSetCallback(SHA1_CTX*, collision_block_callback); + +/* update SHA-1 context with buffer contents */ +void SHA1DCUpdate(SHA1_CTX*, const char*, size_t); + +/* obtain SHA-1 hash from SHA-1 context */ +/* returns: 0 = no collision detected, otherwise = collision found => warn user for active attack */ +int SHA1DCFinal(unsigned char[20], SHA1_CTX*); + +#if defined(__cplusplus) +} +#endif + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H +#endif + +#endif diff --git a/src/hash/sha1dc/ubc_check.c b/src/hash/sha1dc/ubc_check.c new file mode 100644 index 000000000..b3beff2af --- /dev/null +++ b/src/hash/sha1dc/ubc_check.c @@ -0,0 +1,372 @@ +/*** +* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com> +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +/* +// this file was generated by the 'parse_bitrel' program in the tools section +// using the data files from directory 'tools/data/3565' +// +// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check +// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) +// dm[80] is the expanded message block XOR-difference defined by the DV +// testt is the step to do the recompression from for collision detection +// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check +// +// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs +// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met +// thus one needs to do the recompression check for each DV that has its bit set +// +// ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded +// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c +// ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section +*/ + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include <stdint.h> +#endif +#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C +#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C +#endif +#include "ubc_check.h" + +static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0; +static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1; +static const uint32_t DV_I_45_0_bit = (uint32_t)(1) << 2; +static const uint32_t DV_I_46_0_bit = (uint32_t)(1) << 3; +static const uint32_t DV_I_46_2_bit = (uint32_t)(1) << 4; +static const uint32_t DV_I_47_0_bit = (uint32_t)(1) << 5; +static const uint32_t DV_I_47_2_bit = (uint32_t)(1) << 6; +static const uint32_t DV_I_48_0_bit = (uint32_t)(1) << 7; +static const uint32_t DV_I_48_2_bit = (uint32_t)(1) << 8; +static const uint32_t DV_I_49_0_bit = (uint32_t)(1) << 9; +static const uint32_t DV_I_49_2_bit = (uint32_t)(1) << 10; +static const uint32_t DV_I_50_0_bit = (uint32_t)(1) << 11; +static const uint32_t DV_I_50_2_bit = (uint32_t)(1) << 12; +static const uint32_t DV_I_51_0_bit = (uint32_t)(1) << 13; +static const uint32_t DV_I_51_2_bit = (uint32_t)(1) << 14; +static const uint32_t DV_I_52_0_bit = (uint32_t)(1) << 15; +static const uint32_t DV_II_45_0_bit = (uint32_t)(1) << 16; +static const uint32_t DV_II_46_0_bit = (uint32_t)(1) << 17; +static const uint32_t DV_II_46_2_bit = (uint32_t)(1) << 18; +static const uint32_t DV_II_47_0_bit = (uint32_t)(1) << 19; +static const uint32_t DV_II_48_0_bit = (uint32_t)(1) << 20; +static const uint32_t DV_II_49_0_bit = (uint32_t)(1) << 21; +static const uint32_t DV_II_49_2_bit = (uint32_t)(1) << 22; +static const uint32_t DV_II_50_0_bit = (uint32_t)(1) << 23; +static const uint32_t DV_II_50_2_bit = (uint32_t)(1) << 24; +static const uint32_t DV_II_51_0_bit = (uint32_t)(1) << 25; +static const uint32_t DV_II_51_2_bit = (uint32_t)(1) << 26; +static const uint32_t DV_II_52_0_bit = (uint32_t)(1) << 27; +static const uint32_t DV_II_53_0_bit = (uint32_t)(1) << 28; +static const uint32_t DV_II_54_0_bit = (uint32_t)(1) << 29; +static const uint32_t DV_II_55_0_bit = (uint32_t)(1) << 30; +static const uint32_t DV_II_56_0_bit = (uint32_t)(1) << 31; + +dv_info_t sha1_dvs[] = +{ + {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } } +, {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } } +, {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } } +, {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } } +, {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } } +, {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } } +, {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } } +, {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } } +, {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } } +, {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } } +, {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } } +, {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } } +, {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } } +, {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } } +, {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } } +, {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } } +, {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } } +, {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } } +, {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } } +, {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } } +, {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } } +, {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } } +, {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } } +, {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } } +, {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } } +, {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } } +, {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } } +, {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } } +, {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } } +, {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } } +, {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } } +, {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } } +, {0,0,0,0,0,0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}} +}; +void ubc_check(const uint32_t W[80], uint32_t dvmask[1]) +{ + uint32_t mask = ~((uint32_t)(0)); + mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit)); + mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); + mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); + mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); + mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); + mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit)); + mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); + mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit)); + mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit)); + mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit)); + mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit)); + mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit)); + mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit)); + mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit)); + mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit)); + mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); + mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); + mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); + mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit)); + mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit)); + mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit)); + mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit)); + mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); + mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit)); + mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit)); + mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) + mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); + mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)) + mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); + if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)) + mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)); + if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)) + mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)) + mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); + if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)) + mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)); + if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)) + mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); + if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)) + mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)); + if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)) + mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)); + if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)) + mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)); + if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)) + mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)); + if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)) + mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)); + if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)) + mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)); + mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit)); + if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)) + mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)); + mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit)); + if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)) + mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)); + mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit)); + mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit)); + if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)) + mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)); + mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit)); + if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)) + mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) + mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); + if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)) + mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); + mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit)); + if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)) + mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)); + if (mask & (DV_I_48_0_bit|DV_II_48_0_bit)) + mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit)); + if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) + mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); + if (mask & (DV_I_47_0_bit|DV_II_47_0_bit)) + mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit)); + if (mask & (DV_I_46_0_bit|DV_II_46_0_bit)) + mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit)); + mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit)); + if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) + mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); + if (mask & (DV_II_51_0_bit|DV_II_54_0_bit)) + mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit)); + if (mask & (DV_II_50_0_bit|DV_II_53_0_bit)) + mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit)); + if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) + mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); + if (mask & (DV_II_51_0_bit|DV_II_52_0_bit)) + mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit)); + if (mask & (DV_II_49_0_bit|DV_II_52_0_bit)) + mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit)); + if (mask & (DV_II_51_0_bit|DV_II_53_0_bit)) + mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit)); + if (mask & (DV_II_50_0_bit|DV_II_52_0_bit)) + mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit)); + if (mask & (DV_II_49_0_bit|DV_II_51_0_bit)) + mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit)); + mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); + mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); + if (mask & (DV_I_51_0_bit|DV_I_52_0_bit)) + mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit)); + mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit)); + mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit)); + mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit)); + mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit)); + mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit)); + mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit)); + mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit)); + mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit)); + mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit)); + mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit)); + if (mask & (DV_I_52_0_bit|DV_II_51_0_bit)) + mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit)); + if (mask & (DV_I_51_0_bit|DV_II_50_0_bit)) + mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit)); + if (mask & (DV_I_48_2_bit|DV_I_51_2_bit)) + mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit)); + if (mask & (DV_I_50_0_bit|DV_II_49_0_bit)) + mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit)); + if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) + mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); + mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit)); + if (mask & (DV_I_51_0_bit|DV_II_47_0_bit)) + mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit)); +if (mask) { + + if (mask & DV_I_43_0_bit) + if ( + !((W[61]^(W[62]>>5)) & (1<<1)) + || !(!((W[59]^(W[63]>>25)) & (1<<5))) + || !((W[58]^(W[63]>>30)) & (1<<0)) + ) mask &= ~DV_I_43_0_bit; + if (mask & DV_I_44_0_bit) + if ( + !((W[62]^(W[63]>>5)) & (1<<1)) + || !(!((W[60]^(W[64]>>25)) & (1<<5))) + || !((W[59]^(W[64]>>30)) & (1<<0)) + ) mask &= ~DV_I_44_0_bit; + if (mask & DV_I_46_2_bit) + mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit); + if (mask & DV_I_47_2_bit) + if ( + !((W[62]^(W[63]>>5)) & (1<<2)) + || !(!((W[41]^W[43]) & (1<<6))) + ) mask &= ~DV_I_47_2_bit; + if (mask & DV_I_48_2_bit) + if ( + !((W[63]^(W[64]>>5)) & (1<<2)) + || !(!((W[48]^(W[49]<<5)) & (1<<6))) + ) mask &= ~DV_I_48_2_bit; + if (mask & DV_I_49_2_bit) + if ( + !(!((W[49]^(W[50]<<5)) & (1<<6))) + || !((W[42]^W[50]) & (1<<1)) + || !(!((W[39]^(W[40]<<5)) & (1<<6))) + || !((W[38]^W[40]) & (1<<1)) + ) mask &= ~DV_I_49_2_bit; + if (mask & DV_I_50_0_bit) + mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit); + if (mask & DV_I_50_2_bit) + mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit); + if (mask & DV_I_51_0_bit) + mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit); + if (mask & DV_I_51_2_bit) + if ( + !(!((W[51]^(W[52]<<5)) & (1<<6))) + || !(!((W[49]^W[51]) & (1<<6))) + || !(!((W[37]^(W[37]>>5)) & (1<<1))) + || !(!((W[35]^(W[39]>>25)) & (1<<5))) + ) mask &= ~DV_I_51_2_bit; + if (mask & DV_I_52_0_bit) + mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit); + if (mask & DV_II_46_2_bit) + mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit); + if (mask & DV_II_48_0_bit) + if ( + !(!((W[36]^(W[40]>>25)) & (1<<3))) + || !((W[35]^(W[40]<<2)) & (1<<30)) + ) mask &= ~DV_II_48_0_bit; + if (mask & DV_II_49_0_bit) + if ( + !(!((W[37]^(W[41]>>25)) & (1<<3))) + || !((W[36]^(W[41]<<2)) & (1<<30)) + ) mask &= ~DV_II_49_0_bit; + if (mask & DV_II_49_2_bit) + if ( + !(!((W[53]^(W[54]<<5)) & (1<<6))) + || !(!((W[51]^W[53]) & (1<<6))) + || !((W[50]^W[54]) & (1<<1)) + || !(!((W[45]^(W[46]<<5)) & (1<<6))) + || !(!((W[37]^(W[41]>>25)) & (1<<5))) + || !((W[36]^(W[41]>>30)) & (1<<0)) + ) mask &= ~DV_II_49_2_bit; + if (mask & DV_II_50_0_bit) + if ( + !((W[55]^W[58]) & (1<<29)) + || !(!((W[38]^(W[42]>>25)) & (1<<3))) + || !((W[37]^(W[42]<<2)) & (1<<30)) + ) mask &= ~DV_II_50_0_bit; + if (mask & DV_II_50_2_bit) + if ( + !(!((W[54]^(W[55]<<5)) & (1<<6))) + || !(!((W[52]^W[54]) & (1<<6))) + || !((W[51]^W[55]) & (1<<1)) + || !((W[45]^W[47]) & (1<<1)) + || !(!((W[38]^(W[42]>>25)) & (1<<5))) + || !((W[37]^(W[42]>>30)) & (1<<0)) + ) mask &= ~DV_II_50_2_bit; + if (mask & DV_II_51_0_bit) + if ( + !(!((W[39]^(W[43]>>25)) & (1<<3))) + || !((W[38]^(W[43]<<2)) & (1<<30)) + ) mask &= ~DV_II_51_0_bit; + if (mask & DV_II_51_2_bit) + if ( + !(!((W[55]^(W[56]<<5)) & (1<<6))) + || !(!((W[53]^W[55]) & (1<<6))) + || !((W[52]^W[56]) & (1<<1)) + || !((W[46]^W[48]) & (1<<1)) + || !(!((W[39]^(W[43]>>25)) & (1<<5))) + || !((W[38]^(W[43]>>30)) & (1<<0)) + ) mask &= ~DV_II_51_2_bit; + if (mask & DV_II_52_0_bit) + if ( + !(!((W[59]^W[60]) & (1<<29))) + || !(!((W[40]^(W[44]>>25)) & (1<<3))) + || !(!((W[40]^(W[44]>>25)) & (1<<4))) + || !((W[39]^(W[44]<<2)) & (1<<30)) + ) mask &= ~DV_II_52_0_bit; + if (mask & DV_II_53_0_bit) + if ( + !((W[58]^W[61]) & (1<<29)) + || !(!((W[57]^(W[61]>>25)) & (1<<4))) + || !(!((W[41]^(W[45]>>25)) & (1<<3))) + || !(!((W[41]^(W[45]>>25)) & (1<<4))) + ) mask &= ~DV_II_53_0_bit; + if (mask & DV_II_54_0_bit) + if ( + !(!((W[58]^(W[62]>>25)) & (1<<4))) + || !(!((W[42]^(W[46]>>25)) & (1<<3))) + || !(!((W[42]^(W[46]>>25)) & (1<<4))) + ) mask &= ~DV_II_54_0_bit; + if (mask & DV_II_55_0_bit) + if ( + !(!((W[59]^(W[63]>>25)) & (1<<4))) + || !(!((W[57]^(W[59]>>25)) & (1<<4))) + || !(!((W[43]^(W[47]>>25)) & (1<<3))) + || !(!((W[43]^(W[47]>>25)) & (1<<4))) + ) mask &= ~DV_II_55_0_bit; + if (mask & DV_II_56_0_bit) + if ( + !(!((W[60]^(W[64]>>25)) & (1<<4))) + || !(!((W[44]^(W[48]>>25)) & (1<<3))) + || !(!((W[44]^(W[48]>>25)) & (1<<4))) + ) mask &= ~DV_II_56_0_bit; +} + + dvmask[0]=mask; +} + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C +#endif diff --git a/src/hash/sha1dc/ubc_check.h b/src/hash/sha1dc/ubc_check.h new file mode 100644 index 000000000..d7e17dc73 --- /dev/null +++ b/src/hash/sha1dc/ubc_check.h @@ -0,0 +1,52 @@ +/*** +* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com> +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +/* +// this file was generated by the 'parse_bitrel' program in the tools section +// using the data files from directory 'tools/data/3565' +// +// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check +// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) +// dm[80] is the expanded message block XOR-difference defined by the DV +// testt is the step to do the recompression from for collision detection +// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check +// +// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs +// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met +// thus one needs to do the recompression check for each DV that has its bit set +*/ + +#ifndef SHA1DC_UBC_CHECK_H +#define SHA1DC_UBC_CHECK_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include <stdint.h> +#endif + +#define DVMASKSIZE 1 +typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t; +extern dv_info_t sha1_dvs[]; +void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]); + +#define DOSTORESTATE58 +#define DOSTORESTATE65 + +#define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0]) + +#if defined(__cplusplus) +} +#endif + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H +#endif + +#endif diff --git a/src/hashsig.c b/src/hashsig.c index e99637d8b..30d059463 100644 --- a/src/hashsig.c +++ b/src/hashsig.c @@ -4,6 +4,9 @@ * 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 "common.h" + #include "git2/sys/hashsig.h" #include "fileops.h" #include "util.h" @@ -214,7 +217,7 @@ static int hashsig_finalize_hashes(git_hashsig *sig) if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE && !(sig->opt & GIT_HASHSIG_ALLOW_SMALL_FILES)) { giterr_set(GITERR_INVALID, - "File too small for similarity signature calculation"); + "file too small for similarity signature calculation"); return GIT_EBUFS; } @@ -286,7 +289,7 @@ int git_hashsig_create_fromfile( if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) { if ((error = (int)buflen) < 0) giterr_set(GITERR_OS, - "Read error on '%s' calculating similarity hashes", path); + "read error on '%s' calculating similarity hashes", path); break; } diff --git a/src/ident.c b/src/ident.c index 4718ed664..7eccf9a43 100644 --- a/src/ident.c +++ b/src/ident.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #include "git2/sys/filter.h" #include "filter.h" #include "buffer.h" diff --git a/src/idxmap.c b/src/idxmap.c new file mode 100644 index 000000000..45f0c2204 --- /dev/null +++ b/src/idxmap.c @@ -0,0 +1,133 @@ +/* + * 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 "idxmap.h" + +/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */ +static kh_inline khint_t idxentry_hash(const git_index_entry *e) +{ + const char *s = e->path; + khint_t h = (khint_t)git__tolower(*s); + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s); + return h + GIT_IDXENTRY_STAGE(e); +} + +#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0) +#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0) + +__KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal) +__KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal) + +int git_idxmap_alloc(git_idxmap **map) +{ + if ((*map = kh_init(idx)) == NULL) { + giterr_set_oom(); + return -1; + } + + return 0; +} + +int git_idxmap_icase_alloc(git_idxmap_icase **map) +{ + if ((*map = kh_init(idxicase)) == NULL) { + giterr_set_oom(); + return -1; + } + + return 0; +} + +void git_idxmap_insert(git_idxmap *map, const git_index_entry *key, void *value, int *rval) +{ + khiter_t idx = kh_put(idx, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_idxmap_icase_insert(git_idxmap_icase *map, const git_index_entry *key, void *value, int *rval) +{ + khiter_t idx = kh_put(idxicase, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +size_t git_idxmap_lookup_index(git_idxmap *map, const git_index_entry *key) +{ + return kh_get(idx, map, key); +} + +size_t git_idxmap_icase_lookup_index(git_idxmap_icase *map, const git_index_entry *key) +{ + return kh_get(idxicase, map, key); +} + +void *git_idxmap_value_at(git_idxmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +int git_idxmap_valid_index(git_idxmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_idxmap_has_data(git_idxmap *map, size_t idx) +{ + return kh_exist(map, idx); +} + +void git_idxmap_resize(git_idxmap *map, size_t size) +{ + kh_resize(idx, map, size); +} + +void git_idxmap_icase_resize(git_idxmap_icase *map, size_t size) +{ + kh_resize(idxicase, map, size); +} + +void git_idxmap_free(git_idxmap *map) +{ + kh_destroy(idx, map); +} + +void git_idxmap_clear(git_idxmap *map) +{ + kh_clear(idx, map); +} + +void git_idxmap_delete_at(git_idxmap *map, size_t idx) +{ + kh_del(idx, map, idx); +} + +void git_idxmap_icase_delete_at(git_idxmap_icase *map, size_t idx) +{ + kh_del(idxicase, map, idx); +} + +void git_idxmap_delete(git_idxmap *map, const git_index_entry *key) +{ + khiter_t idx = git_idxmap_lookup_index(map, key); + if (git_idxmap_valid_index(map, idx)) + git_idxmap_delete_at(map, idx); +} +void git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key) +{ + khiter_t idx = git_idxmap_icase_lookup_index(map, key); + if (git_idxmap_valid_index((git_idxmap *)map, idx)) + git_idxmap_icase_delete_at(map, idx); +} diff --git a/src/idxmap.h b/src/idxmap.h index 4122a89fe..f7e903a61 100644 --- a/src/idxmap.h +++ b/src/idxmap.h @@ -7,8 +7,9 @@ #ifndef INCLUDE_idxmap_h__ #define INCLUDE_idxmap_h__ -#include <ctype.h> #include "common.h" + +#include <ctype.h> #include "git2/index.h" #define kmalloc git__malloc @@ -26,66 +27,27 @@ typedef khash_t(idxicase) git_idxmap_icase; typedef khiter_t git_idxmap_iter; -/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */ -static kh_inline khint_t idxentry_hash(const git_index_entry *e) -{ - const char *s = e->path; - khint_t h = (khint_t)git__tolower(*s); - if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s); - return h + GIT_IDXENTRY_STAGE(e); -} - -#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0) -#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0) - -#define GIT__USE_IDXMAP \ - __KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal) - -#define GIT__USE_IDXMAP_ICASE \ - __KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal) - -#define git_idxmap_alloc(hp) \ - ((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0 - -#define git_idxmap_icase_alloc(hp) \ - ((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0 - -#define git_idxmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(idx, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_idxmap_icase_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(idxicase, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_idxmap_lookup_index(h, k) kh_get(idx, h, k) -#define git_idxmap_icase_lookup_index(h, k) kh_get(idxicase, h, k) -#define git_idxmap_value_at(h, idx) kh_val(h, idx) -#define git_idxmap_valid_index(h, idx) (idx != kh_end(h)) -#define git_idxmap_has_data(h, idx) kh_exist(h, idx) +int git_idxmap_alloc(git_idxmap **map); +int git_idxmap_icase_alloc(git_idxmap_icase **map); +void git_idxmap_insert(git_idxmap *map, const git_index_entry *key, void *value, int *rval); +void git_idxmap_icase_insert(git_idxmap_icase *map, const git_index_entry *key, void *value, int *rval); -#define git_idxmap_resize(h,s) kh_resize(idx, h, s) -#define git_idxmap_free(h) kh_destroy(idx, h), h = NULL -#define git_idxmap_clear(h) kh_clear(idx, h) +size_t git_idxmap_lookup_index(git_idxmap *map, const git_index_entry *key); +size_t git_idxmap_icase_lookup_index(git_idxmap_icase *map, const git_index_entry *key); +void *git_idxmap_value_at(git_idxmap *map, size_t idx); +int git_idxmap_valid_index(git_idxmap *map, size_t idx); +int git_idxmap_has_data(git_idxmap *map, size_t idx); -#define git_idxmap_delete_at(h, id) kh_del(idx, h, id) -#define git_idxmap_icase_delete_at(h, id) kh_del(idxicase, h, id) +void git_idxmap_resize(git_idxmap *map, size_t size); +void git_idxmap_icase_resize(git_idxmap_icase *map, size_t size); +void git_idxmap_free(git_idxmap *map); +void git_idxmap_clear(git_idxmap *map); -#define git_idxmap_delete(h, key) do { \ - khiter_t __pos = git_idxmap_lookup_index(h, key); \ - if (git_idxmap_valid_index(h, __pos)) \ - git_idxmap_delete_at(h, __pos); } while (0) +void git_idxmap_delete_at(git_idxmap *map, size_t idx); +void git_idxmap_icase_delete_at(git_idxmap_icase *map, size_t idx); -#define git_idxmap_icase_delete(h, key) do { \ - khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \ - if (git_idxmap_valid_index(h, __pos)) \ - git_idxmap_icase_delete_at(h, __pos); } while (0) +void git_idxmap_delete(git_idxmap *map, const git_index_entry *key); +void git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key); #define git_idxmap_begin kh_begin #define git_idxmap_end kh_end diff --git a/src/ignore.c b/src/ignore.c index dcbd5c1ca..f089dbeb5 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -1,6 +1,14 @@ +/* + * 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 "ignore.h" + #include "git2/ignore.h" #include "common.h" -#include "ignore.h" #include "attrcache.h" #include "path.h" #include "config.h" @@ -40,38 +48,42 @@ */ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg) { + int (*cmp)(const char *, const char *, size_t); git_attr_fnmatch *longer, *shorter; char *p; - if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0 - && (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) { - - /* If lengths match we need to have an exact match */ - if (rule->length == neg->length) { - return strcmp(rule->pattern, neg->pattern) == 0; - } else if (rule->length < neg->length) { - shorter = rule; - longer = neg; - } else { - shorter = neg; - longer = rule; - } - - /* Otherwise, we need to check if the shorter - * rule is a basename only (that is, it contains - * no path separator) and, if so, if it - * matches the tail of the longer rule */ - p = longer->pattern + longer->length - shorter->length; + if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 + || (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) + return false; + + if (neg->flags & GIT_ATTR_FNMATCH_ICASE) + cmp = git__strncasecmp; + else + cmp = strncmp; + + /* If lengths match we need to have an exact match */ + if (rule->length == neg->length) { + return cmp(rule->pattern, neg->pattern, rule->length) == 0; + } else if (rule->length < neg->length) { + shorter = rule; + longer = neg; + } else { + shorter = neg; + longer = rule; + } - if (p[-1] != '/') - return false; - if (memchr(shorter->pattern, '/', shorter->length) != NULL) - return false; + /* Otherwise, we need to check if the shorter + * rule is a basename only (that is, it contains + * no path separator) and, if so, if it + * matches the tail of the longer rule */ + p = longer->pattern + longer->length - shorter->length; - return memcmp(p, shorter->pattern, shorter->length) == 0; - } + if (p[-1] != '/') + return false; + if (memchr(shorter->pattern, '/', shorter->length) != NULL) + return false; - return false; + return cmp(p, shorter->pattern, shorter->length) == 0; } /** @@ -89,7 +101,7 @@ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg) */ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match) { - int error = 0; + int error = 0, fnflags; size_t i; git_attr_fnmatch *rule; char *path; @@ -97,6 +109,10 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match *out = 0; + fnflags = FNM_PATHNAME; + if (match->flags & GIT_ATTR_FNMATCH_ICASE) + fnflags |= FNM_IGNORECASE; + /* path of the file relative to the workdir, so we match the rules in subdirs */ if (match->containing_dir) { git_buf_puts(&buf, match->containing_dir); @@ -117,12 +133,12 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match continue; } - /* - * When dealing with a directory, we add '/<star>' so - * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR - * alone isn't enough as that's also set for nagations, so we - * need to check that NEGATIVE is off. - */ + /* + * When dealing with a directory, we add '/<star>' so + * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR + * alone isn't enough as that's also set for nagations, so we + * need to check that NEGATIVE is off. + */ git_buf_clear(&buf); if (rule->containing_dir) { git_buf_puts(&buf, rule->containing_dir); @@ -136,7 +152,7 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match if (error < 0) goto out; - if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) { + if ((error = p_fnmatch(git_buf_cstr(&buf), path, fnflags)) < 0) { giterr_set(GITERR_INVALID, "error matching pattern"); goto out; } @@ -175,7 +191,7 @@ static int parse_ignore_file( context = attrs->entry->path; if (git_mutex_lock(&attrs->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock ignore file"); + giterr_set(GITERR_OS, "failed to lock ignore file"); return -1; } @@ -199,8 +215,14 @@ static int parse_ignore_file( scan = git__next_line(scan); - /* if a negative match doesn't actually do anything, throw it away */ - if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) + /* + * If a negative match doesn't actually do anything, + * throw it away. As we cannot always verify whether a + * rule containing wildcards negates another rule, we + * do not optimize away these rules, though. + * */ + if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE + && !(match->flags & GIT_ATTR_FNMATCH_HASWILD)) error = does_negate_rule(&valid_rule, &attrs->rules, match); if (!error && valid_rule) @@ -277,8 +299,9 @@ int git_ignore__for_path( { int error = 0; const char *workdir = git_repository_workdir(repo); + git_buf infopath = GIT_BUF_INIT; - assert(ignores && path); + assert(repo && ignores && path); memset(ignores, 0, sizeof(*ignores)); ignores->repo = repo; @@ -322,10 +345,14 @@ int git_ignore__for_path( goto cleanup; } + if ((error = git_repository_item_path(&infopath, + repo, GIT_REPOSITORY_ITEM_INFO)) < 0) + goto cleanup; + /* load .git/info/exclude */ error = push_ignore_file( ignores, &ignores->ign_global, - git_repository_path(repo), GIT_IGNORE_FILE_INREPO); + infopath.ptr, GIT_IGNORE_FILE_INREPO); if (error < 0) goto cleanup; @@ -336,6 +363,7 @@ int git_ignore__for_path( git_repository_attr_cache(repo)->cfg_excl_file); cleanup: + git_buf_free(&infopath); if (error < 0) git_ignore__free(ignores); @@ -503,9 +531,9 @@ int git_ignore_path_is_ignored( unsigned int i; git_attr_file *file; - assert(ignored && pathname); + assert(repo && ignored && pathname); - workdir = repo ? git_repository_workdir(repo) : NULL; + workdir = git_repository_workdir(repo); memset(&path, 0, sizeof(path)); memset(&ignores, 0, sizeof(ignores)); diff --git a/src/ignore.h b/src/ignore.h index d40bd60f9..5895d3faa 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -7,12 +7,14 @@ #ifndef INCLUDE_ignore_h__ #define INCLUDE_ignore_h__ +#include "common.h" + #include "repository.h" #include "vector.h" #include "attr_file.h" #define GIT_IGNORE_FILE ".gitignore" -#define GIT_IGNORE_FILE_INREPO "info/exclude" +#define GIT_IGNORE_FILE_INREPO "exclude" #define GIT_IGNORE_FILE_XDG "ignore" /* The git_ignores structure maintains three sets of ignores: diff --git a/src/index.c b/src/index.c index bc15959a8..b7602fb44 100644 --- a/src/index.c +++ b/src/index.c @@ -5,11 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "index.h" + #include <stddef.h> -#include "common.h" #include "repository.h" -#include "index.h" #include "tree.h" #include "tree-cache.h" #include "hash.h" @@ -27,9 +27,6 @@ #include "git2/config.h" #include "git2/sys/index.h" -GIT__USE_IDXMAP -GIT__USE_IDXMAP_ICASE - #define INSERT_IN_MAP_EX(idx, map, e, err) do { \ if ((idx)->ignore_case) \ git_idxmap_icase_insert((khash_t(idxicase) *) (map), (e), (e), (err)); \ @@ -57,10 +54,6 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr unsigned int flags, git_index_matched_path_cb cb, void *payload); -#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) -#define short_entry_size(len) entry_size(struct entry_short, len) -#define long_entry_size(len) entry_size(struct entry_long, len) - #define minimal_entry_size (offsetof(struct entry_short, path)) static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ; @@ -552,7 +545,7 @@ int git_index_clear(git_index *index) static int create_index_error(int error, const char *msg) { - giterr_set(GITERR_INDEX, msg); + giterr_set_str(GITERR_INDEX, msg); return error; } @@ -570,7 +563,7 @@ int git_index_set_caps(git_index *index, int caps) if (!repo) return create_index_error( - -1, "Cannot access repository to set index caps"); + -1, "cannot access repository to set index caps"); if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORECASE)) index->ignore_case = (val != 0); @@ -639,7 +632,7 @@ int git_index_read(git_index *index, int force) if (!index->index_file_path) return create_index_error(-1, - "Failed to read index: The index is in-memory only"); + "failed to read index: The index is in-memory only"); index->on_disk = git_path_exists(index->index_file_path); @@ -653,7 +646,7 @@ int git_index_read(git_index *index, int force) ((updated = compare_checksum(index)) < 0)) { giterr_set( GITERR_INDEX, - "Failed to read index: '%s' no longer exists", + "failed to read index: '%s' no longer exists", index->index_file_path); return updated; } @@ -765,7 +758,7 @@ int git_index_set_version(git_index *index, unsigned int version) if (version < INDEX_VERSION_NUMBER_LB || version > INDEX_VERSION_NUMBER_UB) { - giterr_set(GITERR_INDEX, "Invalid version number"); + giterr_set(GITERR_INDEX, "invalid version number"); return -1; } @@ -805,7 +798,7 @@ int git_index_write_tree(git_oid *oid, git_index *index) if (repo == NULL) return create_index_error(-1, "Failed to write tree. " - "The index file is not backed up by an existing repository"); + "the index file is not backed up by an existing repository"); return git_tree__write_index(oid, index, repo); } @@ -847,7 +840,7 @@ const git_index_entry *git_index_get_bypath( if (git_idxmap_valid_index(index->entries_map, pos)) return git_idxmap_value_at(index->entries_map, pos); - giterr_set(GITERR_INDEX, "Index does not contain %s", path); + giterr_set(GITERR_INDEX, "index does not contain '%s'", path); return NULL; } @@ -934,7 +927,7 @@ static int index_entry_init( if (INDEX_OWNER(index) == NULL) return create_index_error(-1, - "Could not initialize index entry. " + "could not initialize index entry. " "Index is not backed up by an existing repository."); if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0) @@ -1365,7 +1358,7 @@ static int index_insert( error = git_vector_insert_sorted(&index->entries, entry, index_no_dups); if (error == 0) { - INSERT_IN_MAP(index, entry, error); + INSERT_IN_MAP(index, entry, &error); } } @@ -1423,7 +1416,7 @@ int git_index_add_frombuffer( if (INDEX_OWNER(index) == NULL) return create_index_error(-1, - "Could not initialize index entry. " + "could not initialize index entry. " "Index is not backed up by an existing repository."); if (!valid_filemode(source_entry->mode)) { @@ -1592,7 +1585,7 @@ int git_index__fill(git_index *index, const git_vector *source_entries) if ((ret = git_vector_insert(&index->entries, entry)) < 0) break; - INSERT_IN_MAP(index, entry, ret); + INSERT_IN_MAP(index, entry, &ret); if (ret < 0) break; } @@ -1637,7 +1630,7 @@ int git_index_remove(git_index *index, const char *path, int stage) if (index_find(&position, index, path, 0, stage) < 0) { giterr_set( - GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); + GITERR_INDEX, "index does not contain %s at stage %d", path, stage); error = GIT_ENOTFOUND; } else { error = index_remove_entry(index, position); @@ -1709,7 +1702,7 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) if (git_vector_bsearch2( &pos, &index->entries, index->entries_search_path, path) < 0) { - giterr_set(GITERR_INDEX, "Index does not contain %s", path); + giterr_set(GITERR_INDEX, "index does not contain %s", path); return GIT_ENOTFOUND; } @@ -2153,7 +2146,7 @@ void git_index_reuc_clear(git_index *index) static int index_error_invalid(const char *message) { - giterr_set(GITERR_INDEX, "Invalid data in index - %s", message); + giterr_set(GITERR_INDEX, "invalid data in index - %s", message); return -1; } @@ -2285,12 +2278,29 @@ out_err: return 0; } +static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flags) +{ + if (varint_len) { + if (flags & GIT_IDXENTRY_EXTENDED) + return offsetof(struct entry_long, path) + path_len + 1 + varint_len; + else + return offsetof(struct entry_short, path) + path_len + 1 + varint_len; + } else { +#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) + if (flags & GIT_IDXENTRY_EXTENDED) + return entry_size(struct entry_long, path_len); + else + return entry_size(struct entry_short, path_len); +#undef entry_size + } +} + static size_t read_entry( git_index_entry **out, git_index *index, const void *buffer, size_t buffer_size, - const char **last) + const char *last) { size_t path_length, entry_size; const char *path_ptr; @@ -2347,35 +2357,34 @@ static size_t read_entry( path_length = path_end - path_ptr; } - if (entry.flags & GIT_IDXENTRY_EXTENDED) - entry_size = long_entry_size(path_length); - else - entry_size = short_entry_size(path_length); - - if (INDEX_FOOTER_SIZE + entry_size > buffer_size) - return 0; - + entry_size = index_entry_size(path_length, 0, entry.flags); entry.path = (char *)path_ptr; } else { size_t varint_len; - size_t shared = git_decode_varint((const unsigned char *)path_ptr, - &varint_len); - size_t len = strlen(path_ptr + varint_len); - size_t last_len = strlen(*last); - size_t tmp_path_len; + size_t strip_len = git_decode_varint((const unsigned char *)path_ptr, + &varint_len); + size_t last_len = strlen(last); + size_t prefix_len = last_len - strip_len; + size_t suffix_len = strlen(path_ptr + varint_len); + size_t path_len; if (varint_len == 0) return index_error_invalid("incorrect prefix length"); - GITERR_CHECK_ALLOC_ADD(&tmp_path_len, shared, len + 1); - tmp_path = git__malloc(tmp_path_len); + GITERR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len); + GITERR_CHECK_ALLOC_ADD(&path_len, path_len, 1); + tmp_path = git__malloc(path_len); GITERR_CHECK_ALLOC(tmp_path); - memcpy(tmp_path, last, last_len); - memcpy(tmp_path + last_len, path_ptr + varint_len, len); - entry_size = long_entry_size(shared + len); + + memcpy(tmp_path, last, prefix_len); + memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1); + entry_size = index_entry_size(suffix_len, varint_len, entry.flags); entry.path = tmp_path; } + if (INDEX_FOOTER_SIZE + entry_size > buffer_size) + return 0; + if (index_entry_dup(out, index, &entry) < 0) { git__free(tmp_path); return 0; @@ -2448,7 +2457,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) unsigned int i; struct index_header header = { 0 }; git_oid checksum_calculated, checksum_expected; - const char **last = NULL; + const char *last = NULL; const char *empty = ""; #define seek_forward(_increase) { \ @@ -2472,16 +2481,16 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) index->version = header.version; if (index->version >= INDEX_VERSION_NUMBER_COMP) - last = ∅ + last = empty; seek_forward(INDEX_HEADER_SIZE); assert(!index->entries.length); if (index->ignore_case) - kh_resize(idxicase, (khash_t(idxicase) *) index->entries_map, header.entry_count); + git_idxmap_icase_resize((khash_t(idxicase) *) index->entries_map, header.entry_count); else - kh_resize(idx, index->entries_map, header.entry_count); + git_idxmap_resize(index->entries_map, header.entry_count); /* Parse all the entries */ for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { @@ -2499,7 +2508,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) goto done; } - INSERT_IN_MAP(index, entry, error); + INSERT_IN_MAP(index, entry, &error); if (error < 0) { index_entry_free(entry); @@ -2507,6 +2516,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) } error = 0; + if (index->version >= INDEX_VERSION_NUMBER_COMP) + last = entry->path; + seek_forward(entry_size); } @@ -2577,11 +2589,12 @@ static bool is_index_extended(git_index *index) return (extended > 0); } -static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char **last) +static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char *last) { void *mem = NULL; struct entry_short *ondisk; size_t path_len, disk_size; + int varint_len = 0; char *path; const char *path_start = entry->path; size_t same_len = 0; @@ -2589,7 +2602,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha path_len = ((struct entry_internal *)entry)->pathlen; if (last) { - const char *last_c = *last; + const char *last_c = last; while (*path_start == *last_c) { if (!*path_start || !*last_c) @@ -2599,13 +2612,10 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha ++same_len; } path_len -= same_len; - *last = entry->path; + varint_len = git_encode_varint(NULL, 0, same_len); } - if (entry->flags & GIT_IDXENTRY_EXTENDED) - disk_size = long_entry_size(path_len); - else - disk_size = short_entry_size(path_len); + disk_size = index_entry_size(path_len, varint_len, entry->flags); if (git_filebuf_reserve(file, &mem, disk_size) < 0) return -1; @@ -2645,16 +2655,34 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha ondisk_ext->flags_extended = htons(entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS); path = ondisk_ext->path; - } - else + disk_size -= offsetof(struct entry_long, path); + } else { path = ondisk->path; + disk_size -= offsetof(struct entry_short, path); + } if (last) { - path += git_encode_varint((unsigned char *) path, - disk_size, - path_len - same_len); + varint_len = git_encode_varint((unsigned char *) path, + disk_size, same_len); + assert(varint_len > 0); + path += varint_len; + disk_size -= varint_len; + + /* + * If using path compression, we are not allowed + * to have additional trailing NULs. + */ + assert(disk_size == path_len + 1); + } else { + /* + * If no path compression is used, we do have + * NULs as padding. As such, simply assert that + * we have enough space left to write the path. + */ + assert(disk_size > path_len); } - memcpy(path, path_start, path_len); + + memcpy(path, path_start, path_len + 1); return 0; } @@ -2665,8 +2693,7 @@ static int write_entries(git_index *index, git_filebuf *file) size_t i; git_vector case_sorted, *entries; git_index_entry *entry; - const char **last = NULL; - const char *empty = ""; + const char *last = NULL; /* If index->entries is sorted case-insensitively, then we need * to re-sort it case-sensitively before writing */ @@ -2679,11 +2706,14 @@ static int write_entries(git_index *index, git_filebuf *file) } if (index->version >= INDEX_VERSION_NUMBER_COMP) - last = ∅ + last = ""; - git_vector_foreach(entries, i, entry) + git_vector_foreach(entries, i, entry) { if ((error = write_disk_entry(file, entry, last)) < 0) break; + if (index->version >= INDEX_VERSION_NUMBER_COMP) + last = entry->path; + } if (index->ignore_case) git_vector_free(&case_sorted); @@ -2979,12 +3009,12 @@ int git_index_read_tree(git_index *index, const git_tree *tree) goto cleanup; if (index->ignore_case) - kh_resize(idxicase, (khash_t(idxicase) *) entries_map, entries.length); + git_idxmap_icase_resize((khash_t(idxicase) *) entries_map, entries.length); else - kh_resize(idx, entries_map, entries.length); + git_idxmap_resize(entries_map, entries.length); git_vector_foreach(&entries, i, e) { - INSERT_IN_MAP_EX(index, entries_map, e, error); + INSERT_IN_MAP_EX(index, entries_map, e, &error); if (error < 0) { giterr_set(GITERR_INDEX, "failed to insert entry into map"); @@ -3037,9 +3067,9 @@ static int git_index_read_iterator( goto done; if (index->ignore_case && new_length_hint) - kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_length_hint); + git_idxmap_icase_resize((khash_t(idxicase) *) new_entries_map, new_length_hint); else if (new_length_hint) - kh_resize(idx, new_entries_map, new_length_hint); + git_idxmap_resize(new_entries_map, new_length_hint); opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_CONFLICTS; @@ -3103,7 +3133,7 @@ static int git_index_read_iterator( if (add_entry) { if ((error = git_vector_insert(&new_entries, add_entry)) == 0) - INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); + INSERT_IN_MAP_EX(index, new_entries_map, add_entry, &error); } if (remove_entry && error >= 0) @@ -3390,7 +3420,7 @@ static int index_apply_to_all( i--; /* back up foreach if we removed this */ break; default: - giterr_set(GITERR_INVALID, "Unknown index action %d", action); + giterr_set(GITERR_INVALID, "unknown index action %d", action); error = -1; break; } @@ -3475,13 +3505,13 @@ int git_indexwriter_init( if (!index->index_file_path) return create_index_error(-1, - "Failed to write index: The index is in-memory only"); + "failed to write index: The index is in-memory only"); if ((error = git_filebuf_open( &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) { if (error == GIT_ELOCKED) - giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process"); + giterr_set(GITERR_INDEX, "the index is locked; this might be due to a concurrent or crashed process"); return error; } @@ -3530,7 +3560,7 @@ int git_indexwriter_commit(git_indexwriter *writer) if ((error = git_futils_filestamp_check( &writer->index->stamp, writer->index->index_file_path)) < 0) { - giterr_set(GITERR_OS, "Could not read index timestamp"); + giterr_set(GITERR_OS, "could not read index timestamp"); return -1; } diff --git a/src/index.h b/src/index.h index 9918f140d..0f1c0956c 100644 --- a/src/index.h +++ b/src/index.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_index_h__ #define INCLUDE_index_h__ +#include "common.h" + #include "fileops.h" #include "filebuf.h" #include "vector.h" diff --git a/src/indexer.c b/src/indexer.c index a3a866989..766bbc3c3 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -5,10 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "indexer.h" + #include "git2/indexer.h" #include "git2/object.h" -#include "common.h" #include "pack.h" #include "mwindow.h" #include "posix.h" @@ -17,8 +18,7 @@ #include "oid.h" #include "oidmap.h" #include "zstream.h" - -GIT__USE_OIDMAP +#include "object.h" extern git_mutex git__mwindow_mutex; @@ -33,9 +33,10 @@ struct entry { struct git_indexer { unsigned int parsed_header :1, - opened_pack :1, + pack_committed :1, have_stream :1, - have_delta :1; + have_delta :1, + do_fsync :1; struct git_pack_header hdr; struct git_pack_file *pack; unsigned int mode; @@ -83,12 +84,12 @@ static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) /* Verify we recognize this pack file format. */ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) { - giterr_set(GITERR_INDEXER, "Wrong pack signature"); + giterr_set(GITERR_INDEXER, "wrong pack signature"); return -1; } if (!pack_version_ok(hdr->hdr_version)) { - giterr_set(GITERR_INDEXER, "Wrong pack version"); + giterr_set(GITERR_INDEXER, "wrong pack version"); return -1; } @@ -125,6 +126,9 @@ int git_indexer_new( git_hash_ctx_init(&idx->hash_ctx); git_hash_ctx_init(&idx->trailer); + if (git_repository__fsync_gitdir) + idx->do_fsync = 1; + error = git_buf_joinpath(&path, prefix, suff); if (error < 0) goto cleanup; @@ -151,12 +155,23 @@ cleanup: if (fd != -1) p_close(fd); + if (git_buf_len(&tmp_path) > 0) + p_unlink(git_buf_cstr(&tmp_path)); + + if (idx->pack != NULL) + p_unlink(idx->pack->pack_name); + git_buf_free(&path); git_buf_free(&tmp_path); git__free(idx); return -1; } +void git_indexer__set_fsync(git_indexer *idx, int do_fsync) +{ + idx->do_fsync = !!do_fsync; +} + /* Try to store the delta so we can try to resolve it later */ static int store_delta(git_indexer *idx) { @@ -288,7 +303,7 @@ static int store_object(git_indexer *idx) git_oid_cpy(&pentry->sha1, &oid); pentry->offset = entry_start; - k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error); + k = git_oidmap_put(idx->pack->idx_cache, &pentry->sha1, &error); if (error == -1) { git__free(pentry); giterr_set_oom(); @@ -302,7 +317,7 @@ static int store_object(git_indexer *idx) } - kh_value(idx->pack->idx_cache, k) = pentry; + git_oidmap_set_value_at(idx->pack->idx_cache, k, pentry); git_oid_cpy(&entry->oid, &oid); @@ -327,9 +342,7 @@ on_error: GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id) { - khiter_t k; - k = kh_get(oid, idx->pack->idx_cache, id); - return (k != kh_end(idx->pack->idx_cache)); + return git_oidmap_exists(idx->pack->idx_cache, id); } static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start) @@ -345,14 +358,14 @@ static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_ent } pentry->offset = entry_start; - k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error); + k = git_oidmap_put(idx->pack->idx_cache, &pentry->sha1, &error); if (error <= 0) { giterr_set(GITERR_INDEXER, "cannot insert object into pack"); return -1; } - kh_value(idx->pack->idx_cache, k) = pentry; + git_oidmap_set_value_at(idx->pack->idx_cache, k, pentry); /* Add the object to the list */ if (git_vector_insert(&idx->objects, entry) < 0) @@ -376,7 +389,7 @@ static int hash_and_save(git_indexer *idx, git_rawobj *obj, git_off_t entry_star GITERR_CHECK_ALLOC(entry); if (git_odb__hashobj(&oid, obj) < 0) { - giterr_set(GITERR_INDEXER, "Failed to hash object"); + giterr_set(GITERR_INDEXER, "failed to hash object"); goto on_error; } @@ -477,13 +490,29 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t static int append_to_pack(git_indexer *idx, const void *data, size_t size) { + git_off_t new_size; + size_t mmap_alignment; + size_t page_offset; + git_off_t page_start; git_off_t current_size = idx->pack->mwf.size; int fd = idx->pack->mwf.fd; + int error; if (!size) return 0; - if (p_lseek(fd, current_size + size - 1, SEEK_SET) < 0 || + if ((error = git__mmap_alignment(&mmap_alignment)) < 0) + return error; + + /* Write a single byte to force the file system to allocate space now or + * report an error, since we can't report errors when writing using mmap. + * Round the size up to the nearest page so that we only need to perform file + * I/O when we add a page, instead of whenever we write even a single byte. */ + new_size = current_size + size; + page_offset = new_size % mmap_alignment; + page_start = new_size - page_offset; + + if (p_lseek(fd, page_start + mmap_alignment - 1, SEEK_SET) < 0 || p_write(idx->pack->mwf.fd, data, 1) < 0) { giterr_set(GITERR_OS, "cannot extend packfile '%s'", idx->pack->pack_name); return -1; @@ -909,7 +938,6 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) git_buf filename = GIT_BUF_INIT; struct entry *entry; git_oid trailer_hash, file_hash; - git_hash_ctx ctx; git_filebuf index_file = {0}; void *packfile_trailer; @@ -918,9 +946,6 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) return -1; } - if (git_hash_ctx_init(&ctx) < 0) - return -1; - /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off + 20 < idx->pack->mwf.size) { giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack"); @@ -964,6 +989,10 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) git_vector_sort(&idx->objects); + /* Use the trailer hash as the pack file name to ensure + * files with different contents have different names */ + git_oid_cpy(&idx->hash, &trailer_hash); + git_buf_sets(&filename, idx->pack->pack_name); git_buf_shorten(&filename, strlen("pack")); git_buf_puts(&filename, "idx"); @@ -971,7 +1000,9 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) return -1; if (git_filebuf_open(&index_file, filename.ptr, - GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0) + GIT_FILEBUF_HASH_CONTENTS | + (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0), + idx->mode) < 0) goto on_error; /* Write out the header */ @@ -988,9 +1019,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) /* Write out the object names (SHA-1 hashes) */ git_vector_foreach(&idx->objects, i, entry) { git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid)); - git_hash_update(&ctx, &entry->oid, GIT_OID_RAWSZ); } - git_hash_final(&idx->hash, &ctx); /* Write out the CRC32 values */ git_vector_foreach(&idx->objects, i, entry) { @@ -1041,6 +1070,18 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) goto on_error; git_mwindow_free_all(&idx->pack->mwf); + + /* Truncate file to undo rounding up to next page_size in append_to_pack */ + if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) { + giterr_set(GITERR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name); + return -1; + } + + if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) { + giterr_set(GITERR_OS, "failed to fsync packfile"); + goto on_error; + } + /* We need to close the descriptor here so Windows doesn't choke on commit_at */ if (p_close(idx->pack->mwf.fd) < 0) { giterr_set(GITERR_OS, "failed to close packfile"); @@ -1053,17 +1094,23 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) goto on_error; /* And don't forget to rename the packfile to its new place. */ - p_rename(idx->pack->pack_name, git_buf_cstr(&filename)); + if (p_rename(idx->pack->pack_name, git_buf_cstr(&filename)) < 0) + goto on_error; + + /* And fsync the parent directory if we're asked to. */ + if (idx->do_fsync && + git_futils_fsync_parent(git_buf_cstr(&filename)) < 0) + goto on_error; + + idx->pack_committed = 1; git_buf_free(&filename); - git_hash_ctx_cleanup(&ctx); return 0; on_error: git_mwindow_free_all(&idx->pack->mwf); git_filebuf_cleanup(&index_file); git_buf_free(&filename); - git_hash_ctx_cleanup(&ctx); return -1; } @@ -1074,10 +1121,11 @@ void git_indexer_free(git_indexer *idx) git_vector_free_deep(&idx->objects); - if (idx->pack && idx->pack->idx_cache) { + if (idx->pack->idx_cache) { struct git_pack_entry *pentry; - kh_foreach_value( - idx->pack->idx_cache, pentry, { git__free(pentry); }); + git_oidmap_foreach_value(idx->pack->idx_cache, pentry, { + git__free(pentry); + }); git_oidmap_free(idx->pack->idx_cache); } @@ -1085,6 +1133,9 @@ void git_indexer_free(git_indexer *idx) git_vector_free_deep(&idx->deltas); if (!git_mutex_lock(&git__mwindow_mutex)) { + if (!idx->pack_committed) + git_packfile_close(idx->pack, true); + git_packfile_free(idx->pack); git_mutex_unlock(&git__mwindow_mutex); } diff --git a/src/indexer.h b/src/indexer.h new file mode 100644 index 000000000..8ee6115a6 --- /dev/null +++ b/src/indexer.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_indexer_h__ +#define INCLUDE_indexer_h__ + +#include "common.h" + +#include "git2/indexer.h" + +extern void git_indexer__set_fsync(git_indexer *idx, int do_fsync); + +#endif diff --git a/src/integer.h b/src/integer.h index b08094c2f..61712cebf 100644 --- a/src/integer.h +++ b/src/integer.h @@ -55,16 +55,16 @@ GIT_INLINE(bool) git__add_uint64_overflow(uint64_t *out, uint64_t one, uint64_t } /* Use clang/gcc compiler intrinsics whenever possible */ -#if (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow) -# define git__add_sizet_overflow(out, one, two) \ - __builtin_uadd_overflow(one, two, out) -# define git__multiply_sizet_overflow(out, one, two) \ - __builtin_umul_overflow(one, two, out) -#elif (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow) +#if (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow) # define git__add_sizet_overflow(out, one, two) \ __builtin_uaddl_overflow(one, two, out) # define git__multiply_sizet_overflow(out, one, two) \ __builtin_umull_overflow(one, two, out) +#elif (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow) +# define git__add_sizet_overflow(out, one, two) \ + __builtin_uadd_overflow(one, two, out) +# define git__multiply_sizet_overflow(out, one, two) \ + __builtin_umul_overflow(one, two, out) #else /** diff --git a/src/iterator.c b/src/iterator.c index 598c69c83..960031233 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -6,6 +6,7 @@ */ #include "iterator.h" + #include "tree.h" #include "index.h" @@ -1311,7 +1312,7 @@ static int filesystem_iterator_frame_push( if (iter->frames.size == FILESYSTEM_MAX_DEPTH) { giterr_set(GITERR_REPOSITORY, - "directory nesting too deep (%d)", iter->frames.size); + "directory nesting too deep (%"PRIuZ")", iter->frames.size); return -1; } diff --git a/src/iterator.h b/src/iterator.h index 0b239a5bd..0bcb128d9 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -8,6 +8,7 @@ #define INCLUDE_iterator_h__ #include "common.h" + #include "git2/index.h" #include "vector.h" #include "buffer.h" diff --git a/src/merge.c b/src/merge.c index 6934aa731..72cfa464d 100644 --- a/src/merge.c +++ b/src/merge.c @@ -5,13 +5,13 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "merge.h" + #include "posix.h" #include "buffer.h" #include "repository.h" #include "revwalk.h" #include "commit_list.h" -#include "merge.h" #include "path.h" #include "refs.h" #include "object.h" @@ -32,6 +32,8 @@ #include "commit.h" #include "oidarray.h" #include "merge_driver.h" +#include "oidmap.h" +#include "array.h" #include "git2/types.h" #include "git2/repository.h" @@ -78,7 +80,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito unsigned int i; if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length); + giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor"); return -1; } @@ -104,7 +106,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito goto on_error; if (!result) { - giterr_set(GITERR_MERGE, "No merge base found"); + giterr_set(GITERR_MERGE, "no merge base found"); error = GIT_ENOTFOUND; goto on_error; } @@ -184,7 +186,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co assert(out && repo && input_array); if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length); + giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor"); return -1; } @@ -230,7 +232,7 @@ static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_reposi if (!result) { git_revwalk_free(walk); - giterr_set(GITERR_MERGE, "No merge base found"); + giterr_set(GITERR_MERGE, "no merge base found"); return GIT_ENOTFOUND; } @@ -562,7 +564,7 @@ int git_repository_mergehead_foreach( assert(repo && cb); - if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository, + if ((error = git_buf_joinpath(&merge_head_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0) return error; @@ -574,7 +576,7 @@ int git_repository_mergehead_foreach( while ((line = git__strsep(&buffer, "\n")) != NULL) { if (strlen(line) != GIT_OID_HEXSZ) { - giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length"); + giterr_set(GITERR_INVALID, "unable to parse OID - invalid length"); error = -1; goto cleanup; } @@ -591,7 +593,7 @@ int git_repository_mergehead_foreach( } if (*buffer) { - giterr_set(GITERR_MERGE, "No EOL at line %d", line_num); + giterr_set(GITERR_MERGE, "no EOL at line %"PRIuZ, line_num); error = -1; goto cleanup; } @@ -1005,27 +1007,6 @@ struct merge_diff_similarity { size_t other_idx; }; -static int index_entry_similarity_exact( - git_repository *repo, - git_index_entry *a, - size_t a_idx, - git_index_entry *b, - size_t b_idx, - void **cache, - const git_merge_options *opts) -{ - GIT_UNUSED(repo); - GIT_UNUSED(a_idx); - GIT_UNUSED(b_idx); - GIT_UNUSED(cache); - GIT_UNUSED(opts); - - if (git_oid__cmp(&a->id, &b->id) == 0) - return 100; - - return 0; -} - static int index_entry_similarity_calc( void **out, git_repository *repo, @@ -1075,7 +1056,7 @@ static int index_entry_similarity_inexact( int score = 0; int error = 0; - if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode)) + if (!GIT_MODE_ISBLOB(a->mode) || !GIT_MODE_ISBLOB(b->mode)) return 0; /* update signature cache if needed */ @@ -1102,12 +1083,154 @@ static int index_entry_similarity_inexact( return score; } -static int merge_diff_mark_similarity( +/* Tracks deletes by oid for merge_diff_mark_similarity_exact(). This is a +* non-shrinking queue where next_pos is the next position to dequeue. +*/ +typedef struct { + git_array_t(size_t) arr; + size_t next_pos; + size_t first_entry; +} deletes_by_oid_queue; + +static void deletes_by_oid_free(git_oidmap *map) { + deletes_by_oid_queue *queue; + + if (!map) + return; + + git_oidmap_foreach_value(map, queue, { + git_array_clear(queue->arr); + }); + git_oidmap_free(map); +} + +static int deletes_by_oid_enqueue(git_oidmap *map, git_pool* pool, const git_oid *id, size_t idx) { + khint_t pos; + deletes_by_oid_queue *queue; + size_t *array_entry; + int error; + + pos = git_oidmap_lookup_index(map, id); + if (!git_oidmap_valid_index(map, pos)) { + queue = git_pool_malloc(pool, sizeof(deletes_by_oid_queue)); + GITERR_CHECK_ALLOC(queue); + + git_array_init(queue->arr); + queue->next_pos = 0; + queue->first_entry = idx; + + git_oidmap_insert(map, id, queue, &error); + if (error < 0) + return -1; + } else { + queue = git_oidmap_value_at(map, pos); + array_entry = git_array_alloc(queue->arr); + GITERR_CHECK_ALLOC(array_entry); + *array_entry = idx; + } + + return 0; +} + +static int deletes_by_oid_dequeue(size_t *idx, git_oidmap *map, const git_oid *id) { + khint_t pos; + deletes_by_oid_queue *queue; + size_t *array_entry; + + pos = git_oidmap_lookup_index(map, id); + + if (!git_oidmap_valid_index(map, pos)) + return GIT_ENOTFOUND; + + queue = git_oidmap_value_at(map, pos); + + if (queue->next_pos == 0) { + *idx = queue->first_entry; + } else { + array_entry = git_array_get(queue->arr, queue->next_pos - 1); + if (array_entry == NULL) + return GIT_ENOTFOUND; + + *idx = *array_entry; + } + + queue->next_pos++; + return 0; +} + +static int merge_diff_mark_similarity_exact( + git_merge_diff_list *diff_list, + struct merge_diff_similarity *similarity_ours, + struct merge_diff_similarity *similarity_theirs) +{ + size_t i, j; + git_merge_diff *conflict_src, *conflict_tgt; + git_oidmap *ours_deletes_by_oid = NULL, *theirs_deletes_by_oid = NULL; + int error = 0; + + if (!(ours_deletes_by_oid = git_oidmap_alloc()) || + !(theirs_deletes_by_oid = git_oidmap_alloc())) { + error = -1; + goto done; + } + + /* Build a map of object ids to conflicts */ + git_vector_foreach(&diff_list->conflicts, i, conflict_src) { + /* Items can be the source of a rename iff they have an item in the + * ancestor slot and lack an item in the ours or theirs slot. */ + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry)) + continue; + + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) { + error = deletes_by_oid_enqueue(ours_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i); + if (error < 0) + goto done; + } + + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) { + error = deletes_by_oid_enqueue(theirs_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i); + if (error < 0) + goto done; + } + } + + git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) { + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry)) + continue; + + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry)) { + if (deletes_by_oid_dequeue(&i, ours_deletes_by_oid, &conflict_tgt->our_entry.id) == 0) { + similarity_ours[i].similarity = 100; + similarity_ours[i].other_idx = j; + + similarity_ours[j].similarity = 100; + similarity_ours[j].other_idx = i; + } + } + + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry)) { + if (deletes_by_oid_dequeue(&i, theirs_deletes_by_oid, &conflict_tgt->their_entry.id) == 0) { + similarity_theirs[i].similarity = 100; + similarity_theirs[i].other_idx = j; + + similarity_theirs[j].similarity = 100; + similarity_theirs[j].other_idx = i; + } + } + } + +done: + deletes_by_oid_free(ours_deletes_by_oid); + deletes_by_oid_free(theirs_deletes_by_oid); + + return error; +} + +static int merge_diff_mark_similarity_inexact( git_repository *repo, git_merge_diff_list *diff_list, struct merge_diff_similarity *similarity_ours, struct merge_diff_similarity *similarity_theirs, - int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_options *), void **cache, const git_merge_options *opts) { @@ -1132,7 +1255,7 @@ static int merge_diff_mark_similarity( if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) { - similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts); + similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts); if (similarity == GIT_EBUFS) continue; @@ -1158,7 +1281,7 @@ static int merge_diff_mark_similarity( if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) { - similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts); + similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts); if (similarity > similarity_theirs[i].similarity && similarity > similarity_theirs[j].similarity) { @@ -1396,11 +1519,10 @@ int git_merge_diff_list__find_renames( /* Calculate similarity between items that were deleted from the ancestor * and added in the other branch. */ - if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours, - similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0) + if ((error = merge_diff_mark_similarity_exact(diff_list, similarity_ours, similarity_theirs)) < 0) goto done; - if (diff_list->conflicts.length <= opts->target_limit) { + if (opts->rename_threshold < 100 && diff_list->conflicts.length <= opts->target_limit) { GITERR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3); cache = git__calloc(cache_size, sizeof(void *)); GITERR_CHECK_ALLOC(cache); @@ -1410,9 +1532,8 @@ int git_merge_diff_list__find_renames( if (src_count > opts->target_limit || tgt_count > opts->target_limit) { /* TODO: report! */ } else { - if ((error = merge_diff_mark_similarity( - repo, diff_list, similarity_ours, similarity_theirs, - index_entry_similarity_inexact, cache, opts)) < 0) + if ((error = merge_diff_mark_similarity_inexact( + repo, diff_list, similarity_ours, similarity_theirs, cache, opts)) < 0) goto done; } } @@ -1713,15 +1834,15 @@ static int merge_normalize_opts( if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - if (given != NULL) + if (given != NULL) { memcpy(opts, given, sizeof(git_merge_options)); - else { + } else { git_merge_options init = GIT_MERGE_OPTIONS_INIT; memcpy(opts, &init, sizeof(init)); + } - opts->flags = GIT_MERGE_FIND_RENAMES; + if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold) opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD; - } if (given && given->default_driver) { opts->default_driver = git__strdup(given->default_driver); @@ -2018,6 +2139,26 @@ int git_merge_trees( git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; + assert(out && repo); + + /* if one side is treesame to the ancestor, take the other side */ + if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) { + const git_tree *result = NULL; + const git_oid *ancestor_tree_id = git_tree_id(ancestor_tree); + + if (our_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(our_tree))) + result = their_tree; + else if (their_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(their_tree))) + result = our_tree; + + if (result) { + if ((error = git_index_new(out)) == 0) + error = git_index_read_tree(*out, result); + + return error; + } + } + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree( @@ -2277,7 +2418,7 @@ static int write_merge_head( assert(repo && heads); - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; @@ -2305,7 +2446,7 @@ static int write_merge_mode(git_repository *repo) assert(repo); - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; @@ -2536,7 +2677,7 @@ static int write_merge_msg( for (i = 0; i < heads_len; i++) entries[i].merge_head = heads[i]; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0 || (error = git_filebuf_write(&file, "Merge ", 6)) < 0) goto cleanup; @@ -2914,7 +3055,7 @@ int git_merge__append_conflicts_to_merge_msg( if (!git_index_has_conflicts(index)) return 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; @@ -3043,7 +3184,7 @@ int git_merge_analysis( assert(analysis_out && preference_out && repo && their_heads); if (their_heads_len != 1) { - giterr_set(GITERR_MERGE, "Can only merge a single branch"); + giterr_set(GITERR_MERGE, "can only merge a single branch"); error = -1; goto done; } @@ -3099,7 +3240,7 @@ int git_merge( assert(repo && their_heads); if (their_heads_len != 1) { - giterr_set(GITERR_MERGE, "Can only merge a single branch"); + giterr_set(GITERR_MERGE, "can only merge a single branch"); return -1; } diff --git a/src/merge.h b/src/merge.h index f8cac161f..173a1b435 100644 --- a/src/merge.h +++ b/src/merge.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_merge_h__ #define INCLUDE_merge_h__ +#include "common.h" + #include "vector.h" #include "commit_list.h" #include "pool.h" diff --git a/src/merge_driver.c b/src/merge_driver.c index 88a53ecdb..ea4bd2787 100644 --- a/src/merge_driver.c +++ b/src/merge_driver.c @@ -5,11 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "merge_driver.h" + #include "vector.h" #include "global.h" #include "merge.h" -#include "merge_driver.h" #include "git2/merge.h" #include "git2/sys/merge.h" @@ -32,6 +32,35 @@ static struct merge_driver_registry merge_driver_registry; static void git_merge_driver_global_shutdown(void); +const git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src) +{ + assert(src); + return src->repo; +} + +const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src) +{ + assert(src); + return src->ancestor; +} + +const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src) +{ + assert(src); + return src->ours; +} + +const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src) +{ + assert(src); + return src->theirs; +} + +const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src) +{ + assert(src); + return src->file_opts; +} int git_merge_driver__builtin_apply( git_merge_driver *self, diff --git a/src/merge_driver.h b/src/merge_driver.h index bde27502c..6b7da5287 100644 --- a/src/merge_driver.h +++ b/src/merge_driver.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_merge_driver_h__ #define INCLUDE_merge_driver_h__ +#include "common.h" + #include "git2/merge.h" #include "git2/index.h" #include "git2/sys/merge.h" diff --git a/src/merge_file.c b/src/merge_file.c index 3f14a4f63..a54d6bd57 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "repository.h" #include "posix.h" #include "fileops.h" @@ -127,7 +128,7 @@ static int merge_file__xdiff( if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile, &their_mmfile, &xmparam, &mmbuffer)) < 0) { - giterr_set(GITERR_MERGE, "Failed to merge files."); + giterr_set(GITERR_MERGE, "failed to merge files"); error = -1; goto done; } diff --git a/src/message.h b/src/message.h index 3c4b8dc45..88fc7884c 100644 --- a/src/message.h +++ b/src/message.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_message_h__ #define INCLUDE_message_h__ +#include "common.h" + #include "git2/message.h" #include "buffer.h" diff --git a/src/mwindow.c b/src/mwindow.c index 8a5b5caee..38d0352b4 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "mwindow.h" + #include "vector.h" #include "fileops.h" #include "map.h" @@ -14,8 +14,6 @@ #include "strmap.h" #include "pack.h" -GIT__USE_STRMAP - #define DEFAULT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ ? 1 * 1024 * 1024 * 1024 \ @@ -84,7 +82,7 @@ int git_mwindow_get_pack(struct git_pack_file **out, const char *path) git_atomic_inc(&pack->refcount); - git_strmap_insert(git__pack_cache, pack->pack_name, pack, error); + git_strmap_insert(git__pack_cache, pack->pack_name, pack, &error); git_mutex_unlock(&git__mwindow_mutex); if (error < 0) { @@ -231,7 +229,7 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf) } if (!lru_w) { - giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU"); + giterr_set(GITERR_OS, "failed to close memory window; couldn't find LRU"); return -1; } diff --git a/src/mwindow.h b/src/mwindow.h index bdde9e0a4..ea962d1b6 100644 --- a/src/mwindow.h +++ b/src/mwindow.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_mwindow__ #define INCLUDE_mwindow__ +#include "common.h" + #include "map.h" #include "vector.h" diff --git a/src/netops.c b/src/netops.c index 90326ea59..68f404d2c 100644 --- a/src/netops.c +++ b/src/netops.c @@ -5,11 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "netops.h" + #include <ctype.h> #include "git2/errors.h" -#include "common.h" -#include "netops.h" #include "posix.h" #include "buffer.h" #include "http_parser.h" @@ -144,7 +144,7 @@ int gitno_connection_data_from_url( default_port = "80"; if (data->use_ssl) { - giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed"); + giterr_set(GITERR_NET, "redirect from HTTPS to HTTP is not allowed"); goto cleanup; } } else if (!git__prefixcmp(url, prefix_https)) { @@ -155,7 +155,7 @@ int gitno_connection_data_from_url( default_port = data->use_ssl ? "443" : "80"; if (!default_port) { - giterr_set(GITERR_NET, "Unrecognized URL prefix"); + giterr_set(GITERR_NET, "unrecognized URL prefix"); goto cleanup; } @@ -187,7 +187,7 @@ int gitno_connection_data_from_url( /* Check for errors in the resulting data */ if (original_host && url[0] != '/' && strcmp(original_host, data->host)) { - giterr_set(GITERR_NET, "Cross host redirect not allowed"); + giterr_set(GITERR_NET, "cross host redirect not allowed"); error = -1; } } @@ -237,7 +237,7 @@ int gitno_extract_url_parts( const char *_host, *_port, *_path, *_userinfo; if (http_parser_parse_url(url, strlen(url), false, &u)) { - giterr_set(GITERR_NET, "Malformed URL '%s'", url); + giterr_set(GITERR_NET, "malformed URL '%s'", url); return GIT_EINVALIDSPEC; } diff --git a/src/netops.h b/src/netops.h index b7170a0f2..75fd9a512 100644 --- a/src/netops.h +++ b/src/netops.h @@ -7,8 +7,9 @@ #ifndef INCLUDE_netops_h__ #define INCLUDE_netops_h__ -#include "posix.h" #include "common.h" + +#include "posix.h" #include "stream.h" #ifdef GIT_OPENSSL diff --git a/src/notes.c b/src/notes.c index fe8d2164f..75108b9c9 100644 --- a/src/notes.c +++ b/src/notes.c @@ -15,7 +15,7 @@ static int note_error_notfound(void) { - giterr_set(GITERR_INVALID, "Note could not be found"); + giterr_set(GITERR_INVALID, "note could not be found"); return GIT_ENOTFOUND; } @@ -226,7 +226,7 @@ static int remove_note_in_tree_enotfound_cb( GIT_UNUSED(note_oid); GIT_UNUSED(fanout); - giterr_set(GITERR_REPOSITORY, "Object '%s' has no note", annotated_object_sha); + giterr_set(GITERR_REPOSITORY, "object '%s' has no note", annotated_object_sha); return current_error; } @@ -244,7 +244,7 @@ static int insert_note_in_tree_eexists_cb(git_tree **out, GIT_UNUSED(note_oid); GIT_UNUSED(fanout); - giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", annotated_object_sha); + giterr_set(GITERR_REPOSITORY, "note for '%s' exists already", annotated_object_sha); return current_error; } diff --git a/src/object.c b/src/object.c index 1d45f9f1b..4d069a34c 100644 --- a/src/object.c +++ b/src/object.c @@ -4,9 +4,11 @@ * 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 "object.h" + #include "git2/object.h" -#include "common.h" #include "repository.h" #include "commit.h" @@ -66,12 +68,12 @@ int git_object__from_odb_object( /* Validate type match */ if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) { giterr_set(GITERR_INVALID, - "The requested type does not match the type in the ODB"); + "the requested type does not match the type in the ODB"); return GIT_ENOTFOUND; } if ((object_size = git_object__size(odb_obj->cached.type)) == 0) { - giterr_set(GITERR_INVALID, "The requested type is invalid"); + giterr_set(GITERR_INVALID, "the requested type is invalid"); return GIT_ENOTFOUND; } @@ -122,7 +124,7 @@ int git_object_lookup_prefix( assert(repo && object_out && id); if (len < GIT_OID_MINPREFIXLEN) { - giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short"); + giterr_set(GITERR_OBJECT, "ambiguous lookup - OID prefix is too short"); return GIT_EAMBIGUOUS; } @@ -147,7 +149,7 @@ int git_object_lookup_prefix( if (type != GIT_OBJ_ANY && type != object->cached.type) { git_object_free(object); giterr_set(GITERR_INVALID, - "The requested type does not match the type in ODB"); + "the requested type does not match the type in ODB"); return GIT_ENOTFOUND; } @@ -292,7 +294,7 @@ static int peel_error(int error, const git_oid *oid, git_otype type) git_oid_fmt(hex_oid, oid); hex_oid[GIT_OID_HEXSZ] = '\0'; - giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be " + giterr_set(GITERR_OBJECT, "the git_object of id '%s' can not be " "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type); return error; diff --git a/src/object.h b/src/object.h index dd227d16d..ff61c1d33 100644 --- a/src/object.h +++ b/src/object.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_object_h__ #define INCLUDE_object_h__ +#include "common.h" + #include "repository.h" extern bool git_object__strict_input_validation; diff --git a/src/object_api.c b/src/object_api.c index e0d8760e7..75efa4d10 100644 --- a/src/object_api.c +++ b/src/object_api.c @@ -4,11 +4,12 @@ * 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 "git2/object.h" #include "common.h" -#include "repository.h" +#include "git2/object.h" + +#include "repository.h" #include "commit.h" #include "tree.h" #include "blob.h" @@ -5,13 +5,13 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "odb.h" + #include <zlib.h> #include "git2/object.h" #include "git2/sys/odb_backend.h" #include "fileops.h" #include "hash.h" -#include "odb.h" #include "delta.h" #include "filter.h" #include "repository.h" @@ -31,6 +31,8 @@ #define GIT_ALTERNATES_MAX_DEPTH 5 +bool git_odb__strict_hash_verification = true; + typedef struct { git_odb_backend *backend; @@ -176,7 +178,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) int error = 0; if (!git_object_typeisloose(type)) { - giterr_set(GITERR_INVALID, "Invalid object type for hash"); + giterr_set(GITERR_INVALID, "invalid object type for hash"); return -1; } @@ -199,7 +201,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) * If size is not zero, the file was truncated after we originally * stat'd it, so we consider this a read failure too */ if (read_len < 0 || size > 0) { - giterr_set(GITERR_OS, "Error reading file for hashing"); + giterr_set(GITERR_OS, "error reading file for hashing"); error = -1; goto done; @@ -251,7 +253,7 @@ int git_odb__hashlink(git_oid *out, const char *path) return -1; if (!git__is_int(st.st_size) || (int)st.st_size < 0) { - giterr_set(GITERR_FILESYSTEM, "File size overflow for 32-bit systems"); + giterr_set(GITERR_FILESYSTEM, "file size overflow for 32-bit systems"); return -1; } @@ -269,7 +271,7 @@ int git_odb__hashlink(git_oid *out, const char *path) read_len = p_readlink(path, link_data, size); link_data[size] = '\0'; if (read_len != size) { - giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path); + giterr_set(GITERR_OS, "failed to read symlink data for '%s'", path); git__free(link_data); return -1; } @@ -295,7 +297,7 @@ int git_odb_hashfile(git_oid *out, const char *path, git_otype type) return fd; if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { - giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); + giterr_set(GITERR_OS, "file size overflow for 32-bit systems"); p_close(fd); return -1; } @@ -475,7 +477,7 @@ size_t git_odb_num_backends(git_odb *odb) static int git_odb__error_unsupported_in_backend(const char *action) { giterr_set(GITERR_ODB, - "Cannot %s - unsupported in the loaded odb backends", action); + "cannot %s - unsupported in the loaded odb backends", action); return -1; } @@ -492,11 +494,11 @@ int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) return 0; } - giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos); + giterr_set(GITERR_ODB, "no ODB backend loaded at index %" PRIuZ, pos); return GIT_ENOTFOUND; } -static int add_default_backends( +int git_odb__add_default_backends( git_odb *db, const char *objects_dir, bool as_alternates, int alternate_depth) { @@ -517,7 +519,7 @@ static int add_default_backends( if (as_alternates) return 0; - giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); + giterr_set(GITERR_ODB, "failed to load object database in '%s'", objects_dir); return -1; } @@ -531,7 +533,7 @@ static int add_default_backends( #endif /* add the loose object backend */ - if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 || + 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) return -1; @@ -586,7 +588,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ alternate = git_buf_cstr(&alternates_path); } - if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) + if ((result = git_odb__add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) break; } @@ -598,7 +600,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int git_odb_add_disk_alternate(git_odb *odb, const char *path) { - return add_default_backends(odb, path, true, 0); + return git_odb__add_default_backends(odb, path, true, 0); } int git_odb_open(git_odb **out, const char *objects_dir) @@ -612,7 +614,7 @@ int git_odb_open(git_odb **out, const char *objects_dir) if (git_odb_new(&db) < 0) return -1; - if (add_default_backends(db, objects_dir, 0, 0) < 0) { + if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) { git_odb_free(db); return -1; } @@ -621,6 +623,24 @@ int git_odb_open(git_odb **out, const char *objects_dir) return 0; } +int git_odb__set_caps(git_odb *odb, int caps) +{ + if (caps == GIT_ODB_CAP_FROM_OWNER) { + git_repository *repo = odb->rc.owner; + int val; + + if (!repo) { + giterr_set(GITERR_ODB, "cannot access repository to set odb caps"); + return -1; + } + + if (!git_repository__cvar(&val, repo, GIT_CVAR_FSYNCOBJECTFILES)) + odb->do_fsync = !!val; + } + + return 0; +} + static void odb_free(git_odb *db) { size_t i; @@ -695,7 +715,7 @@ static int odb_freshen_1( return (int)found; } -static int odb_freshen(git_odb *db, const git_oid *id) +int git_odb__freshen(git_odb *db, const git_oid *id) { assert(db && id); @@ -980,7 +1000,9 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, size_t i; git_rawobj raw; git_odb_object *object; + git_oid hashed; bool found = false; + int error = 0; if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0) found = true; @@ -993,7 +1015,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, continue; if (b->read != NULL) { - int error = b->read(&raw.data, &raw.len, &raw.type, b, id); + error = b->read(&raw.data, &raw.len, &raw.type, b, id); if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) continue; @@ -1007,12 +1029,26 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(id, &hashed)) { + error = git_odb__error_mismatch(id, &hashed); + goto out; + } + } + giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + return error; } int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) @@ -1063,9 +1099,9 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, const git_oid *key, size_t len, bool only_refreshed) { size_t i; - int error = GIT_ENOTFOUND; + int error = 0; git_oid found_full_oid = {{0}}; - git_rawobj raw; + git_rawobj raw = {0}; void *data = NULL; bool found = false; git_odb_object *object; @@ -1080,18 +1116,29 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (b->read_prefix != NULL) { git_oid full_oid; error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len); - if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) + + if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) { + error = 0; continue; + } if (error) - return error; + goto out; git__free(data); data = raw.data; if (found && git_oid__cmp(&full_oid, &found_full_oid)) { - git__free(raw.data); - return git_odb__error_ambiguous("multiple matches for prefix"); + git_buf buf = GIT_BUF_INIT; + + git_buf_printf(&buf, "multiple matches for prefix: %s", + git_oid_tostr_s(&full_oid)); + git_buf_printf(&buf, " %s", + git_oid_tostr_s(&found_full_oid)); + + error = git_odb__error_ambiguous(buf.ptr); + git_buf_free(&buf); + goto out; } found_full_oid = full_oid; @@ -1102,11 +1149,28 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + git_oid hash; + + if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(&found_full_oid, &hash)) { + error = git_odb__error_mismatch(&found_full_oid, &hash); + goto out; + } + } + if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + + return error; } int git_odb_read_prefix( @@ -1167,7 +1231,7 @@ int git_odb_write( assert(oid && db); git_odb_hash(oid, data, len, type); - if (odb_freshen(db, oid)) + if (git_odb__freshen(db, oid)) return 0; for (i = 0; i < db->backends.length && error < 0; ++i) { @@ -1264,10 +1328,10 @@ static int git_odb_stream__invalid_length( const char *action) { giterr_set(GITERR_ODB, - "Cannot %s - " - "Invalid length. %"PRIuZ" was expected. The " - "total size of the received chunks amounts to %"PRIuZ".", - action, stream->declared_size, stream->received_bytes); + "cannot %s - " + "Invalid length. %"PRIdZ" was expected. The " + "total size of the received chunks amounts to %"PRIdZ".", + action, stream->declared_size, stream->received_bytes); return -1; } @@ -1293,7 +1357,7 @@ int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream) git_hash_final(out, stream->hash_ctx); - if (odb_freshen(stream->backend->odb, out)) + if (git_odb__freshen(stream->backend->odb, out)) return 0; return stream->finalize_write(stream, out); @@ -1393,23 +1457,36 @@ int git_odb_refresh(struct git_odb *db) return 0; } +int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual) +{ + char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(expected_oid, sizeof(expected_oid), expected); + git_oid_tostr(actual_oid, sizeof(actual_oid), actual); + + giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s", + expected_oid, actual_oid); + + return GIT_EMISMATCH; +} + int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len) { if (oid != NULL) { char oid_str[GIT_OID_HEXSZ + 1]; git_oid_tostr(oid_str, oid_len+1, oid); - giterr_set(GITERR_ODB, "Object not found - %s (%.*s)", - message, oid_len, oid_str); + giterr_set(GITERR_ODB, "object not found - %s (%.*s)", + message, (int) oid_len, oid_str); } else - giterr_set(GITERR_ODB, "Object not found - %s", message); + giterr_set(GITERR_ODB, "object not found - %s", message); return GIT_ENOTFOUND; } int git_odb__error_ambiguous(const char *message) { - giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message); + giterr_set(GITERR_ODB, "ambiguous SHA1 prefix - %s", message); return GIT_EAMBIGUOUS; } @@ -7,6 +7,8 @@ #ifndef INCLUDE_odb_h__ #define INCLUDE_odb_h__ +#include "common.h" + #include "git2/odb.h" #include "git2/oid.h" #include "git2/types.h" @@ -20,6 +22,8 @@ #define GIT_OBJECT_DIR_MODE 0777 #define GIT_OBJECT_FILE_MODE 0444 +extern bool git_odb__strict_hash_verification; + /* DO NOT EXPORT */ typedef struct { void *data; /**< Raw, decompressed object data. */ @@ -38,8 +42,25 @@ struct git_odb { git_refcount rc; git_vector backends; git_cache own_cache; + unsigned int do_fsync :1; }; +typedef enum { + GIT_ODB_CAP_FROM_OWNER = -1, +} git_odb_cap_t; + +/* + * Set the capabilities for the object database. + */ +int git_odb__set_caps(git_odb *odb, int caps); + +/* + * Add the default loose and packed backends for a database. + */ +int git_odb__add_default_backends( + git_odb *db, const char *objects_dir, + bool as_alternates, int alternate_depth); + /* * Hash a git_rawobj internally. * The `git_rawobj` is supposed to be previously initialized @@ -79,6 +100,12 @@ int git_odb__hashfd_filtered( */ int git_odb__hashlink(git_oid *out, const char *path); +/** + * Generate a GIT_EMISMATCH error for the ODB. + */ +int git_odb__error_mismatch( + const git_oid *expected, const git_oid *actual); + /* * Generate a GIT_ENOTFOUND error for the ODB. */ @@ -98,6 +125,9 @@ int git_odb__read_header_or_object( git_odb_object **out, size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id); +/* freshen an entry in the object database */ +int git_odb__freshen(git_odb *db, const git_oid *id); + /* fully free the object; internal method, DO NOT EXPORT */ void git_odb_object__free(void *object); diff --git a/src/odb_loose.c b/src/odb_loose.c index f312b9c9c..72b47f091 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include <zlib.h> #include "git2/object.h" #include "git2/sys/odb_backend.h" @@ -14,6 +15,7 @@ #include "odb.h" #include "delta.h" #include "filebuf.h" +#include "object.h" #include "git2/odb_backend.h" #include "git2/types.h" @@ -204,6 +206,11 @@ static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len) return inflate(s, 0); } +static void abort_inflate(z_stream *s) +{ + inflateEnd(s); +} + static int finish_inflate(z_stream *s) { int status = Z_OK; @@ -214,7 +221,7 @@ static int finish_inflate(z_stream *s) inflateEnd(s); if ((status != Z_STREAM_END) || (s->avail_in != 0)) { - giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely"); + giterr_set(GITERR_ZLIB, "failed to finish zlib inflation; stream aborted prematurely"); return -1; } @@ -243,7 +250,7 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) zs.avail_in = (uInt)inlen; if (inflateInit(&zs) < Z_OK) { - giterr_set(GITERR_ZLIB, "Failed to inflate buffer"); + giterr_set(GITERR_ZLIB, "failed to inflate buffer"); return -1; } @@ -255,7 +262,7 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) if (status != Z_STREAM_END /* || zs.avail_in != 0 */ || zs.total_out != outlen) { - giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely"); + giterr_set(GITERR_ZLIB, "failed to inflate buffer; stream aborted prematurely"); return -1; } @@ -319,7 +326,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj) */ if ((used = get_binary_object_header(&hdr, obj)) == 0 || !git_object_typeisloose(hdr.type)) { - giterr_set(GITERR_ODB, "Failed to inflate loose object."); + giterr_set(GITERR_ODB, "failed to inflate loose object"); return -1; } @@ -366,7 +373,8 @@ static int inflate_disk_obj(git_rawobj *out, git_buf *obj) (used = get_object_header(&hdr, head)) == 0 || !git_object_typeisloose(hdr.type)) { - giterr_set(GITERR_ODB, "Failed to inflate disk object."); + abort_inflate(&zs); + giterr_set(GITERR_ODB, "failed to inflate disk object"); return -1; } @@ -455,7 +463,7 @@ static int read_header_loose(git_rawobj *out, git_buf *loc) || get_object_header(&header_obj, inflated_buffer) == 0 || git_object_typeisloose(header_obj.type) == 0) { - giterr_set(GITERR_ZLIB, "Failed to read loose object header"); + giterr_set(GITERR_ZLIB, "failed to read loose object header"); error = -1; } else { out->len = header_obj.size; @@ -838,6 +846,17 @@ static void loose_backend__stream_free(git_odb_stream *_stream) git__free(stream); } +static int filebuf_flags(loose_backend *backend) +{ + int flags = GIT_FILEBUF_TEMPORARY | + (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT); + + if (backend->fsync_object_files || git_repository__fsync_gitdir) + flags |= GIT_FILEBUF_FSYNC; + + return flags; +} + static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type) { loose_backend *backend; @@ -864,9 +883,7 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_ stream->stream.mode = GIT_STREAM_WRONLY; if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 || - git_filebuf_open(&stream->fbuf, tmp_path.ptr, - GIT_FILEBUF_TEMPORARY | - (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT), + git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend), backend->object_file_mode) < 0 || stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0) { @@ -894,9 +911,7 @@ static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, c header_len = git_odb__format_object_header(header, sizeof(header), len, type); if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 || - git_filebuf_open(&fbuf, final_path.ptr, - GIT_FILEBUF_TEMPORARY | - (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT), + git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend), backend->object_file_mode) < 0) { error = -1; diff --git a/src/odb_mempack.c b/src/odb_mempack.c index 594a2784c..0c05f0566 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "git2/object.h" #include "git2/sys/odb_backend.h" #include "fileops.h" @@ -18,13 +19,11 @@ #include "git2/types.h" #include "git2/pack.h" -GIT__USE_OIDMAP - struct memobject { git_oid oid; size_t len; git_otype type; - char data[]; + char data[GIT_FLEX_ARRAY]; }; struct memory_packer_db { @@ -41,7 +40,7 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void size_t alloc_len; int rval; - pos = kh_put(oid, db->objects, oid, &rval); + pos = git_oidmap_put(db->objects, oid, &rval); if (rval < 0) return -1; @@ -57,8 +56,8 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void obj->len = len; obj->type = type; - kh_key(db->objects, pos) = &obj->oid; - kh_val(db->objects, pos) = obj; + git_oidmap_set_key_at(db->objects, pos, &obj->oid); + git_oidmap_set_value_at(db->objects, pos, obj); if (type == GIT_OBJ_COMMIT) { struct memobject **store = git_array_alloc(db->commits); @@ -72,13 +71,8 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void static int impl__exists(git_odb_backend *backend, const git_oid *oid) { struct memory_packer_db *db = (struct memory_packer_db *)backend; - khiter_t pos; - pos = kh_get(oid, db->objects, oid); - if (pos != kh_end(db->objects)) - return 1; - - return 0; + return git_oidmap_exists(db->objects, oid); } static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) @@ -87,11 +81,11 @@ static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb struct memobject *obj = NULL; khiter_t pos; - pos = kh_get(oid, db->objects, oid); - if (pos == kh_end(db->objects)) + pos = git_oidmap_lookup_index(db->objects, oid); + if (!git_oidmap_valid_index(db->objects, pos)) return GIT_ENOTFOUND; - obj = kh_val(db->objects, pos); + obj = git_oidmap_value_at(db->objects, pos); *len_p = obj->len; *type_p = obj->type; @@ -108,11 +102,11 @@ static int impl__read_header(size_t *len_p, git_otype *type_p, git_odb_backend * struct memobject *obj = NULL; khiter_t pos; - pos = kh_get(oid, db->objects, oid); - if (pos == kh_end(db->objects)) + pos = git_oidmap_lookup_index(db->objects, oid); + if (!git_oidmap_valid_index(db->objects, pos)) return GIT_ENOTFOUND; - obj = kh_val(db->objects, pos); + obj = git_oidmap_value_at(db->objects, pos); *len_p = obj->len; *type_p = obj->type; @@ -149,7 +143,7 @@ void git_mempack_reset(git_odb_backend *_backend) struct memory_packer_db *db = (struct memory_packer_db *)_backend; struct memobject *object = NULL; - kh_foreach_value(db->objects, object, { + git_oidmap_foreach_value(db->objects, object, { git__free(object); }); @@ -177,6 +171,7 @@ int git_mempack_new(git_odb_backend **out) db->objects = git_oidmap_alloc(); + db->parent.version = GIT_ODB_BACKEND_VERSION; db->parent.read = &impl__read; db->parent.write = &impl__write; db->parent.read_header = &impl__read_header; diff --git a/src/odb_pack.c b/src/odb_pack.c index b80d0337a..20aff5386 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include <zlib.h> #include "git2/repository.h" #include "git2/indexer.h" @@ -428,7 +429,7 @@ static int pack_backend__read_prefix( git_oid_cpy(out_oid, short_oid); } else { struct git_pack_entry e; - git_rawobj raw; + git_rawobj raw = {NULL}; if ((error = pack_entry_find_prefix( &e, (struct pack_backend *)backend, short_oid, len)) == 0 && diff --git a/src/offmap.c b/src/offmap.c new file mode 100644 index 000000000..ab6649697 --- /dev/null +++ b/src/offmap.c @@ -0,0 +1,83 @@ +/* + * 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 "offmap.h" + +__KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal) + +git_offmap *git_offmap_alloc(void) +{ + return kh_init(off); +} + +void git_offmap_free(git_offmap *map) +{ + kh_destroy(off, map); +} + +void git_offmap_clear(git_offmap *map) +{ + kh_clear(off, map); +} + +size_t git_offmap_num_entries(git_offmap *map) +{ + return kh_size(map); +} + +size_t git_offmap_lookup_index(git_offmap *map, const git_off_t key) +{ + return kh_get(off, map, key); +} + +int git_offmap_valid_index(git_offmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_offmap_exists(git_offmap *map, const git_off_t key) +{ + return kh_get(off, map, key) != kh_end(map); +} + +void *git_offmap_value_at(git_offmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +void git_offmap_set_value_at(git_offmap *map, size_t idx, void *value) +{ + kh_val(map, idx) = value; +} + +void git_offmap_delete_at(git_offmap *map, size_t idx) +{ + kh_del(off, map, idx); +} + +int git_offmap_put(git_offmap *map, const git_off_t key, int *err) +{ + return kh_put(off, map, key, err); +} + +void git_offmap_insert(git_offmap *map, const git_off_t key, void *value, int *rval) +{ + khiter_t idx = kh_put(off, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_offmap_delete(git_offmap *map, const git_off_t key) +{ + khiter_t idx = git_offmap_lookup_index(map, key); + if (git_offmap_valid_index(map, idx)) + git_offmap_delete_at(map, idx); +} diff --git a/src/offmap.h b/src/offmap.h index 0d0e51272..0b0896b8f 100644 --- a/src/offmap.h +++ b/src/offmap.h @@ -8,6 +8,7 @@ #define INCLUDE_offmap_h__ #include "common.h" + #include "git2/types.h" #define kmalloc git__malloc @@ -20,45 +21,24 @@ __KHASH_TYPE(off, git_off_t, void *) typedef khash_t(off) git_offmap; -#define GIT__USE_OFFMAP \ - __KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal) - -#define git_offmap_alloc() kh_init(off) -#define git_offmap_free(h) kh_destroy(off, h), h = NULL -#define git_offmap_clear(h) kh_clear(off, h) - -#define git_offmap_num_entries(h) kh_size(h) - -#define git_offmap_lookup_index(h, k) kh_get(off, h, k) -#define git_offmap_valid_index(h, idx) (idx != kh_end(h)) +git_offmap *git_offmap_alloc(void); +void git_offmap_free(git_offmap *map); +void git_offmap_clear(git_offmap *map); -#define git_offmap_exists(h, k) (kh_get(off, h, k) != kh_end(h)) +size_t git_offmap_num_entries(git_offmap *map); -#define git_offmap_value_at(h, idx) kh_val(h, idx) -#define git_offmap_set_value_at(h, idx, v) kh_val(h, idx) = v -#define git_offmap_delete_at(h, idx) kh_del(off, h, idx) +size_t git_offmap_lookup_index(git_offmap *map, const git_off_t key); +int git_offmap_valid_index(git_offmap *map, size_t idx); -#define git_offmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(off, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) +int git_offmap_exists(git_offmap *map, const git_off_t key); -#define git_offmap_insert2(h, key, val, oldv, rval) do { \ - khiter_t __pos = kh_put(off, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) { \ - oldv = kh_val(h, __pos); \ - kh_key(h, __pos) = key; \ - } else { oldv = NULL; } \ - kh_val(h, __pos) = val; \ - } } while (0) +void *git_offmap_value_at(git_offmap *map, size_t idx); +void git_offmap_set_value_at(git_offmap *map, size_t idx, void *value); +void git_offmap_delete_at(git_offmap *map, size_t idx); -#define git_offmap_delete(h, key) do { \ - khiter_t __pos = git_offmap_lookup_index(h, key); \ - if (git_offmap_valid_index(h, __pos)) \ - git_offmap_delete_at(h, __pos); } while (0) +int git_offmap_put(git_offmap *map, const git_off_t key, int *err); +void git_offmap_insert(git_offmap *map, const git_off_t key, void *value, int *rval); +void git_offmap_delete(git_offmap *map, const git_off_t key); #define git_offmap_foreach kh_foreach #define git_offmap_foreach_value kh_foreach_value @@ -5,7 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "oid.h" + #include "git2/oid.h" #include "repository.h" #include "global.h" @@ -16,7 +17,7 @@ static char to_hex[] = "0123456789abcdef"; static int oid_error_invalid(const char *msg) { - giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg); + giterr_set(GITERR_INVALID, "unable to parse OID - %s", msg); return -1; } @@ -380,7 +381,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) node_index idx; if (os->full) { - giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full"); + giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full"); return -1; } @@ -395,7 +396,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) trie_node *node; if (c == -1) { - giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value"); + giterr_set(GITERR_INVALID, "unable to shorten OID - invalid hex value"); return -1; } @@ -410,7 +411,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]); if (node == NULL) { if (os->full) - giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full"); + giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full"); return -1; } } @@ -418,7 +419,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) if (node->children[c] == 0) { if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) { if (os->full) - giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full"); + giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full"); return -1; } break; @@ -7,6 +7,8 @@ #ifndef INCLUDE_oid_h__ #define INCLUDE_oid_h__ +#include "common.h" + #include "git2/oid.h" /** @@ -22,14 +24,7 @@ char *git_oid_allocfmt(const git_oid *id); GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2) { - int i; - - for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) { - if (*sha1 != *sha2) - return *sha1 - *sha2; - } - - return 0; + return memcmp(sha1, sha2, GIT_OID_RAWSZ); } /* diff --git a/src/oidarray.c b/src/oidarray.c index 1d51a2958..fda3b638d 100644 --- a/src/oidarray.c +++ b/src/oidarray.c @@ -5,8 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "git2/oidarray.h" #include "oidarray.h" + +#include "git2/oidarray.h" #include "array.h" void git_oidarray_free(git_oidarray *arr) diff --git a/src/oidarray.h b/src/oidarray.h index a7215ae6c..f051a0ec4 100644 --- a/src/oidarray.h +++ b/src/oidarray.h @@ -8,6 +8,7 @@ #define INCLUDE_oidarray_h__ #include "common.h" + #include "git2/oidarray.h" #include "array.h" diff --git a/src/oidmap.c b/src/oidmap.c new file mode 100644 index 000000000..5f156a18e --- /dev/null +++ b/src/oidmap.c @@ -0,0 +1,105 @@ +/* + * 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 "oidmap.h" + +GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) +{ + khint_t h; + memcpy(&h, oid, sizeof(khint_t)); + return h; +} + +__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal) + +git_oidmap *git_oidmap_alloc() +{ + return kh_init(oid); +} + +void git_oidmap_free(git_oidmap *map) +{ + kh_destroy(oid, map); +} + +void git_oidmap_clear(git_oidmap *map) +{ + kh_clear(oid, map); +} + +size_t git_oidmap_size(git_oidmap *map) +{ + return kh_size(map); +} + +size_t git_oidmap_lookup_index(git_oidmap *map, const git_oid *key) +{ + return kh_get(oid, map, key); +} + +int git_oidmap_valid_index(git_oidmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_oidmap_exists(git_oidmap *map, const git_oid *key) +{ + return kh_get(oid, map, key) != kh_end(map); +} + +int git_oidmap_has_data(git_oidmap *map, size_t idx) +{ + return kh_exist(map, idx); +} + +const git_oid *git_oidmap_key(git_oidmap *map, size_t idx) +{ + return kh_key(map, idx); +} + +void git_oidmap_set_key_at(git_oidmap *map, size_t idx, git_oid *key) +{ + kh_key(map, idx) = key; +} + +void *git_oidmap_value_at(git_oidmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +void git_oidmap_set_value_at(git_oidmap *map, size_t idx, void *value) +{ + kh_val(map, idx) = value; +} + +void git_oidmap_delete_at(git_oidmap *map, size_t idx) +{ + kh_del(oid, map, idx); +} + +int git_oidmap_put(git_oidmap *map, const git_oid *key, int *err) +{ + return kh_put(oid, map, key, err); +} + +void git_oidmap_insert(git_oidmap *map, const git_oid *key, void *value, int *rval) +{ + khiter_t idx = kh_put(oid, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_oidmap_delete(git_oidmap *map, const git_oid *key) +{ + khiter_t idx = git_oidmap_lookup_index(map, key); + if (git_oidmap_valid_index(map, idx)) + git_oidmap_delete_at(map, idx); +} diff --git a/src/oidmap.h b/src/oidmap.h index 2cf208f53..49f129e93 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -8,6 +8,7 @@ #define INCLUDE_oidmap_h__ #include "common.h" + #include "git2/oid.h" #define kmalloc git__malloc @@ -20,35 +21,31 @@ __KHASH_TYPE(oid, const git_oid *, void *) typedef khash_t(oid) git_oidmap; -GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) -{ - khint_t h; - memcpy(&h, oid, sizeof(khint_t)); - return h; -} +git_oidmap *git_oidmap_alloc(void); +void git_oidmap_free(git_oidmap *map); +void git_oidmap_clear(git_oidmap *map); -#define GIT__USE_OIDMAP \ - __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal) +size_t git_oidmap_size(git_oidmap *map); -#define git_oidmap_alloc() kh_init(oid) -#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL +size_t git_oidmap_lookup_index(git_oidmap *map, const git_oid *key); +int git_oidmap_valid_index(git_oidmap *map, size_t idx); -#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k) -#define git_oidmap_valid_index(h, idx) (idx != kh_end(h)) +int git_oidmap_exists(git_oidmap *map, const git_oid *key); +int git_oidmap_has_data(git_oidmap *map, size_t idx); -#define git_oidmap_value_at(h, idx) kh_val(h, idx) +const git_oid *git_oidmap_key(git_oidmap *map, size_t idx); +void git_oidmap_set_key_at(git_oidmap *map, size_t idx, git_oid *key); +void *git_oidmap_value_at(git_oidmap *map, size_t idx); +void git_oidmap_set_value_at(git_oidmap *map, size_t idx, void *value); +void git_oidmap_delete_at(git_oidmap *map, size_t idx); -#define git_oidmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(oid, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) +int git_oidmap_put(git_oidmap *map, const git_oid *key, int *err); +void git_oidmap_insert(git_oidmap *map, const git_oid *key, void *value, int *rval); +void git_oidmap_delete(git_oidmap *map, const git_oid *key); #define git_oidmap_foreach_value kh_foreach_value -#define git_oidmap_size(h) kh_size(h) - -#define git_oidmap_clear(h) kh_clear(oid, h) +#define git_oidmap_begin kh_begin +#define git_oidmap_end kh_end #endif diff --git a/src/openssl_stream.c b/src/openssl_stream.c index b8ab21fef..3860c4f3c 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "openssl_stream.h" + #ifdef GIT_OPENSSL #include <ctype.h> @@ -36,7 +38,7 @@ 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" -#ifdef GIT_THREADS +#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L static git_mutex *openssl_locks; @@ -65,11 +67,14 @@ static void shutdown_ssl_locking(void) CRYPTO_set_locking_callback(NULL); for (i = 0; i < num_locks; ++i) - git_mutex_free(openssl_locks); + git_mutex_free(&openssl_locks[i]); git__free(openssl_locks); } -#endif /* GIT_THREADS */ +#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */ + +static BIO_METHOD *git_stream_bio_method; +static int init_bio_method(void); /** * This function aims to clean-up the SSL context which @@ -77,6 +82,11 @@ static void shutdown_ssl_locking(void) */ static void shutdown_ssl(void) { + if (git_stream_bio_method) { + BIO_meth_free(git_stream_bio_method); + git_stream_bio_method = NULL; + } + if (git__ssl_ctx) { SSL_CTX_free(git__ssl_ctx); git__ssl_ctx = NULL; @@ -94,8 +104,13 @@ int git_openssl_stream_global_init(void) ssl_opts |= SSL_OP_NO_COMPRESSION; #endif +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + /* * Load SSLv{2,3} and TLSv1 so that we can talk with servers * which use the SSL hellos, which are often used for @@ -121,6 +136,13 @@ int git_openssl_stream_global_init(void) git__ssl_ctx = NULL; return -1; } + + if (init_bio_method() < 0) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + return -1; + } + #endif git__on_shutdown(shutdown_ssl); @@ -130,7 +152,7 @@ int git_openssl_stream_global_init(void) int git_openssl_set_locking(void) { -#ifdef GIT_THREADS +#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L int num_locks, i; num_locks = CRYPTO_num_locks(); @@ -147,8 +169,10 @@ int git_openssl_set_locking(void) CRYPTO_set_locking_callback(openssl_locking_function); git__on_shutdown(shutdown_ssl_locking); return 0; +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L + return 0; #else - giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); + giterr_set(GITERR_THREAD, "libgit2 was not built with threads"); return -1; #endif } @@ -156,10 +180,8 @@ int git_openssl_set_locking(void) static int bio_create(BIO *b) { - b->init = 1; - b->num = 0; - b->ptr = NULL; - b->flags = 0; + BIO_set_init(b, 1); + BIO_set_data(b, NULL); return 1; } @@ -169,23 +191,22 @@ static int bio_destroy(BIO *b) if (!b) return 0; - b->init = 0; - b->num = 0; - b->ptr = NULL; - b->flags = 0; + BIO_set_data(b, NULL); return 1; } static int bio_read(BIO *b, char *buf, int len) { - git_stream *io = (git_stream *) b->ptr; + git_stream *io = (git_stream *) BIO_get_data(b); + return (int) git_stream_read(io, buf, len); } static int bio_write(BIO *b, const char *buf, int len) { - git_stream *io = (git_stream *) b->ptr; + git_stream *io = (git_stream *) BIO_get_data(b); + return (int) git_stream_write(io, buf, len, 0); } @@ -214,17 +235,22 @@ static int bio_puts(BIO *b, const char *str) return bio_write(b, str, strlen(str)); } -static BIO_METHOD git_stream_bio_method = { - BIO_TYPE_SOURCE_SINK, - "git_stream", - bio_write, - bio_read, - bio_puts, - bio_gets, - bio_ctrl, - bio_create, - bio_destroy -}; +static int init_bio_method(void) +{ + /* Set up the BIO_METHOD we use for wrapping our own stream implementations */ + git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream"); + GITERR_CHECK_ALLOC(git_stream_bio_method); + + BIO_meth_set_write(git_stream_bio_method, bio_write); + BIO_meth_set_read(git_stream_bio_method, bio_read); + BIO_meth_set_puts(git_stream_bio_method, bio_puts); + BIO_meth_set_gets(git_stream_bio_method, bio_gets); + BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl); + BIO_meth_set_create(git_stream_bio_method, bio_create); + BIO_meth_set_destroy(git_stream_bio_method, bio_destroy); + + return 0; +} static int ssl_set_error(SSL *ssl, int error) { @@ -239,10 +265,10 @@ static int ssl_set_error(SSL *ssl, int error) switch (err) { case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: - giterr_set(GITERR_NET, "SSL error: connection failure\n"); + giterr_set(GITERR_NET, "SSL error: connection failure"); break; case SSL_ERROR_WANT_X509_LOOKUP: - giterr_set(GITERR_NET, "SSL error: x509 error\n"); + giterr_set(GITERR_NET, "SSL error: x509 error"); break; case SSL_ERROR_SYSCALL: e = ERR_get_error(); @@ -309,7 +335,7 @@ static int verify_server_cert(SSL *ssl, const char *host) int i = -1,j; if (SSL_get_verify_result(ssl) != X509_V_OK) { - giterr_set(GITERR_SSL, "The SSL certificate is invalid"); + giterr_set(GITERR_SSL, "the SSL certificate is invalid"); return GIT_ECERTIFICATE; } @@ -339,7 +365,7 @@ static int verify_server_cert(SSL *ssl, const char *host) num = sk_GENERAL_NAME_num(alts); for (i = 0; i < num && matched != 1; i++) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i); - const char *name = (char *) ASN1_STRING_data(gn->d.ia5); + const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5); size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5); /* Skip any names of a type we're not looking for */ @@ -394,7 +420,7 @@ static int verify_server_cert(SSL *ssl, const char *host) if (size > 0) { peer_cn = OPENSSL_malloc(size + 1); GITERR_CHECK_ALLOC(peer_cn); - memcpy(peer_cn, ASN1_STRING_data(str), size); + memcpy(peer_cn, ASN1_STRING_get0_data(str), size); peer_cn[size] = '\0'; } else { goto cert_fail_name; @@ -445,11 +471,12 @@ int openssl_connect(git_stream *stream) st->connected = true; - bio = BIO_new(&git_stream_bio_method); + bio = BIO_new(git_stream_bio_method); GITERR_CHECK_ALLOC(bio); - bio->ptr = st->io; + BIO_set_data(bio, st->io); SSL_set_bio(st->ssl, bio, bio); + /* specify the host in case SNI is needed */ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(st->ssl, st->host); diff --git a/src/openssl_stream.h b/src/openssl_stream.h index 82b5110c4..2dcfcc929 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -7,10 +7,118 @@ #ifndef INCLUDE_openssl_stream_h__ #define INCLUDE_openssl_stream_h__ +#include "common.h" + #include "git2/sys/stream.h" extern int git_openssl_stream_global_init(void); extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port); +/* + * 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. + */ +#ifdef GIT_OPENSSL +# include <openssl/ssl.h> +# include <openssl/err.h> +# include <openssl/x509v3.h> +# include <openssl/bio.h> + + + +# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + +GIT_INLINE(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; +} + +GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom) +{ + git__free(biom); +} + +GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int)) +{ + biom->bwrite = write; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int)) +{ + biom->bread = read; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *)) +{ + biom->bputs = puts; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int)) + +{ + biom->bgets = gets; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *)) +{ + biom->ctrl = ctrl; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *)) +{ + biom->create = create; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *)) +{ + biom->destroy = destroy; + return 1; +} + +GIT_INLINE(int) BIO_get_new_index(void) +{ + /* This exists as of 1.1 so before we'd just have 0 */ + return 0; +} + +GIT_INLINE(void) BIO_set_init(BIO *b, int init) +{ + b->init = init; +} + +GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr) +{ + a->ptr = ptr; +} + +GIT_INLINE(void*) BIO_get_data(BIO *a) +{ + return a->ptr; +} + +GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *)x); +} + +# endif // OpenSSL < 1.1 +#endif // GIT_OPENSSL + #endif diff --git a/src/pack-objects.c b/src/pack-objects.c index 9f62322f7..ef272e8f5 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -41,8 +41,6 @@ struct pack_write_context { git_transfer_progress *stats; }; -GIT__USE_OIDMAP - #ifdef GIT_THREADS #define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \ @@ -162,7 +160,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) git_mutex_init(&pb->progress_mutex) || git_cond_init(&pb->progress_cond)) { - giterr_set(GITERR_OS, "Failed to initialize packbuilder mutex"); + giterr_set(GITERR_OS, "failed to initialize packbuilder mutex"); goto on_error; } @@ -197,10 +195,10 @@ static void rehash(git_packbuilder *pb) size_t i; int ret; - kh_clear(oid, pb->object_ix); + git_oidmap_clear(pb->object_ix); for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) { - pos = kh_put(oid, pb->object_ix, &po->id, &ret); - kh_value(pb->object_ix, pos) = po; + pos = git_oidmap_put(pb->object_ix, &po->id, &ret); + git_oidmap_set_value_at(pb->object_ix, pos, po); } } @@ -216,8 +214,7 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, /* If the object already exists in the hash table, then we don't * have any work to do */ - pos = kh_get(oid, pb->object_ix, oid); - if (pos != kh_end(pb->object_ix)) + if (git_oidmap_exists(pb->object_ix, oid)) return 0; if (pb->nr_objects >= pb->nr_alloc) { @@ -225,7 +222,7 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, GITERR_CHECK_ALLOC_MULTIPLY(&newsize, newsize, 3 / 2); if (!git__is_uint32(newsize)) { - giterr_set(GITERR_NOMEMORY, "Packfile too large to fit in memory."); + giterr_set(GITERR_NOMEMORY, "packfile too large to fit in memory."); return -1; } @@ -247,13 +244,13 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, git_oid_cpy(&po->id, oid); po->hash = name_hash(name); - pos = kh_put(oid, pb->object_ix, &po->id, &ret); + pos = git_oidmap_put(pb->object_ix, &po->id, &ret); if (ret < 0) { giterr_set_oom(); return ret; } assert(ret != 0); - kh_value(pb->object_ix, pos) = po; + git_oidmap_set_value_at(pb->object_ix, pos, po); pb->done = false; @@ -298,7 +295,7 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po) goto on_error; if (error == GIT_EBUFS || delta_size != po->delta_size) { - giterr_set(GITERR_INVALID, "Delta size changed"); + giterr_set(GITERR_INVALID, "delta size changed"); goto on_error; } @@ -517,11 +514,11 @@ static int cb_tag_foreach(const char *name, git_oid *oid, void *data) GIT_UNUSED(name); - pos = kh_get(oid, pb->object_ix, oid); - if (pos == kh_end(pb->object_ix)) + pos = git_oidmap_lookup_index(pb->object_ix, oid); + if (!git_oidmap_valid_index(pb->object_ix, pos)) return 0; - po = kh_value(pb->object_ix, pos); + po = git_oidmap_value_at(pb->object_ix, pos); po->tagged = 1; /* TODO: peel objects */ @@ -808,7 +805,7 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, if (sz != trg_size) { giterr_set(GITERR_INVALID, - "Inconsistent target object length"); + "inconsistent target object length"); return -1; } @@ -830,7 +827,7 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, if (sz != src_size) { giterr_set(GITERR_INVALID, - "Inconsistent source object length"); + "inconsistent source object length"); return -1; } @@ -1388,6 +1385,7 @@ int git_packbuilder_write( git_indexer *indexer; git_transfer_progress stats; struct pack_write_context ctx; + int t; PREPARE_PACK; @@ -1395,6 +1393,9 @@ int git_packbuilder_write( &indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0) return -1; + if (!git_repository__cvar(&t, pb->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) + git_indexer__set_fsync(indexer, 1); + ctx.indexer = indexer; ctx.stats = &stats; @@ -1541,7 +1542,7 @@ static int retrieve_object(git_walk_object **out, git_packbuilder *pb, const git if ((error = lookup_walk_object(&obj, pb, id)) < 0) return error; - git_oidmap_insert(pb->walk_objects, &obj->id, obj, error); + git_oidmap_insert(pb->walk_objects, &obj->id, obj, &error); } *out = obj; @@ -1738,7 +1739,7 @@ int git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk) if (error == GIT_ITEROVER) error = 0; - return 0; + return error; } int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload) diff --git a/src/pack-objects.h b/src/pack-objects.h index 5a84f4158..e1e0ee3c8 100644 --- a/src/pack-objects.h +++ b/src/pack-objects.h @@ -16,6 +16,7 @@ #include "netops.h" #include "zstream.h" #include "pool.h" +#include "indexer.h" #include "git2/oid.h" #include "git2/pack.h" diff --git a/src/pack.c b/src/pack.c index 310f00fa3..7fd95c905 100644 --- a/src/pack.c +++ b/src/pack.c @@ -5,9 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" -#include "odb.h" #include "pack.h" + +#include "odb.h" #include "delta.h" #include "sha1_lookup.h" #include "mwindow.h" @@ -16,9 +16,6 @@ #include <zlib.h> -GIT__USE_OFFMAP -GIT__USE_OIDMAP - static int packfile_open(struct git_pack_file *p); static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n); static int packfile_unpack_compressed( @@ -45,7 +42,7 @@ static int pack_entry_find_offset( static int packfile_error(const char *message) { - giterr_set(GITERR_ODB, "Invalid pack file - %s", message); + giterr_set(GITERR_ODB, "invalid pack file - %s", message); return -1; } @@ -78,13 +75,12 @@ static void free_cache_object(void *o) static void cache_free(git_pack_cache *cache) { - khiter_t k; + git_pack_cache_entry *entry; if (cache->entries) { - for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) { - if (kh_exist(cache->entries, k)) - free_cache_object(kh_value(cache->entries, k)); - } + git_offmap_foreach_value(cache->entries, entry, { + free_cache_object(entry); + }); git_offmap_free(cache->entries); cache->entries = NULL; @@ -99,7 +95,7 @@ static int cache_init(git_pack_cache *cache) cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT; if (git_mutex_init(&cache->lock)) { - giterr_set(GITERR_OS, "Failed to initialize pack cache mutex"); + giterr_set(GITERR_OS, "failed to initialize pack cache mutex"); git__free(cache->entries); cache->entries = NULL; @@ -118,9 +114,9 @@ static git_pack_cache_entry *cache_get(git_pack_cache *cache, git_off_t offset) if (git_mutex_lock(&cache->lock) < 0) return NULL; - k = kh_get(off, cache->entries, offset); - if (k != kh_end(cache->entries)) { /* found it */ - entry = kh_value(cache->entries, k); + k = git_offmap_lookup_index(cache->entries, offset); + if (git_offmap_valid_index(cache->entries, k)) { /* found it */ + entry = git_offmap_value_at(cache->entries, k); git_atomic_inc(&entry->refcount); entry->last_usage = cache->use_ctr++; } @@ -132,21 +128,16 @@ static git_pack_cache_entry *cache_get(git_pack_cache *cache, git_off_t offset) /* Run with the cache lock held */ static void free_lowest_entry(git_pack_cache *cache) { + git_off_t offset; git_pack_cache_entry *entry; - khiter_t k; - - for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) { - if (!kh_exist(cache->entries, k)) - continue; - - entry = kh_value(cache->entries, k); + git_offmap_foreach(cache->entries, offset, entry, { if (entry && entry->refcount.val == 0) { cache->memory_used -= entry->raw.len; - kh_del(off, cache->entries, k); + git_offmap_delete(cache->entries, offset); free_cache_object(entry); } - } + }); } static int cache_add( @@ -170,14 +161,14 @@ static int cache_add( return -1; } /* Add it to the cache if nobody else has */ - exists = kh_get(off, cache->entries, offset) != kh_end(cache->entries); + exists = git_offmap_exists(cache->entries, offset); if (!exists) { while (cache->memory_used + base->len > cache->memory_limit) free_lowest_entry(cache); - k = kh_put(off, cache->entries, offset, &error); + k = git_offmap_put(cache->entries, offset, &error); assert(error != 0); - kh_value(cache->entries, k) = entry; + git_offmap_set_value_at(cache->entries, k, entry); cache->memory_used += entry->raw.len; *cached_out = entry; @@ -226,7 +217,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p) if (p_fstat(fd, &st) < 0) { p_close(fd); - giterr_set(GITERR_OS, "Unable to stat pack index '%s'", path); + giterr_set(GITERR_OS, "unable to stat pack index '%s'", path); return -1; } @@ -235,7 +226,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p) (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20) { p_close(fd); - giterr_set(GITERR_ODB, "Invalid pack index '%s'", path); + giterr_set(GITERR_ODB, "invalid pack index '%s'", path); return -1; } @@ -321,7 +312,7 @@ static int pack_index_open(struct git_pack_file *p) { int error = 0; size_t name_len; - git_buf idx_name = GIT_BUF_INIT; + git_buf idx_name; if (p->index_version > -1) return 0; @@ -329,11 +320,13 @@ static int pack_index_open(struct git_pack_file *p) name_len = strlen(p->pack_name); assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */ - git_buf_grow(&idx_name, name_len); + if (git_buf_init(&idx_name, name_len) < 0) + return -1; + git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack")); git_buf_puts(&idx_name, ".idx"); if (git_buf_oom(&idx_name)) { - giterr_set_oom(); + git_buf_free(&idx_name); return -1; } @@ -509,8 +502,10 @@ int git_packfile_resolve_header( git_packfile_stream_free(&stream); if (error < 0) return error; - } else + } else { *size_p = size; + base_offset = 0; + } while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { curpos = base_offset; @@ -757,8 +752,11 @@ int git_packfile_unpack( } cleanup: - if (error < 0) + if (error < 0) { git__free(obj->data); + if (cached) + git_atomic_dec(&cached->refcount); + } if (elem) *obj_offset = curpos; @@ -957,10 +955,10 @@ git_off_t get_delta_base( git_oid oid; git_oid_fromraw(&oid, base_info); - k = kh_get(oid, p->idx_cache, &oid); - if (k != kh_end(p->idx_cache)) { + k = git_oidmap_lookup_index(p->idx_cache, &oid); + if (git_oidmap_valid_index(p->idx_cache, k)) { *curpos += 20; - return ((struct git_pack_entry *)kh_value(p->idx_cache, k))->offset; + return ((struct git_pack_entry *)git_oidmap_value_at(p->idx_cache, k))->offset; } else { /* If we're building an index, don't try to find the pack * entry; we just haven't seen it yet. We'll make @@ -986,6 +984,18 @@ git_off_t get_delta_base( * ***********************************************************/ +void git_packfile_close(struct git_pack_file *p, bool unlink_packfile) +{ + if (p->mwf.fd >= 0) { + git_mwindow_free_all_locked(&p->mwf); + p_close(p->mwf.fd); + p->mwf.fd = -1; + } + + if (unlink_packfile) + p_unlink(p->pack_name); +} + void git_packfile_free(struct git_pack_file *p) { if (!p) @@ -993,10 +1003,7 @@ void git_packfile_free(struct git_pack_file *p) cache_free(&p->bases); - if (p->mwf.fd >= 0) { - git_mwindow_free_all_locked(&p->mwf); - p_close(p->mwf.fd); - } + git_packfile_close(p, false); pack_index_free(p); @@ -1077,7 +1084,7 @@ static int packfile_open(struct git_pack_file *p) return 0; cleanup: - giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name); + giterr_set(GITERR_OS, "invalid packfile '%s'", p->pack_name); if (p->mwf.fd >= 0) p_close(p->mwf.fd); @@ -1153,7 +1160,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) p->index_version = -1; if (git_mutex_init(&p->lock)) { - giterr_set(GITERR_OS, "Failed to initialize packfile mutex"); + giterr_set(GITERR_OS, "failed to initialize packfile mutex"); git__free(p); return -1; } @@ -1268,8 +1275,8 @@ static int pack_entry_find_offset( const git_oid *short_oid, size_t len) { - const uint32_t *level1_ofs = p->index_map.data; - const unsigned char *index = p->index_map.data; + const uint32_t *level1_ofs; + const unsigned char *index; unsigned hi, lo, stride; int pos, found = 0; git_off_t offset; @@ -1283,11 +1290,11 @@ static int pack_entry_find_offset( if ((error = pack_index_open(p)) < 0) return error; assert(p->index_map.data); - - index = p->index_map.data; - level1_ofs = p->index_map.data; } + index = p->index_map.data; + level1_ofs = p->index_map.data; + if (p->index_version > 1) { level1_ofs += 2; index += 8; @@ -1309,11 +1316,7 @@ static int pack_entry_find_offset( short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects); #endif -#ifdef GIT_USE_LOOKUP - pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id); -#else pos = sha1_position(index, stride, lo, hi, short_oid->id); -#endif if (pos >= 0) { /* An object matching exactly the oid was found */ diff --git a/src/pack.h b/src/pack.h index 5302db5b7..c0ca74f14 100644 --- a/src/pack.h +++ b/src/pack.h @@ -8,11 +8,12 @@ #ifndef INCLUDE_pack_h__ #define INCLUDE_pack_h__ +#include "common.h" + #include <zlib.h> #include "git2/oid.h" -#include "common.h" #include "map.h" #include "mwindow.h" #include "odb.h" @@ -149,6 +150,7 @@ git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs, git_off_t *curpos, git_otype type, git_off_t delta_obj_offset); +void git_packfile_close(struct git_pack_file *p, bool unlink_packfile); void git_packfile_free(struct git_pack_file *p); int git_packfile_alloc(struct git_pack_file **pack_out, const char *path); diff --git a/src/patch.c b/src/patch.c index 9b7c9c64c..5e329518d 100644 --- a/src/patch.c +++ b/src/patch.c @@ -1,7 +1,14 @@ -#include "git2/patch.h" -#include "diff.h" +/* +* 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 "patch.h" +#include "git2/patch.h" +#include "diff.h" int git_patch__invoke_callbacks( git_patch *patch, diff --git a/src/patch.h b/src/patch.h index 525ae7007..156d1310e 100644 --- a/src/patch.h +++ b/src/patch.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_patch_h__ #define INCLUDE_patch_h__ +#include "common.h" + #include "git2/patch.h" #include "array.h" diff --git a/src/patch_generate.c b/src/patch_generate.c index a13f2ff5d..3995c56be 100644 --- a/src/patch_generate.c +++ b/src/patch_generate.c @@ -4,13 +4,14 @@ * 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 "common.h" + +#include "patch_generate.h" + #include "git2/blob.h" #include "diff.h" #include "diff_generate.h" #include "diff_file.h" #include "diff_driver.h" -#include "patch_generate.h" #include "diff_xdiff.h" #include "delta.h" #include "zstream.h" @@ -206,35 +207,14 @@ static int patch_generated_load(git_patch_generated *patch, git_patch_generated_ ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0)); - /* always try to load workdir content first because filtering may - * need 2x data size and this minimizes peak memory footprint - */ - if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->ofile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->ofile.file)) - goto cleanup; - } - if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->nfile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->nfile.file)) - goto cleanup; - } - - /* once workdir has been tried, load other data as needed */ - if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->ofile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->ofile.file)) - goto cleanup; - } - if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->nfile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->nfile.file)) - goto cleanup; - } + if ((error = git_diff_file_content__load( + &patch->ofile, &patch->base.diff_opts)) < 0 || + should_skip_binary(patch, patch->ofile.file)) + goto cleanup; + if ((error = git_diff_file_content__load( + &patch->nfile, &patch->base.diff_opts)) < 0 || + should_skip_binary(patch, patch->nfile.file)) + goto cleanup; /* if previously missing an oid, and now that we have it the two sides * are the same (and not submodules), update MODIFIED -> UNMODIFIED @@ -284,7 +264,7 @@ static int create_binary( size_t b_datalen) { git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT; - size_t delta_data_len; + size_t delta_data_len = 0; int error; /* The git_delta function accepts unsigned long only */ @@ -417,58 +397,10 @@ static int diff_required(git_diff *diff, const char *action) { if (diff) return 0; - giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action); + giterr_set(GITERR_INVALID, "must provide valid diff to %s", action); return -1; } -int git_diff_foreach( - git_diff *diff, - git_diff_file_cb file_cb, - git_diff_binary_cb binary_cb, - git_diff_hunk_cb hunk_cb, - git_diff_line_cb data_cb, - void *payload) -{ - int error = 0; - git_xdiff_output xo; - size_t idx; - git_patch_generated patch; - - if ((error = diff_required(diff, "git_diff_foreach")) < 0) - return error; - - memset(&xo, 0, sizeof(xo)); - memset(&patch, 0, sizeof(patch)); - diff_output_init( - &xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload); - git_xdiff_init(&xo, &diff->opts); - - git_vector_foreach(&diff->deltas, idx, patch.base.delta) { - - /* check flags against patch status */ - if (git_diff_delta__should_skip(&diff->opts, patch.base.delta)) - continue; - - if (binary_cb || hunk_cb || data_cb) { - if ((error = patch_generated_init(&patch, diff, idx)) != 0 || - (error = patch_generated_load(&patch, &xo.output)) != 0) - return error; - } - - if ((error = patch_generated_invoke_file_callback(&patch, &xo.output)) == 0) { - if (binary_cb || hunk_cb || data_cb) - error = patch_generated_create(&patch, &xo.output); - } - - git_patch_free(&patch.base); - - if (error) - break; - } - - return error; -} - typedef struct { git_patch_generated patch; git_diff_delta delta; @@ -710,7 +642,7 @@ int git_patch_from_blob_and_buffer( git_patch **out, const git_blob *old_blob, const char *old_path, - const char *buf, + const void *buf, size_t buflen, const char *buf_path, const git_diff_options *opts) @@ -749,7 +681,7 @@ int git_patch_from_buffers( const void *old_buf, size_t old_len, const char *old_path, - const char *new_buf, + const void *new_buf, size_t new_len, const char *new_path, const git_diff_options *opts) @@ -776,7 +708,7 @@ int git_patch_generated_from_diff( delta = git_vector_get(&diff->deltas, idx); if (!delta) { - giterr_set(GITERR_INVALID, "Index out of range for delta in diff"); + giterr_set(GITERR_INVALID, "index out of range for delta in diff"); return GIT_ENOTFOUND; } diff --git a/src/patch_generate.h b/src/patch_generate.h index 2e89b5c4d..20f78cbfa 100644 --- a/src/patch_generate.h +++ b/src/patch_generate.h @@ -8,6 +8,7 @@ #define INCLUDE_patch_generate_h__ #include "common.h" + #include "diff.h" #include "diff_file.h" #include "patch.h" diff --git a/src/patch_parse.c b/src/patch_parse.c index 5ee09ee27..a531eec4d 100644 --- a/src/patch_parse.c +++ b/src/patch_parse.c @@ -4,9 +4,11 @@ * 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 "patch_parse.h" + #include "git2/patch.h" #include "patch.h" -#include "patch_parse.h" #include "diff_parse.h" #include "path.h" @@ -176,7 +178,7 @@ static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx) int ret; if (ctx->line_len < 1 || !git__isdigit(ctx->line[0])) - return parse_err("invalid file mode at line %d", ctx->line_num); + return parse_err("invalid file mode at line %"PRIuZ, ctx->line_num); if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0) return ret; @@ -205,7 +207,7 @@ static int parse_header_oid( if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ || git_oid_fromstrn(oid, ctx->line, len) < 0) - return parse_err("invalid hex formatted object id at line %d", + return parse_err("invalid hex formatted object id at line %"PRIuZ, ctx->line_num); parse_advance_chars(ctx, len); @@ -350,7 +352,7 @@ static int parse_header_similarity( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0) - return parse_err("invalid similarity percentage at line %d", + return parse_err("invalid similarity percentage at line %"PRIuZ, ctx->line_num); return 0; @@ -362,7 +364,7 @@ static int parse_header_dissimilarity( uint16_t dissimilarity; if (parse_header_percent(&dissimilarity, ctx) < 0) - return parse_err("invalid similarity percentage at line %d", + return parse_err("invalid similarity percentage at line %"PRIuZ, ctx->line_num); patch->base.delta->similarity = 100 - dissimilarity; @@ -406,15 +408,15 @@ static int parse_header_git( /* Parse the diff --git line */ if (parse_advance_expected_str(ctx, "diff --git ") < 0) - return parse_err("corrupt git diff header at line %d", ctx->line_num); + return parse_err("corrupt git diff header at line %"PRIuZ, ctx->line_num); if (parse_header_path(&patch->header_old_path, ctx) < 0) - return parse_err("corrupt old path in git diff header at line %d", + return parse_err("corrupt old path in git diff header at line %"PRIuZ, ctx->line_num); if (parse_advance_ws(ctx) < 0 || parse_header_path(&patch->header_new_path, ctx) < 0) - return parse_err("corrupt new path in git diff header at line %d", + return parse_err("corrupt new path in git diff header at line %"PRIuZ, ctx->line_num); /* Parse remaining header lines */ @@ -444,10 +446,10 @@ static int parse_header_git( goto done; parse_advance_ws(ctx); - parse_advance_expected_str(ctx, "\n"); - if (ctx->line_len > 0) { - error = parse_err("trailing data at line %d", ctx->line_num); + if (parse_advance_expected_str(ctx, "\n") < 0 || + ctx->line_len > 0) { + error = parse_err("trailing data at line %"PRIuZ, ctx->line_num); goto done; } @@ -456,7 +458,7 @@ static int parse_header_git( } if (!found) { - error = parse_err("invalid patch header at line %d", + error = parse_err("invalid patch header at line %"PRIuZ, ctx->line_num); goto done; } @@ -536,7 +538,7 @@ static int parse_hunk_header( hunk->hunk.header_len = ctx->line - header_start; if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1)) - return parse_err("oversized patch hunk header at line %d", + return parse_err("oversized patch hunk header at line %"PRIuZ, ctx->line_num); memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len); @@ -545,7 +547,7 @@ static int parse_hunk_header( return 0; fail: - giterr_set(GITERR_PATCH, "invalid patch hunk header at line %d", + giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ, ctx->line_num); return -1; } @@ -562,15 +564,16 @@ static int parse_hunk_body( int newlines = hunk->hunk.new_lines; for (; - ctx->remain_len > 4 && (oldlines || newlines) && - memcmp(ctx->line, "@@ -", 4) != 0; + ctx->remain_len > 1 && + (oldlines || newlines) && + (ctx->remain_len <= 4 || memcmp(ctx->line, "@@ -", 4) != 0); parse_advance_line(ctx)) { int origin; int prefix = 1; if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') { - error = parse_err("invalid patch instruction at line %d", + error = parse_err("invalid patch instruction at line %"PRIuZ, ctx->line_num); goto done; } @@ -596,7 +599,7 @@ static int parse_hunk_body( break; default: - error = parse_err("invalid patch hunk at line %d", ctx->line_num); + error = parse_err("invalid patch hunk at line %"PRIuZ, ctx->line_num); goto done; } @@ -672,7 +675,7 @@ static int parse_patch_header( continue; } - error = parse_err("invalid hunk header outside patch at line %d", + error = parse_err("invalid hunk header outside patch at line %"PRIuZ, line_num); goto done; } @@ -715,12 +718,12 @@ static int parse_patch_binary_side( parse_advance_chars(ctx, 6); } else { error = parse_err( - "unknown binary delta type at line %d", ctx->line_num); + "unknown binary delta type at line %"PRIuZ, ctx->line_num); goto done; } if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) { - error = parse_err("invalid binary size at line %d", ctx->line_num); + error = parse_err("invalid binary size at line %"PRIuZ, ctx->line_num); goto done; } @@ -736,7 +739,7 @@ static int parse_patch_binary_side( decoded_len = c - 'a' + (('z' - 'a') + 1) + 1; if (!decoded_len) { - error = parse_err("invalid binary length at line %d", ctx->line_num); + error = parse_err("invalid binary length at line %"PRIuZ, ctx->line_num); goto done; } @@ -745,7 +748,7 @@ static int parse_patch_binary_side( encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5; if (encoded_len > ctx->line_len - 1) { - error = parse_err("truncated binary data at line %d", ctx->line_num); + error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num); goto done; } @@ -754,14 +757,14 @@ static int parse_patch_binary_side( goto done; if (decoded.size - decoded_orig != decoded_len) { - error = parse_err("truncated binary data at line %d", ctx->line_num); + error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num); goto done; } parse_advance_chars(ctx, encoded_len); if (parse_advance_nl(ctx) < 0) { - error = parse_err("trailing data at line %d", ctx->line_num); + error = parse_err("trailing data at line %"PRIuZ, ctx->line_num); goto done; } } @@ -785,7 +788,7 @@ static int parse_patch_binary( if (parse_advance_expected_str(ctx, "GIT binary patch") < 0 || parse_advance_nl(ctx) < 0) - return parse_err("corrupt git binary header at line %d", ctx->line_num); + return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num); /* parse old->new binary diff */ if ((error = parse_patch_binary_side( @@ -793,7 +796,7 @@ static int parse_patch_binary( return error; if (parse_advance_nl(ctx) < 0) - return parse_err("corrupt git binary separator at line %d", + return parse_err("corrupt git binary separator at line %"PRIuZ, ctx->line_num); /* parse new->old binary diff */ @@ -802,7 +805,7 @@ static int parse_patch_binary( return error; if (parse_advance_nl(ctx) < 0) - return parse_err("corrupt git binary patch separator at line %d", + return parse_err("corrupt git binary patch separator at line %"PRIuZ, ctx->line_num); patch->base.binary.contains_data = 1; @@ -820,7 +823,7 @@ static int parse_patch_binary_nodata( parse_advance_expected_str(ctx, patch->header_new_path) < 0 || parse_advance_expected_str(ctx, " differ") < 0 || parse_advance_nl(ctx) < 0) - return parse_err("corrupt git binary header at line %d", ctx->line_num); + return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num); patch->base.binary.contains_data = 0; patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY; @@ -912,7 +915,7 @@ static int check_prefix( if (remain_len || !*path) return parse_err( - "header filename does not contain %d path components", + "header filename does not contain %"PRIuZ" path components", prefix_len); done: @@ -1014,8 +1017,10 @@ git_patch_parse_ctx *git_patch_parse_ctx_init( return NULL; if (content_len) { - if ((ctx->content = git__malloc(content_len)) == NULL) + if ((ctx->content = git__malloc(content_len)) == NULL) { + git__free(ctx); return NULL; + } memcpy((char *)ctx->content, content, content_len); } diff --git a/src/patch_parse.h b/src/patch_parse.h index 99eb4587b..f27651e39 100644 --- a/src/patch_parse.h +++ b/src/patch_parse.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_patch_parse_h__ #define INCLUDE_patch_parse_h__ +#include "common.h" + +#include "patch.h" + typedef struct { git_refcount rc; diff --git a/src/path.c b/src/path.c index e5f04a56a..ea0a3c3f6 100644 --- a/src/path.c +++ b/src/path.c @@ -4,8 +4,9 @@ * 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 "common.h" + #include "path.h" + #include "posix.h" #include "repository.h" #ifdef GIT_WIN32 @@ -111,13 +112,41 @@ Exit: } /* + * Determine if the path is a Windows prefix and, if so, returns + * its actual lentgh. If it is not a prefix, returns -1. + */ +static int win32_prefix_length(const char *path, int len) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(path); + GIT_UNUSED(len); +#else + /* + * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return + * 'C:/' here + */ + if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) + return 2; + + /* + * Similarly checks if we're dealing with a network computer name + * '//computername/.git' will return '//computername/' + */ + if (looks_like_network_computer_name(path, len)) + return len; +#endif + + return -1; +} + +/* * Based on the Android implementation, BSD licensed. * Check http://android.git.kernel.org/ */ int git_path_dirname_r(git_buf *buffer, const char *path) { const char *endp; - int result, len; + int is_prefix = 0, len; /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { @@ -131,6 +160,11 @@ int git_path_dirname_r(git_buf *buffer, const char *path) while (endp > path && *endp == '/') endp--; + if ((len = win32_prefix_length(path, endp - path + 1)) > 0) { + is_prefix = 1; + goto Exit; + } + /* Find the start of the dir */ while (endp > path && *endp != '/') endp--; @@ -146,35 +180,23 @@ int git_path_dirname_r(git_buf *buffer, const char *path) endp--; } while (endp > path && *endp == '/'); - /* Cast is safe because max path < max int */ - len = (int)(endp - path + 1); - -#ifdef GIT_WIN32 - /* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return - 'C:/' here */ - - if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) { - len = 3; - goto Exit; - } - - /* Similarly checks if we're dealing with a network computer name - '//computername/.git' will return '//computername/' */ - - if (looks_like_network_computer_name(path, len)) { - len++; + if ((len = win32_prefix_length(path, endp - path + 1)) > 0) { + is_prefix = 1; goto Exit; } -#endif + /* Cast is safe because max path < max int */ + len = (int)(endp - path + 1); Exit: - result = len; - - if (buffer != NULL && git_buf_set(buffer, path, len) < 0) - return -1; + if (buffer) { + if (git_buf_set(buffer, path, len) < 0) + return -1; + if (is_prefix && git_buf_putc(buffer, '/') < 0) + return -1; + } - return result; + return len; } @@ -341,7 +363,7 @@ int git_path_prettify(git_buf *path_out, const char *path, const char *base) if (p_realpath(path, buf) == NULL) { /* giterr_set resets the errno when dealing with a GITERR_OS kind of error */ int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1; - giterr_set(GITERR_OS, "Failed to resolve path '%s'", path); + giterr_set(GITERR_OS, "failed to resolve path '%s'", path); git_buf_clear(path_out); @@ -632,20 +654,24 @@ int git_path_set_error(int errno_value, const char *path, const char *action) switch (errno_value) { case ENOENT: case ENOTDIR: - giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action); + giterr_set(GITERR_OS, "could not find '%s' to %s", path, action); return GIT_ENOTFOUND; case EINVAL: case ENAMETOOLONG: - giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path); + giterr_set(GITERR_OS, "invalid path for filesystem '%s'", path); return GIT_EINVALIDSPEC; case EEXIST: - giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path); + giterr_set(GITERR_OS, "failed %s - '%s' already exists", action, path); return GIT_EEXISTS; + case EACCES: + giterr_set(GITERR_OS, "failed %s - '%s' is locked", action, path); + return GIT_ELOCKED; + default: - giterr_set(GITERR_OS, "Could not %s '%s'", action, path); + giterr_set(GITERR_OS, "could not %s '%s'", action, path); return -1; } } @@ -675,7 +701,8 @@ static bool _check_dir_contents( return false; /* save excursion */ - git_buf_joinpath(dir, dir->ptr, sub); + if (git_buf_joinpath(dir, dir->ptr, sub) < 0) + return false; result = predicate(dir->ptr); @@ -754,7 +781,7 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) /* error out if trying to up one from a hard base */ if (to == base && ceiling != 0) { giterr_set(GITERR_INVALID, - "Cannot strip root component off url"); + "cannot strip root component off url"); return -1; } @@ -800,8 +827,8 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) int git_path_apply_relative(git_buf *target, const char *relpath) { - git_buf_joinpath(target, git_buf_cstr(target), relpath); - return git_path_resolve_relative(target, 0); + return git_buf_joinpath(target, git_buf_cstr(target), relpath) || + git_path_resolve_relative(target, 0); } int git_path_cmp( @@ -983,7 +1010,7 @@ int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) return 0; fail: - giterr_set(GITERR_OS, "Unable to convert unicode path data"); + giterr_set(GITERR_OS, "unable to convert unicode path data"); return -1; } @@ -1076,7 +1103,7 @@ int git_path_direach( wd_len = git_buf_len(path); if ((dir = opendir(path->ptr)) == NULL) { - giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr); + giterr_set(GITERR_OS, "failed to open directory '%s'", path->ptr); if (errno == ENOENT) return GIT_ENOTFOUND; @@ -1141,7 +1168,6 @@ int git_path_diriter_init( unsigned int flags) { git_win32_path path_filter; - git_buf hack = {0}; static int is_win7_or_later = -1; if (is_win7_or_later < 0) @@ -1158,13 +1184,13 @@ int git_path_diriter_init( git_path_trim_slashes(&diriter->path_utf8); if (diriter->path_utf8.size == 0) { - giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path); + giterr_set(GITERR_FILESYSTEM, "could not open directory '%s'", path); return -1; } if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 || !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) { - giterr_set(GITERR_OS, "Could not parse the directory path '%s'", path); + giterr_set(GITERR_OS, "could not parse the directory path '%s'", path); return -1; } @@ -1177,7 +1203,7 @@ int git_path_diriter_init( is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); if (diriter->handle == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Could not open directory '%s'", path); + giterr_set(GITERR_OS, "could not open directory '%s'", path); return -1; } @@ -1307,14 +1333,14 @@ int git_path_diriter_init( git_path_trim_slashes(&diriter->path); if (diriter->path.size == 0) { - giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path); + giterr_set(GITERR_FILESYSTEM, "could not open directory '%s'", path); return -1; } if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) { git_buf_free(&diriter->path); - giterr_set(GITERR_OS, "Failed to open directory '%s'", path); + giterr_set(GITERR_OS, "failed to open directory '%s'", path); return -1; } @@ -1347,7 +1373,7 @@ int git_path_diriter_next(git_path_diriter *diriter) return GIT_ITEROVER; giterr_set(GITERR_OS, - "Could not read directory '%s'", diriter->path); + "could not read directory '%s'", diriter->path.ptr); return -1; } } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name)); @@ -1683,6 +1709,7 @@ GIT_INLINE(unsigned int) dotgit_flags( unsigned int flags) { int protectHFS = 0, protectNTFS = 0; + int error = 0; flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; @@ -1695,13 +1722,13 @@ GIT_INLINE(unsigned int) dotgit_flags( #endif if (repo && !protectHFS) - git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS); - if (protectHFS) + error = git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS); + if (!error && protectHFS) flags |= GIT_PATH_REJECT_DOT_GIT_HFS; if (repo && !protectNTFS) - git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS); - if (protectNTFS) + error = git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS); + if (!error && protectNTFS) flags |= GIT_PATH_REJECT_DOT_GIT_NTFS; return flags; diff --git a/src/path.h b/src/path.h index fb45a6534..360372cfb 100644 --- a/src/path.h +++ b/src/path.h @@ -8,6 +8,7 @@ #define INCLUDE_path_h__ #include "common.h" + #include "posix.h" #include "buffer.h" #include "vector.h" diff --git a/src/pathspec.c b/src/pathspec.c index 361b398b8..998b6fb36 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -5,9 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "pathspec.h" + #include "git2/pathspec.h" #include "git2/diff.h" -#include "pathspec.h" #include "buf_text.h" #include "attr_file.h" #include "iterator.h" @@ -487,7 +488,7 @@ static int pathspec_match_from_iterator( /* if every pattern failed to match, then we have failed */ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) { - giterr_set(GITERR_INVALID, "No matching files were found"); + giterr_set(GITERR_INVALID, "no matching files were found"); error = GIT_ENOTFOUND; } @@ -658,7 +659,7 @@ int git_pathspec_match_diff( /* if every pattern failed to match, then we have failed */ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) { - giterr_set(GITERR_INVALID, "No matching deltas were found"); + giterr_set(GITERR_INVALID, "no matching deltas were found"); error = GIT_ENOTFOUND; } diff --git a/src/pathspec.h b/src/pathspec.h index 40cd21c3f..c4d1a83d3 100644 --- a/src/pathspec.h +++ b/src/pathspec.h @@ -8,7 +8,8 @@ #define INCLUDE_pathspec_h__ #include "common.h" -#include <git2/pathspec.h> + +#include "git2/pathspec.h" #include "buffer.h" #include "vector.h" #include "pool.h" diff --git a/src/pool.c b/src/pool.c index b4fc50fca..c0efe9c9d 100644 --- a/src/pool.c +++ b/src/pool.c @@ -1,4 +1,12 @@ +/* + * 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 "pool.h" + #include "posix.h" #ifndef GIT_WIN32 #include <unistd.h> diff --git a/src/pool.h b/src/pool.h index f61f16944..92ddf994a 100644 --- a/src/pool.h +++ b/src/pool.h @@ -8,6 +8,7 @@ #define INCLUDE_pool_h__ #include "common.h" + #include "vector.h" typedef struct git_pool_page git_pool_page; diff --git a/src/posix.c b/src/posix.c index b3f1a1cd3..6e7985ece 100644 --- a/src/posix.c +++ b/src/posix.c @@ -4,12 +4,15 @@ * 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 "common.h" + #include "posix.h" + #include "path.h" #include <stdio.h> #include <ctype.h> +size_t p_fsync__cnt = 0; + #ifndef GIT_WIN32 #ifdef NO_ADDRINFO @@ -38,7 +41,7 @@ int p_getaddrinfo( if (ainfo->ai_servent) ainfo->ai_port = ainfo->ai_servent->s_port; else - ainfo->ai_port = atol(port); + ainfo->ai_port = htons(atol(port)); memcpy(&ainfo->ai_addr_in.sin_addr, ainfo->ai_hostent->h_addr_list[0], @@ -240,7 +243,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs out->len = 0; if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { - giterr_set(GITERR_OS, "Trying to map shared-writeable"); + giterr_set(GITERR_OS, "trying to map shared-writeable"); return -1; } diff --git a/src/posix.h b/src/posix.h index f204751cf..3e3bcb287 100644 --- a/src/posix.h +++ b/src/posix.h @@ -8,6 +8,7 @@ #define INCLUDE_posix_h__ #include "common.h" + #include <fcntl.h> #include <time.h> #include "fnmatch.h" @@ -24,6 +25,10 @@ #define _S_IFLNK S_IFLNK #endif +#ifndef S_IWUSR +#define S_IWUSR 00200 +#endif + #ifndef S_IXUSR #define S_IXUSR 00100 #endif @@ -111,6 +116,12 @@ extern int p_rename(const char *from, const char *to); extern int git__page_size(size_t *page_size); extern int git__mmap_alignment(size_t *page_size); +/* The number of times `p_fsync` has been called. Note that this is for + * test code only; it it not necessarily thread-safe and should not be + * relied upon in production. + */ +extern size_t p_fsync__cnt; + /** * Platform-dependent methods */ diff --git a/src/pqueue.c b/src/pqueue.c index 54a60ca04..3820e999c 100644 --- a/src/pqueue.c +++ b/src/pqueue.c @@ -6,6 +6,7 @@ */ #include "pqueue.h" + #include "util.h" #define PQUEUE_LCHILD_OF(I) (((I)<<1)+1) @@ -86,14 +87,15 @@ int git_pqueue_insert(git_pqueue *pq, void *item) if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 && pq->length >= pq->_alloc_size) { - /* skip this item if below min item in heap */ - if (pq->_cmp(item, git_vector_get(pq, 0)) <= 0) + /* skip this item if below min item in heap or if + * we do not have a comparison function */ + if (!pq->_cmp || pq->_cmp(item, git_vector_get(pq, 0)) <= 0) return 0; /* otherwise remove the min item before inserting new */ (void)git_pqueue_pop(pq); } - if (!(error = git_vector_insert(pq, item))) + if (!(error = git_vector_insert(pq, item)) && pq->_cmp) pqueue_up(pq, pq->length - 1); return error; @@ -101,9 +103,15 @@ int git_pqueue_insert(git_pqueue *pq, void *item) void *git_pqueue_pop(git_pqueue *pq) { - void *rval = git_pqueue_get(pq, 0); + void *rval; + + if (!pq->_cmp) { + rval = git_vector_last(pq); + } else { + rval = git_pqueue_get(pq, 0); + } - if (git_pqueue_size(pq) > 1) { + if (git_pqueue_size(pq) > 1 && pq->_cmp) { /* move last item to top of heap, shrink, and push item down */ pq->contents[0] = git_vector_last(pq); git_vector_pop(pq); diff --git a/src/pqueue.h b/src/pqueue.h index da7b74edf..c0a6cd49e 100644 --- a/src/pqueue.h +++ b/src/pqueue.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_pqueue_h__ #define INCLUDE_pqueue_h__ +#include "common.h" + #include "vector.h" typedef git_vector git_pqueue; @@ -35,6 +37,7 @@ extern int git_pqueue_init( #define git_pqueue_clear git_vector_clear #define git_pqueue_size git_vector_length #define git_pqueue_get git_vector_get +#define git_pqueue_reverse git_vector_reverse /** * Insert a new item into the queue diff --git a/src/proxy.c b/src/proxy.c index f53ac1151..b07371d48 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -5,7 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "proxy.h" + #include "git2/proxy.h" int git_proxy_init_options(git_proxy_options *opts, unsigned int version) diff --git a/src/proxy.h b/src/proxy.h index bf9382737..7582301c9 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -7,8 +7,10 @@ #ifndef INCLUDE_proxy_h__ #define INCLUDE_proxy_h__ +#include "common.h" + #include "git2/proxy.h" extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src); -#endif
\ No newline at end of file +#endif diff --git a/src/push.c b/src/push.c index b4901388b..41b66df06 100644 --- a/src/push.c +++ b/src/push.c @@ -5,14 +5,14 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "push.h" + #include "git2.h" -#include "common.h" #include "pack.h" #include "pack-objects.h" #include "remote.h" #include "vector.h" -#include "push.h" #include "tree.h" static int push_spec_rref_cmp(const void *a, const void *b) @@ -90,7 +90,7 @@ static void free_refspec(push_spec *spec) static int check_rref(char *ref) { if (git__prefixcmp(ref, "refs/")) { - giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref); + giterr_set(GITERR_INVALID, "not a valid reference '%s'", ref); return -1; } @@ -111,7 +111,7 @@ static int check_lref(git_push *push, char *ref) giterr_set(GITERR_REFERENCE, "src refspec '%s' does not match any existing object", ref); else - giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref); + giterr_set(GITERR_INVALID, "not a valid reference '%s'", ref); return -1; } @@ -178,6 +178,9 @@ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks) if (!fetch_spec) continue; + /* Clear the buffer which can be dirty from previous iteration */ + git_buf_clear(&remote_ref_name); + if ((error = git_refspec_transform(&remote_ref_name, fetch_spec, status->ref)) < 0) goto on_error; @@ -321,7 +324,7 @@ static int revwalk(git_vector *commits, git_push *push) if (!git_odb_exists(push->repo->_odb, &spec->roid)) { giterr_set(GITERR_REFERENCE, - "Cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); + "cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -332,7 +335,7 @@ static int revwalk(git_vector *commits, git_push *push) if (error == GIT_ENOTFOUND || (!error && !git_oid_equal(&base, &spec->roid))) { giterr_set(GITERR_REFERENCE, - "Cannot push non-fastforwardable reference"); + "cannot push non-fastforwardable reference"); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -553,7 +556,7 @@ static int calculate_work(git_push *push) /* This is a create or update. Local ref must exist. */ if (git_reference_name_to_id( &spec->loid, push->repo, spec->refspec.src) < 0) { - giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->refspec.src); + giterr_set(GITERR_REFERENCE, "no such reference '%s'", spec->refspec.src); return -1; } } @@ -579,7 +582,7 @@ static int do_push(git_push *push, const git_remote_callbacks *callbacks) git_transport *transport = push->remote->transport; if (!transport->push) { - giterr_set(GITERR_NET, "Remote transport doesn't support push"); + giterr_set(GITERR_NET, "remote transport doesn't support push"); error = -1; goto on_error; } diff --git a/src/push.h b/src/push.h index e32ad2f4d..31ac43609 100644 --- a/src/push.h +++ b/src/push.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_push_h__ #define INCLUDE_push_h__ +#include "common.h" + #include "git2.h" #include "refspec.h" diff --git a/src/rebase.c b/src/rebase.c index 470e62a23..3be751254 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "buffer.h" #include "repository.h" #include "posix.h" @@ -92,7 +93,7 @@ static int rebase_state_type( git_buf path = GIT_BUF_INIT; git_rebase_type_t type = GIT_REBASE_TYPE_NONE; - if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0) + if (git_buf_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0) return -1; if (git_path_isdir(git_buf_cstr(&path))) { @@ -101,7 +102,7 @@ static int rebase_state_type( } git_buf_clear(&path); - if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0) + if (git_buf_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0) return -1; if (git_path_isdir(git_buf_cstr(&path))) { @@ -152,7 +153,7 @@ GIT_INLINE(int) rebase_readint( return error; if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) { - giterr_set(GITERR_REBASE, "The file '%s' contains an invalid numeric value", filename); + giterr_set(GITERR_REBASE, "the file '%s' contains an invalid numeric value", filename); return -1; } @@ -170,7 +171,7 @@ GIT_INLINE(int) rebase_readoid( return error; if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) { - giterr_set(GITERR_REBASE, "The file '%s' contains an invalid object ID", filename); + giterr_set(GITERR_REBASE, "the file '%s' contains an invalid object ID", filename); return -1; } @@ -316,7 +317,7 @@ int git_rebase_open( goto done; if (rebase->type == GIT_REBASE_TYPE_NONE) { - giterr_set(GITERR_REBASE, "There is no rebase in progress"); + giterr_set(GITERR_REBASE, "there is no rebase in progress"); error = GIT_ENOTFOUND; goto done; } @@ -372,14 +373,14 @@ int git_rebase_open( switch (rebase->type) { case GIT_REBASE_TYPE_INTERACTIVE: - giterr_set(GITERR_REBASE, "Interactive rebase is not supported"); + giterr_set(GITERR_REBASE, "interactive rebase is not supported"); error = -1; break; case GIT_REBASE_TYPE_MERGE: error = rebase_open_merge(rebase); break; case GIT_REBASE_TYPE_APPLY: - giterr_set(GITERR_REBASE, "Patch application rebase is not supported"); + giterr_set(GITERR_REBASE, "patch application rebase is not supported"); error = -1; break; default: @@ -447,8 +448,8 @@ static int rebase_setupfiles_merge(git_rebase *rebase) size_t i; int error = 0; - if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || - (error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0) + if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || + (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0) goto done; for (i = 0; i < git_array_size(rebase->operations); i++) { @@ -459,7 +460,7 @@ static int rebase_setupfiles_merge(git_rebase *rebase) git_oid_fmt(id_str, &operation->id); - if ((error = rebase_setupfile(rebase, commit_filename.ptr, -1, + if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0, "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0) goto done; } @@ -478,7 +479,7 @@ static int rebase_setupfiles(git_rebase *rebase) git_oid_fmt(orig_head, &rebase->orig_head_id); if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) { - giterr_set(GITERR_OS, "Failed to create rebase directory '%s'", rebase->state_path); + giterr_set(GITERR_OS, "failed to create rebase directory '%s'", rebase->state_path); return -1; } @@ -486,10 +487,10 @@ static int rebase_setupfiles(git_rebase *rebase) rebase->orig_head_name; if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 || - rebase_setupfile(rebase, HEAD_NAME_FILE, -1, "%s\n", orig_head_name) < 0 || - rebase_setupfile(rebase, ONTO_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || - rebase_setupfile(rebase, ORIG_HEAD_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || - rebase_setupfile(rebase, QUIET_FILE, -1, rebase->quiet ? "t\n" : "\n") < 0) + rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 || + rebase_setupfile(rebase, ONTO_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || + rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || + rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0) return -1; return rebase_setupfiles_merge(rebase); @@ -511,7 +512,7 @@ static int rebase_ensure_not_in_progress(git_repository *repo) return error; if (type != GIT_REBASE_TYPE_NONE) { - giterr_set(GITERR_REBASE, "There is an existing rebase in progress"); + giterr_set(GITERR_REBASE, "there is an existing rebase in progress"); return -1; } @@ -536,7 +537,7 @@ static int rebase_ensure_not_dirty( goto done; if (git_diff_num_deltas(diff) > 0) { - giterr_set(GITERR_REBASE, "Uncommitted changes exist in index"); + giterr_set(GITERR_REBASE, "uncommitted changes exist in index"); error = fail_with; goto done; } @@ -546,11 +547,13 @@ static int rebase_ensure_not_dirty( } if (check_workdir) { - if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0) + git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; + diff_opts.ignore_submodules = GIT_SUBMODULE_IGNORE_UNTRACKED; + if ((error = git_diff_index_to_workdir(&diff, repo, index, &diff_opts)) < 0) goto done; if (git_diff_num_deltas(diff) > 0) { - giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir"); + giterr_set(GITERR_REBASE, "unstaged changes exist in workdir"); error = fail_with; goto done; } @@ -586,7 +589,7 @@ static int rebase_init_operations( (error = git_revwalk_hide(revwalk, git_annotated_commit_id(upstream))) < 0) goto done; - git_revwalk_sorting(revwalk, GIT_SORT_REVERSE | GIT_SORT_TIME); + git_revwalk_sorting(revwalk, GIT_SORT_REVERSE); while ((error = git_revwalk_next(&id, revwalk)) == 0) { if ((error = git_commit_lookup(&commit, repo, &id)) < 0) @@ -624,13 +627,13 @@ static int rebase_init_merge( GIT_UNUSED(upstream); - if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) + if ((error = git_buf_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0) goto done; rebase->state_path = git_buf_detach(&state_path); GITERR_CHECK_ALLOC(rebase->state_path); - if (branch->ref_name) { + if (branch->ref_name && strcmp(branch->ref_name, "HEAD")) { rebase->orig_head_name = git__strdup(branch->ref_name); GITERR_CHECK_ALLOC(rebase->orig_head_name); } else { @@ -731,7 +734,7 @@ int git_rebase_init( if (inmemory) error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); else - rebase_init_merge(rebase, repo, branch ,upstream, onto); + error = rebase_init_merge(rebase, repo, branch ,upstream, onto); if (error == 0) *out = rebase; @@ -807,7 +810,7 @@ static int rebase_next_merge( goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { - giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); + giterr_set(GITERR_REBASE, "cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { @@ -821,8 +824,8 @@ static int rebase_next_merge( normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit); if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || - (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || - (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || + (error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 || + (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || @@ -864,7 +867,7 @@ static int rebase_next_inmemory( goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { - giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); + giterr_set(GITERR_REBASE, "cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { @@ -1259,7 +1262,7 @@ static int rebase_copy_notes( goto done; on_error: - giterr_set(GITERR_REBASE, "Invalid rewritten file at line %d", linenum); + giterr_set(GITERR_REBASE, "invalid rewritten file at line %d", linenum); error = -1; done: diff --git a/src/refdb.c b/src/refdb.c index debba1276..c162a153f 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -5,8 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" -#include "posix.h" +#include "refdb.h" #include "git2/object.h" #include "git2/refs.h" @@ -14,9 +13,9 @@ #include "git2/sys/refdb_backend.h" #include "hash.h" -#include "refdb.h" #include "refs.h" #include "reflog.h" +#include "posix.h" int git_refdb_new(git_refdb **out, git_repository *repo) { @@ -125,13 +124,15 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob) { + int error; + if (!db->backend || !db->backend->iterator) { - giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); + giterr_set(GITERR_REFERENCE, "this backend doesn't support iterators"); return -1; } - if (db->backend->iterator(out, db->backend, glob) < 0) - return -1; + if ((error = db->backend->iterator(out, db->backend, glob)) < 0) + return error; GIT_REFCOUNT_INC(db); (*out)->db = db; diff --git a/src/refdb.h b/src/refdb.h index 4ee3b8065..2d4ec753a 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_refdb_h__ #define INCLUDE_refdb_h__ +#include "common.h" + #include "git2/refdb.h" #include "repository.h" diff --git a/src/refdb_fs.c b/src/refdb_fs.c index f978038e6..ade734c6a 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "refdb_fs.h" + #include "refs.h" #include "hash.h" #include "repository.h" @@ -13,7 +15,6 @@ #include "pack.h" #include "reflog.h" #include "refdb.h" -#include "refdb_fs.h" #include "iterator.h" #include "sortedcache.h" #include "signature.h" @@ -26,8 +27,6 @@ #include <git2/sys/refs.h> #include <git2/sys/reflog.h> -GIT__USE_STRMAP - #define DEFAULT_NESTING_LEVEL 5 #define MAX_NESTING_LEVEL 10 @@ -55,12 +54,16 @@ typedef struct refdb_fs_backend { git_refdb_backend parent; git_repository *repo; - char *path; + /* path to git directory */ + char *gitpath; + /* path to common objects' directory */ + char *commonpath; git_sortedcache *refcache; int peeling_mode; git_iterator_flag_t iterator_flags; uint32_t direach_flags; + int fsync; } refdb_fs_backend; static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name); @@ -77,7 +80,7 @@ static int packed_reload(refdb_fs_backend *backend) git_buf packedrefs = GIT_BUF_INIT; char *scan, *eof, *eol; - if (!backend->path) + if (!backend->gitpath) return 0; error = git_sortedcache_lockandload(backend->refcache, &packedrefs); @@ -185,7 +188,7 @@ static int packed_reload(refdb_fs_backend *backend) return 0; parse_failed: - giterr_set(GITERR_REFERENCE, "Corrupted packed references file"); + giterr_set(GITERR_REFERENCE, "corrupted packed references file"); git_sortedcache_clear(backend->refcache, false); git_sortedcache_wunlock(backend->refcache); @@ -212,7 +215,7 @@ static int loose_parse_oid( return 0; corrupted: - giterr_set(GITERR_REFERENCE, "Corrupted loose reference file: %s", filename); + giterr_set(GITERR_REFERENCE, "corrupted loose reference file: %s", filename); return -1; } @@ -238,7 +241,7 @@ static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name) /* if we fail to load the loose reference, assume someone changed * the filesystem under us and skip it... */ - if (loose_readbuffer(&ref_file, backend->path, name) < 0) { + if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) { giterr_clear(); goto done; } @@ -287,7 +290,7 @@ static int _dirent_loose_load(void *payload, git_buf *full_path) return error; } - file_path = full_path->ptr + strlen(backend->path); + file_path = full_path->ptr + strlen(backend->gitpath); return loose_lookup_to_packfile(backend, file_path); } @@ -303,7 +306,7 @@ static int packed_loadloose(refdb_fs_backend *backend) int error; git_buf refs_path = GIT_BUF_INIT; - if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) + if (git_buf_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0) return -1; /* @@ -326,12 +329,13 @@ static int refdb_fs_backend__exists( { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; git_buf ref_path = GIT_BUF_INIT; + int error; assert(backend); - if (packed_reload(backend) < 0 || - git_buf_joinpath(&ref_path, backend->path, ref_name) < 0) - return -1; + if ((error = packed_reload(backend)) < 0 || + (error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0) + return error; *exists = git_path_isfile(ref_path.ptr) || (git_sortedcache_lookup(backend->refcache, ref_name) != NULL); @@ -348,7 +352,7 @@ static const char *loose_parse_symbolic(git_buf *file_content) refname_start = (const char *)file_content->ptr; if (git_buf_len(file_content) < header_len + 1) { - giterr_set(GITERR_REFERENCE, "Corrupted loose reference file"); + giterr_set(GITERR_REFERENCE, "corrupted loose reference file"); return NULL; } @@ -361,6 +365,19 @@ static const char *loose_parse_symbolic(git_buf *file_content) return refname_start; } +/* + * Returns whether a reference is stored per worktree or not. + * Per-worktree references are: + * + * - all pseudorefs, e.g. HEAD and MERGE_HEAD + * - all references stored inside of "refs/bisect/" + */ +static bool is_per_worktree_ref(const char *ref_name) +{ + return git__prefixcmp(ref_name, "refs/") != 0 || + git__prefixcmp(ref_name, "refs/bisect/") == 0; +} + static int loose_lookup( git_reference **out, refdb_fs_backend *backend, @@ -368,11 +385,17 @@ static int loose_lookup( { git_buf ref_file = GIT_BUF_INIT; int error = 0; + const char *ref_dir; if (out) *out = NULL; - if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0) + if (is_per_worktree_ref(ref_name)) + ref_dir = backend->gitpath; + else + ref_dir = backend->commonpath; + + if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0) /* cannot read loose ref file - gah */; else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) { const char *target; @@ -397,7 +420,7 @@ static int loose_lookup( static int ref_error_notfound(const char *name) { - giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name); + giterr_set(GITERR_REFERENCE, "reference '%s' not found", name); return GIT_ENOTFOUND; } @@ -409,8 +432,8 @@ static int packed_lookup( int error = 0; struct packref *entry; - if (packed_reload(backend) < 0) - return -1; + if ((error = packed_reload(backend)) < 0) + return error; if (git_sortedcache_rlock(backend->refcache) < 0) return -1; @@ -483,12 +506,12 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry = NULL; - if (!backend->path) /* do nothing if no path for loose refs */ + if (!backend->commonpath) /* do nothing if no commonpath for loose refs */ return 0; fsit_opts.flags = backend->iterator_flags; - if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 || + if ((error = git_buf_printf(&path, "%s/refs", backend->commonpath)) < 0 || (error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) { git_buf_free(&path); return error; @@ -615,13 +638,14 @@ static int refdb_fs_backend__iterator_next_name( static int refdb_fs_backend__iterator( git_reference_iterator **out, git_refdb_backend *_backend, const char *glob) { + int error; refdb_fs_iter *iter; refdb_fs_backend *backend = (refdb_fs_backend *)_backend; assert(backend); - if (packed_reload(backend) < 0) - return -1; + if ((error = packed_reload(backend)) < 0) + return error; iter = git__calloc(1, sizeof(refdb_fs_iter)); GITERR_CHECK_ALLOC(iter); @@ -674,20 +698,22 @@ static int reference_path_available( int force) { size_t i; + int error; - if (packed_reload(backend) < 0) - return -1; + if ((error = packed_reload(backend)) < 0) + return error; if (!force) { int exists; - if (refdb_fs_backend__exists( - &exists, (git_refdb_backend *)backend, new_ref) < 0) - return -1; + if ((error = refdb_fs_backend__exists( + &exists, (git_refdb_backend *)backend, new_ref)) < 0) { + return error; + } if (exists) { giterr_set(GITERR_REFERENCE, - "Failed to write reference '%s': a reference with " + "failed to write reference '%s': a reference with " "that name already exists.", new_ref); return GIT_EEXISTS; } @@ -701,7 +727,7 @@ static int reference_path_available( if (ref && !ref_is_available(old_ref, new_ref, ref->name)) { git_sortedcache_runlock(backend->refcache); giterr_set(GITERR_REFERENCE, - "Path to reference '%s' collides with existing one", new_ref); + "path to reference '%s' collides with existing one", new_ref); return -1; } } @@ -712,26 +738,36 @@ static int reference_path_available( static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name) { - int error; + int error, filebuf_flags; git_buf ref_path = GIT_BUF_INIT; + const char *basedir; assert(file && backend && name); if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { - giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name); + giterr_set(GITERR_INVALID, "invalid reference name '%s'", name); return GIT_EINVALIDSPEC; } + if (is_per_worktree_ref(name)) + basedir = backend->gitpath; + else + basedir = backend->commonpath; + /* Remove a possibly existing empty directory hierarchy * which name would collide with the reference name */ - if (git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0) - return -1; + if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0) + return error; - if (git_buf_joinpath(&ref_path, backend->path, name) < 0) + if (git_buf_joinpath(&ref_path, basedir, name) < 0) return -1; - error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE); + filebuf_flags = GIT_FILEBUF_FORCE; + if (backend->fsync) + filebuf_flags |= GIT_FILEBUF_FSYNC; + + error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE); if (error == GIT_EDIRECTORY) giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name); @@ -901,40 +937,62 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file) static int packed_remove_loose(refdb_fs_backend *backend) { size_t i; - git_buf full_path = GIT_BUF_INIT; - int failed = 0; + git_filebuf lock = GIT_FILEBUF_INIT; + git_buf ref_content = GIT_BUF_INIT; + int error = 0; /* backend->refcache is already locked when this is called */ for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) { struct packref *ref = git_sortedcache_entry(backend->refcache, i); + git_oid current_id; if (!ref || !(ref->flags & PACKREF_WAS_LOOSE)) continue; - if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0) - return -1; /* critical; do not try to recover on oom */ + git_filebuf_cleanup(&lock); - if (git_path_exists(full_path.ptr) && p_unlink(full_path.ptr) < 0) { - if (failed) - continue; + /* We need to stop anybody from updating the ref while we try to do a safe delete */ + error = loose_lock(&lock, backend, ref->name); + /* If someone else is updating it, let them do it */ + if (error == GIT_EEXISTS || error == GIT_ENOTFOUND) + continue; - giterr_set(GITERR_REFERENCE, - "Failed to remove loose reference '%s' after packing: %s", - full_path.ptr, strerror(errno)); - failed = 1; + if (error < 0) { + git_buf_free(&ref_content); + giterr_set(GITERR_REFERENCE, "failed to lock loose reference '%s'", ref->name); + return error; } + error = git_futils_readbuffer(&ref_content, lock.path_original); + /* Someone else beat us to cleaning up the ref, let's simply continue */ + if (error == GIT_ENOTFOUND) + continue; + + /* This became a symref between us packing and trying to delete it, so ignore it */ + if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF)) + continue; + + /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */ + if (loose_parse_oid(¤t_id, lock.path_original, &ref_content) < 0) + continue; + + /* If the ref moved since we packed it, we must not delete it */ + if (!git_oid_equal(¤t_id, &ref->oid)) + continue; + /* * if we fail to remove a single file, this is *not* good, * but we should keep going and remove as many as possible. - * After we've removed as many files as possible, we return - * the error code anyway. + * If we fail to remove, the ref is still in the old state, so + * we haven't lost information. */ + p_unlink(lock.path_original); } - git_buf_free(&full_path); - return failed ? -1 : 0; + git_buf_free(&ref_content); + git_filebuf_cleanup(&lock); + return 0; } /* @@ -944,41 +1002,45 @@ static int packed_write(refdb_fs_backend *backend) { git_sortedcache *refcache = backend->refcache; git_filebuf pack_file = GIT_FILEBUF_INIT; + int error, open_flags = 0; size_t i; /* lock the cache to updates while we do this */ - if (git_sortedcache_wlock(refcache) < 0) - return -1; + if ((error = git_sortedcache_wlock(refcache)) < 0) + return error; + + if (backend->fsync) + open_flags = GIT_FILEBUF_FSYNC; /* Open the file! */ - if (git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE) < 0) + if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0) goto fail; /* Packfiles have a header... apparently * This is in fact not required, but we might as well print it * just for kicks */ - if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0) + if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0) goto fail; for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) { struct packref *ref = git_sortedcache_entry(refcache, i); assert(ref); - if (packed_find_peel(backend, ref) < 0) + if ((error = packed_find_peel(backend, ref)) < 0) goto fail; - if (packed_write_ref(ref, &pack_file) < 0) + if ((error = packed_write_ref(ref, &pack_file)) < 0) goto fail; } /* if we've written all the references properly, we can commit * the packfile to make the changes effective */ - if (git_filebuf_commit(&pack_file) < 0) + if ((error = git_filebuf_commit(&pack_file)) < 0) goto fail; /* when and only when the packfile has been properly written, * we can go ahead and remove the loose refs */ - if (packed_remove_loose(backend) < 0) + if ((error = packed_remove_loose(backend)) < 0) goto fail; git_sortedcache_updated(refcache); @@ -991,7 +1053,7 @@ fail: git_filebuf_cleanup(&pack_file); git_sortedcache_wunlock(refcache); - return -1; + return error; } static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message); @@ -1079,7 +1141,7 @@ out: static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message) { int error; - git_oid old_id = {{0}}; + git_oid old_id; git_reference *tmp = NULL, *head = NULL, *peeled = NULL; const char *name; @@ -1087,7 +1149,8 @@ static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref return 0; /* if we can't resolve, we use {0}*40 as old id */ - git_reference_name_to_id(&old_id, backend->repo, ref->name); + if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0) + memset(&old_id, 0, sizeof(old_id)); if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0) return error; @@ -1143,8 +1206,7 @@ static int refdb_fs_backend__write( assert(backend); - error = reference_path_available(backend, ref->name, NULL, force); - if (error < 0) + if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0) return error; /* We need to perform the reflog append and old value check under the ref's lock */ @@ -1257,18 +1319,17 @@ static int refdb_fs_backend__delete_tail( } /* If a loose reference exists, remove it from the filesystem */ - if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0) + if (git_buf_joinpath(&loose_path, backend->gitpath, ref_name) < 0) return -1; - if (git_path_isfile(loose_path.ptr)) { - error = p_unlink(loose_path.ptr); - loose_deleted = 1; - } - - git_buf_free(&loose_path); - if (error != 0) + error = p_unlink(loose_path.ptr); + if (error < 0 && errno == ENOENT) + error = 0; + else if (error < 0) goto cleanup; + else if (error == 0) + loose_deleted = 1; if ((error = packed_reload(backend)) < 0) goto cleanup; @@ -1291,6 +1352,7 @@ static int refdb_fs_backend__delete_tail( error = packed_write(backend); cleanup: + git_buf_free(&loose_path); git_filebuf_cleanup(file); return error; @@ -1362,14 +1424,15 @@ static int refdb_fs_backend__rename( static int refdb_fs_backend__compress(git_refdb_backend *_backend) { + int error; refdb_fs_backend *backend = (refdb_fs_backend *)_backend; assert(backend); - if (packed_reload(backend) < 0 || /* load the existing packfile */ - packed_loadloose(backend) < 0 || /* add all the loose refs */ - packed_write(backend) < 0) /* write back to disk */ - return -1; + if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */ + (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */ + (error = packed_write(backend)) < 0) /* write back to disk */ + return error; return 0; } @@ -1381,28 +1444,30 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) assert(backend); git_sortedcache_free(backend->refcache); - git__free(backend->path); + git__free(backend->gitpath); + git__free(backend->commonpath); git__free(backend); } -static int setup_namespace(git_buf *path, git_repository *repo) +static char *setup_namespace(git_repository *repo, const char *in) { - char *parts, *start, *end; + git_buf path = GIT_BUF_INIT; + char *parts, *start, *end, *out = NULL; - /* Not all repositories have a path */ - if (repo->path_repository == NULL) - return 0; + if (!in) + goto done; - /* Load the path to the repo first */ - git_buf_puts(path, repo->path_repository); + git_buf_puts(&path, in); /* if the repo is not namespaced, nothing else to do */ - if (repo->namespace == NULL) - return 0; + if (repo->namespace == NULL) { + out = git_buf_detach(&path); + goto done; + } parts = end = git__strdup(repo->namespace); if (parts == NULL) - return -1; + goto done; /* * From `man gitnamespaces`: @@ -1410,21 +1475,24 @@ static int setup_namespace(git_buf *path, git_repository *repo) * of namespaces; for example, GIT_NAMESPACE=foo/bar will store * refs under refs/namespaces/foo/refs/namespaces/bar/ */ - while ((start = git__strsep(&end, "/")) != NULL) { - git_buf_printf(path, "refs/namespaces/%s/", start); - } + while ((start = git__strsep(&end, "/")) != NULL) + git_buf_printf(&path, "refs/namespaces/%s/", start); - git_buf_printf(path, "refs/namespaces/%s/refs", end); + git_buf_printf(&path, "refs/namespaces/%s/refs", end); git__free(parts); /* Make sure that the folder with the namespace exists */ - if (git_futils_mkdir_relative(git_buf_cstr(path), repo->path_repository, - 0777, GIT_MKDIR_PATH, NULL) < 0) - return -1; + if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777, + GIT_MKDIR_PATH, NULL) < 0) + goto done; - /* Return root of the namespaced path, i.e. without the trailing '/refs' */ - git_buf_rtruncate_at_char(path, '/'); - return 0; + /* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */ + git_buf_rtruncate_at_char(&path, '/'); + out = git_buf_detach(&path); + +done: + git_buf_free(&path); + return out; } static int reflog_alloc(git_reflog **reflog, const char *name) @@ -1457,7 +1525,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) #define seek_forward(_increase) do { \ if (_increase >= buf_size) { \ - giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \ + giterr_set(GITERR_INVALID, "ran out of data while parsing reflog"); \ goto fail; \ } \ buf += _increase; \ @@ -1535,7 +1603,7 @@ static int create_new_reflog_file(const char *filepath) GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name) { - return git_buf_join3(path, '/', repo->path_repository, GIT_REFLOG_DIR, name); + return git_buf_join3(path, '/', repo->commondir, GIT_REFLOG_DIR, name); } static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name) @@ -1673,7 +1741,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char repo = backend->repo; if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { - giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname); + giterr_set(GITERR_INVALID, "invalid reference name '%s'", refname); return GIT_EINVALIDSPEC; } @@ -1682,7 +1750,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char if (!git_path_isfile(git_buf_cstr(&log_path))) { giterr_set(GITERR_INVALID, - "Log file for reference '%s' doesn't exist.", refname); + "log file for reference '%s' doesn't exist", refname); error = -1; goto cleanup; } @@ -1734,7 +1802,7 @@ success: /* Append to the reflog, must be called under reference lock */ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message) { - int error, is_symbolic; + int error, is_symbolic, open_flags; git_oid old_id = {{0}}, new_id = {{0}}; git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; git_repository *repo = backend->repo; @@ -1789,9 +1857,10 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co * there maybe an obsolete/unused directory (or directory hierarchy) in the way. */ if (git_path_isdir(git_buf_cstr(&path))) { - if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) - error = -1; - else if (git_path_isdir(git_buf_cstr(&path))) { + if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) { + if (error == GIT_ENOTFOUND) + error = 0; + } else if (git_path_isdir(git_buf_cstr(&path))) { giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder", ref->name); error = GIT_EDIRECTORY; @@ -1801,7 +1870,12 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co goto cleanup; } - error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE); + open_flags = O_WRONLY | O_CREAT | O_APPEND; + + if (backend->fsync) + open_flags |= O_FSYNC; + + error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE); cleanup: git_buf_free(&buf); @@ -1829,7 +1903,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0) return error; - if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0) + if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0) return -1; if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0) @@ -1861,7 +1935,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ p_close(fd); if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) { - giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name); + giterr_set(GITERR_OS, "failed to rename reflog for %s", new_name); error = -1; goto cleanup; } @@ -1878,7 +1952,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ } if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) { - giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name); + giterr_set(GITERR_OS, "failed to rename reflog for %s", new_name); error = -1; } @@ -1920,7 +1994,7 @@ int git_refdb_backend_fs( git_repository *repository) { int t = 0; - git_buf path = GIT_BUF_INIT; + git_buf gitpath = GIT_BUF_INIT; refdb_fs_backend *backend; backend = git__calloc(1, sizeof(refdb_fs_backend)); @@ -1928,18 +2002,27 @@ int git_refdb_backend_fs( backend->repo = repository; - if (setup_namespace(&path, repository) < 0) - goto fail; + if (repository->gitdir) { + backend->gitpath = setup_namespace(repository, repository->gitdir); + + if (backend->gitpath == NULL) + goto fail; + } - backend->path = git_buf_detach(&path); + if (repository->commondir) { + backend->commonpath = setup_namespace(repository, repository->commondir); - if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 || + if (backend->commonpath == NULL) + goto fail; + } + + if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 || git_sortedcache_new( &backend->refcache, offsetof(struct packref, name), - NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0) + NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0) goto fail; - git_buf_free(&path); + git_buf_free(&gitpath); if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) { backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE; @@ -1949,6 +2032,9 @@ int git_refdb_backend_fs( backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE; backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE; } + if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) || + git_repository__fsync_gitdir) + backend->fsync = 1; backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; @@ -1971,8 +2057,9 @@ int git_refdb_backend_fs( return 0; fail: - git_buf_free(&path); - git__free(backend->path); + git_buf_free(&gitpath); + git__free(backend->gitpath); + git__free(backend->commonpath); git__free(backend); return -1; } diff --git a/src/refdb_fs.h b/src/refdb_fs.h index 79e296833..0c84814df 100644 --- a/src/refdb_fs.h +++ b/src/refdb_fs.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_refdb_fs_h__ #define INCLUDE_refdb_fs_h__ +#include "common.h" + +#include "strmap.h" + typedef struct { git_strmap *packfile; time_t packfile_time; diff --git a/src/reflog.c b/src/reflog.c index 9ce9aee6f..938999218 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -6,6 +6,7 @@ */ #include "reflog.h" + #include "repository.h" #include "filebuf.h" #include "signature.h" @@ -93,7 +94,7 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_sign if (newline) { if (newline[1] != '\0') { - giterr_set(GITERR_INVALID, "Reflog message cannot contain newline"); + giterr_set(GITERR_INVALID, "reflog message cannot contain newline"); goto cleanup; } @@ -193,7 +194,7 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry) entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx); if (entry == NULL) { - giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx); + giterr_set(GITERR_REFERENCE, "no reflog entry at index %"PRIuZ, idx); return GIT_ENOTFOUND; } diff --git a/src/reflog.h b/src/reflog.h index 2d31ae47d..d54b4cde8 100644 --- a/src/reflog.h +++ b/src/reflog.h @@ -8,6 +8,7 @@ #define INCLUDE_reflog_h__ #include "common.h" + #include "git2/reflog.h" #include "vector.h" diff --git a/src/refs.c b/src/refs.c index bff443ac9..942054001 100644 --- a/src/refs.c +++ b/src/refs.c @@ -6,6 +6,7 @@ */ #include "refs.h" + #include "hash.h" #include "repository.h" #include "fileops.h" @@ -24,7 +25,7 @@ #include <git2/signature.h> #include <git2/commit.h> -GIT__USE_STRMAP +bool git_reference__enable_symbolic_ref_target_validation = true; #define DEFAULT_NESTING_LEVEL 5 #define MAX_NESTING_LEVEL 10 @@ -178,7 +179,8 @@ int git_reference_name_to_id( static int reference_normalize_for_repo( git_refname_t out, git_repository *repo, - const char *name) + const char *name, + bool validate) { int precompose; unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL; @@ -187,6 +189,9 @@ static int reference_normalize_for_repo( precompose) flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE; + if (!validate) + flags |= GIT_REF_FORMAT__VALIDATION_DISABLE; + return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags); } @@ -213,7 +218,7 @@ int git_reference_lookup_resolved( scan_type = GIT_REF_SYMBOLIC; - if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0) + if ((error = reference_normalize_for_repo(scan_name, repo, name, true)) < 0) return error; if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) @@ -236,7 +241,7 @@ int git_reference_lookup_resolved( if (scan_type != GIT_REF_OID && max_nesting != 0) { giterr_set(GITERR_REFERENCE, - "Cannot resolve reference (>%u levels deep)", max_nesting); + "cannot resolve reference (>%u levels deep)", max_nesting); git_reference_free(ref); return -1; } @@ -245,6 +250,40 @@ int git_reference_lookup_resolved( return 0; } +int git_reference__read_head( + git_reference **out, + git_repository *repo, + const char *path) +{ + git_buf reference = GIT_BUF_INIT; + char *name = NULL; + int error; + + if ((error = git_futils_readbuffer(&reference, path)) < 0) + goto out; + git_buf_rtrim(&reference); + + if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) == 0) { + git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF)); + + name = git_path_basename(path); + + if ((*out = git_reference__alloc_symbolic(name, reference.ptr)) == NULL) { + error = -1; + goto out; + } + } else { + if ((error = git_reference_lookup(out, repo, reference.ptr)) < 0) + goto out; + } + +out: + git__free(name); + git_buf_free(&reference); + + return error; +} + int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname) { int error = 0, i; @@ -298,7 +337,7 @@ cleanup: if (error && !foundvalid) { /* never found a valid reference name */ giterr_set(GITERR_REFERENCE, - "Could not use '%s' as valid reference name", git_buf_cstr(&name)); + "could not use '%s' as valid reference name", git_buf_cstr(&name)); } if (error == GIT_ENOTFOUND) @@ -383,7 +422,7 @@ static int reference__create( if (ref_out) *ref_out = NULL; - error = reference_normalize_for_repo(normalized, repo, name); + error = reference_normalize_for_repo(normalized, repo, name, true); if (error < 0) return error; @@ -396,7 +435,7 @@ static int reference__create( if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) { giterr_set(GITERR_REFERENCE, - "Target OID for the reference doesn't exist on the repository"); + "target OID for the reference doesn't exist on the repository"); return -1; } @@ -404,7 +443,10 @@ static int reference__create( } else { git_refname_t normalized_target; - if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0) + error = reference_normalize_for_repo(normalized_target, repo, + symbolic, git_reference__enable_symbolic_ref_target_validation); + + if (error < 0) return error; ref = git_reference__alloc_symbolic(normalized, normalized_target); @@ -524,7 +566,7 @@ static int ensure_is_an_updatable_direct_reference(git_reference *ref) if (ref->type == GIT_REF_OID) return 0; - giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); + giterr_set(GITERR_REFERENCE, "cannot set OID on symbolic reference"); return -1; } @@ -552,7 +594,7 @@ static int ensure_is_an_updatable_symbolic_reference(git_reference *ref) if (ref->type == GIT_REF_SYMBOLIC) return 0; - giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference"); + giterr_set(GITERR_REFERENCE, "cannot set symbolic target on a direct reference"); return -1; } @@ -573,20 +615,63 @@ int git_reference_symbolic_set_target( out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message); } +typedef struct { + const char *old_name; + git_refname_t new_name; +} rename_cb_data; + +static int update_wt_heads(git_repository *repo, const char *path, void *payload) +{ + rename_cb_data *data = (rename_cb_data *) payload; + git_reference *head = NULL; + char *gitdir = NULL; + int error; + + if ((error = git_reference__read_head(&head, repo, path)) < 0) { + giterr_set(GITERR_REFERENCE, "could not read HEAD when renaming references"); + goto out; + } + + if ((gitdir = git_path_dirname(path)) == NULL) { + error = -1; + goto out; + } + + if (git_reference_type(head) != GIT_REF_SYMBOLIC || + git__strcmp(head->target.symbolic, data->old_name) != 0) { + error = 0; + goto out; + } + + /* Update HEAD it was pointing to the reference being renamed */ + if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) { + giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference"); + goto out; + } + +out: + git_reference_free(head); + git__free(gitdir); + + return error; +} + static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force, const git_signature *signature, const char *message) { + git_repository *repo; git_refname_t normalized; bool should_head_be_updated = false; int error = 0; assert(ref && new_name && signature); + repo = git_reference_owner(ref); + if ((error = reference_normalize_for_repo( - normalized, git_reference_owner(ref), new_name)) < 0) + normalized, repo, new_name, true)) < 0) return error; - /* Check if we have to update HEAD. */ if ((error = git_branch_is_head(ref)) < 0) return error; @@ -596,14 +681,18 @@ static int reference__rename(git_reference **out, git_reference *ref, const char if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0) return error; - /* Update HEAD it was pointing to the reference being renamed */ - if (should_head_be_updated && - (error = git_repository_set_head(ref->db->repo, normalized)) < 0) { - giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); - return error; + /* Update HEAD if it was pointing to the reference being renamed */ + if (should_head_be_updated) { + error = git_repository_set_head(ref->db->repo, normalized); + } else { + rename_cb_data payload; + payload.old_name = ref->name; + memcpy(&payload.new_name, &normalized, sizeof(normalized)); + + error = git_repository_foreach_head(repo, update_wt_heads, &payload); } - return 0; + return error; } @@ -636,7 +725,7 @@ int git_reference_resolve(git_reference **ref_out, const git_reference *ref) return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1); default: - giterr_set(GITERR_REFERENCE, "Invalid reference"); + giterr_set(GITERR_REFERENCE, "invalid reference"); return -1; } } @@ -876,6 +965,7 @@ int git_reference__normalize_name( int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); + bool validate = (flags & GIT_REF_FORMAT__VALIDATION_DISABLE) == 0; #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; @@ -886,7 +976,7 @@ int git_reference__normalize_name( process_flags = flags; current = (char *)name; - if (*current == '/') + if (validate && *current == '/') goto cleanup; if (normalize) @@ -902,6 +992,13 @@ int git_reference__normalize_name( } #endif + if (!validate) { + git_buf_sets(buf, current); + + error = git_buf_oom(buf) ? -1 : 0; + goto cleanup; + } + while (true) { segment_len = ensure_segment_validity(current); if (segment_len < 0) { @@ -973,7 +1070,7 @@ cleanup: if (error == GIT_EINVALIDSPEC) giterr_set( GITERR_REFERENCE, - "The given reference name '%s' is not valid", name); + "the given reference name '%s' is not valid", name); if (error && normalize) git_buf_free(buf); @@ -1000,7 +1097,7 @@ int git_reference_normalize_name( if (git_buf_len(&buf) > buffer_size - 1) { giterr_set( GITERR_REFERENCE, - "The provided buffer is too short to hold the normalization of '%s'", name); + "the provided buffer is too short to hold the normalization of '%s'", name); error = GIT_EBUFS; goto cleanup; } @@ -1046,7 +1143,7 @@ static int get_terminal(git_reference **out, git_repository *repo, const char *r int error = 0; if (nesting > MAX_NESTING_LEVEL) { - giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting); + giterr_set(GITERR_REFERENCE, "reference chain too deep (%d)", nesting); return GIT_ENOTFOUND; } @@ -1115,6 +1212,18 @@ int git_reference__update_terminal( return error; } +static const char *commit_type(const git_commit *commit) +{ + unsigned int count = git_commit_parentcount(commit); + + if (count >= 2) + return " (merge)"; + else if (count == 0) + return " (initial)"; + else + return ""; +} + int git_reference__update_for_commit( git_repository *repo, git_reference *ref, @@ -1131,7 +1240,7 @@ int git_reference__update_for_commit( if ((error = git_commit_lookup(&commit, repo, id)) < 0 || (error = git_buf_printf(&reflog_msg, "%s%s: %s", operation ? operation : "commit", - git_commit_parentcount(commit) == 0 ? " (initial)" : "", + commit_type(commit), git_commit_summary(commit))) < 0) goto done; @@ -1229,7 +1338,7 @@ static int peel_error(int error, git_reference *ref, const char* msg) { giterr_set( GITERR_INVALID, - "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg); + "the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg); return error; } diff --git a/src/refs.h b/src/refs.h index fda9532de..7a1a9aed1 100644 --- a/src/refs.h +++ b/src/refs.h @@ -8,6 +8,7 @@ #define INCLUDE_refs_h__ #include "common.h" + #include "git2/oid.h" #include "git2/refs.h" #include "git2/refdb.h" @@ -15,6 +16,8 @@ #include "buffer.h" #include "oid.h" +extern bool git_reference__enable_symbolic_ref_target_validation; + #define GIT_REFS_DIR "refs/" #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" @@ -53,6 +56,7 @@ #define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE #define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16) +#define GIT_REF_FORMAT__VALIDATION_DISABLE (1u << 15) #define GIT_REFNAME_MAX 1024 @@ -104,6 +108,20 @@ int git_reference_lookup_resolved( const char *name, int max_deref); +/** + * Read reference from a file. + * + * This function will read in the file at `path`. If it is a + * symref, it will return a new unresolved symbolic reference + * with the given name pointing to the reference pointed to by + * the file. If it is not a symbolic reference, it will return + * the resolved reference. + */ +int git_reference__read_head( + git_reference **out, + git_repository *repo, + const char *path); + int git_reference__log_signature(git_signature **out, git_repository *repo); /** Update a reference after a commit. */ diff --git a/src/refspec.c b/src/refspec.c index d200e5609..01a77c97f 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -5,10 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "refspec.h" + #include "git2/errors.h" -#include "common.h" -#include "refspec.h" #include "util.h" #include "posix.h" #include "refs.h" diff --git a/src/refspec.h b/src/refspec.h index 9a87c97a5..fd2d8b312 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_refspec_h__ #define INCLUDE_refspec_h__ +#include "common.h" + #include "git2/refspec.h" #include "buffer.h" #include "vector.h" diff --git a/src/remote.c b/src/remote.c index c1d7d59ea..303911760 100644 --- a/src/remote.c +++ b/src/remote.c @@ -5,15 +5,15 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "remote.h" + #include "git2/config.h" #include "git2/types.h" #include "git2/oid.h" #include "git2/net.h" -#include "common.h" #include "config.h" #include "repository.h" -#include "remote.h" #include "fetch.h" #include "refs.h" #include "refspec.h" @@ -192,15 +192,15 @@ static int canonicalize_url(git_buf *out, const char *in) static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) { git_remote *remote; - git_config *config = NULL; + git_config *config_ro = NULL, *config_rw; git_buf canonical_url = GIT_BUF_INIT; git_buf var = GIT_BUF_INIT; int error = -1; - /* name is optional */ - assert(out && repo && url); + /* repo, name, and fetch are optional */ + assert(out && url); - if ((error = git_repository_config__weakptr(&config, repo)) < 0) + if (repo && (error = git_repository_config_snapshot(&config_ro, repo)) < 0) return error; remote = git__calloc(1, sizeof(git_remote)); @@ -212,7 +212,12 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; - remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH); + if (repo) { + remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); + } else { + remote->url = git__strdup(canonical_url.ptr); + } + GITERR_CHECK_ALLOC(remote->url); if (name != NULL) { remote->name = git__strdup(name); @@ -221,7 +226,9 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0) goto on_error; - if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0) + if (repo && + ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 || + (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)) goto on_error; } @@ -233,10 +240,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0) goto on_error; - if ((error = git_repository_config_snapshot(&config, repo)) < 0) - goto on_error; - - if ((error = lookup_remote_prune_config(remote, config, name)) < 0) + if (repo && (error = lookup_remote_prune_config(remote, config_ro, name)) < 0) goto on_error; /* Move the data over to where the matching functions can find them */ @@ -260,7 +264,7 @@ on_error: if (error) git_remote_free(remote); - git_config_free(config); + git_config_free(config_ro); git_buf_free(&canonical_url); git_buf_free(&var); return error; @@ -283,7 +287,7 @@ static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) giterr_set( GITERR_CONFIG, - "Remote '%s' already exists.", name); + "remote '%s' already exists", name); return GIT_EEXISTS; } @@ -331,6 +335,11 @@ int git_remote_create_anonymous(git_remote **out, git_repository *repo, const ch return create_internal(out, repo, NULL, url, NULL); } +int git_remote_create_detached(git_remote **out, const char *url) +{ + return create_internal(out, NULL, NULL, url, NULL); +} + int git_remote_dup(git_remote **dest, git_remote *source) { size_t i; @@ -476,7 +485,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) if (!optional_setting_found) { error = GIT_ENOTFOUND; - giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name); + giterr_set(GITERR_CONFIG, "remote '%s' does not exist", name); goto cleanup; } @@ -675,7 +684,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re url = git_remote__urlfordirection(remote, direction); if (url == NULL) { giterr_set(GITERR_INVALID, - "Malformed remote '%s' - missing URL", remote->name); + "Malformed remote '%s' - missing %s URL", + remote->name ? remote->name : "(anonymous)", + direction == GIT_DIRECTION_FETCH ? "fetch" : "push"); return -1; } @@ -770,8 +781,12 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur goto found; } - /* HTTP_PROXY / HTTPS_PROXY environment variables */ - error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); + /* http_proxy / https_proxy environment variables */ + error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy"); + + /* try uppercase environment variables */ + if (error == GIT_ENOTFOUND) + error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); if (error < 0) { if (error == GIT_ENOTFOUND) { @@ -856,6 +871,11 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const assert(remote); + if (!remote->repo) { + giterr_set(GITERR_INVALID, "cannot download detached remote"); + return -1; + } + if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; @@ -1718,7 +1738,7 @@ int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_ error = 0; break; default: - giterr_set(GITERR_INVALID, "Invalid value for the tagopt setting"); + giterr_set(GITERR_INVALID, "invalid value for the tagopt setting"); error = -1; } @@ -2341,6 +2361,11 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi assert(remote); + if (!remote->repo) { + giterr_set(GITERR_INVALID, "cannot download detached remote"); + return -1; + } + if (opts) { cbs = &opts->callbacks; custom_headers = &opts->custom_headers; @@ -2400,6 +2425,13 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ const git_strarray *custom_headers = NULL; const git_proxy_options *proxy = NULL; + assert(remote); + + if (!remote->repo) { + giterr_set(GITERR_INVALID, "cannot download detached remote"); + return -1; + } + if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; @@ -2408,7 +2440,7 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ proxy = &opts->proxy_opts; } - assert(remote && refspecs); + assert(remote); if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) return error; diff --git a/src/remote.h b/src/remote.h index e696997f4..a94481f25 100644 --- a/src/remote.h +++ b/src/remote.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_remote_h__ #define INCLUDE_remote_h__ +#include "common.h" + #include "git2/remote.h" #include "git2/transport.h" #include "git2/sys/transport.h" diff --git a/src/repository.c b/src/repository.c index cf3d18a2d..fe549e6e8 100644 --- a/src/repository.c +++ b/src/repository.c @@ -4,6 +4,9 @@ * 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 "repository.h" + #include <ctype.h> #include "git2/object.h" @@ -11,7 +14,6 @@ #include "git2/sys/repository.h" #include "common.h" -#include "repository.h" #include "commit.h" #include "tag.h" #include "blob.h" @@ -27,13 +29,43 @@ #include "merge.h" #include "diff_driver.h" #include "annotated_commit.h" +#include "submodule.h" +#include "worktree.h" + +#include "strmap.h" #ifdef GIT_WIN32 # include "win32/w32_util.h" #endif +bool git_repository__fsync_gitdir = false; + +static const struct { + git_repository_item_t parent; + const char *name; + bool directory; +} items[] = { + { GIT_REPOSITORY_ITEM_GITDIR, NULL, true }, + { GIT_REPOSITORY_ITEM_WORKDIR, NULL, true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, NULL, true }, + { GIT_REPOSITORY_ITEM_GITDIR, "index", false }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "objects", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "refs", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "packed-refs", false }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "remotes", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "config", false }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "info", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "hooks", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "logs", true }, + { GIT_REPOSITORY_ITEM_GITDIR, "modules", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "worktrees", true } +}; + static int check_repositoryformatversion(git_config *config); +#define GIT_COMMONDIR_FILE "commondir" +#define GIT_GITDIR_FILE "gitdir" + #define GIT_FILE_CONTENT_PREFIX "gitdir:" #define GIT_BRANCH_MASTER "master" @@ -109,6 +141,7 @@ void git_repository__cleanup(git_repository *repo) { assert(repo); + git_repository_submodule_cache_clear(repo); git_cache_clear(&repo->objects); git_attr_cache_flush(repo); @@ -136,8 +169,9 @@ void git_repository_free(git_repository *repo) git_buf_free(git_array_get(repo->reserved_names, i)); git_array_clear(repo->reserved_names); - git__free(repo->path_gitlink); - git__free(repo->path_repository); + git__free(repo->gitlink); + git__free(repo->gitdir); + git__free(repo->commondir); git__free(repo->workdir); git__free(repo->namespace); git__free(repo->ident_name); @@ -152,17 +186,41 @@ void git_repository_free(git_repository *repo) * * Open a repository object from its path */ -static bool valid_repository_path(git_buf *repository_path) +static bool valid_repository_path(git_buf *repository_path, git_buf *common_path) { - /* Check OBJECTS_DIR first, since it will generate the longest path name */ - if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false) - return false; + /* Check if we have a separate commondir (e.g. we have a + * worktree) */ + if (git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) { + git_buf common_link = GIT_BUF_INIT; + git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE); + + git_futils_readbuffer(&common_link, common_link.ptr); + git_buf_rtrim(&common_link); + + if (git_path_is_relative(common_link.ptr)) { + git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr); + } else { + git_buf_swap(common_path, &common_link); + } + + git_buf_free(&common_link); + } + else { + git_buf_set(common_path, repository_path->ptr, repository_path->size); + } + + /* Make sure the commondir path always has a trailing * slash */ + if (git_buf_rfind(common_path, '/') != (ssize_t)common_path->size - 1) + git_buf_putc(common_path, '/'); /* Ensure HEAD file exists */ if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false) return false; - if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false) + /* Check files in common dir */ + if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false) + return false; + if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false) return false; return true; @@ -201,6 +259,7 @@ int git_repository_new(git_repository **out) GITERR_CHECK_ALLOC(repo); repo->is_bare = 1; + repo->is_worktree = 0; return 0; } @@ -220,9 +279,10 @@ static int load_config_data(git_repository *repo, const git_config *config) static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { - int error; + int error; git_config_entry *ce; - git_buf worktree = GIT_BUF_INIT; + git_buf worktree = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT; if (repo->is_bare) return 0; @@ -231,9 +291,26 @@ static int load_workdir(git_repository *repo, git_config *config, git_buf *paren &ce, config, "core.worktree", false)) < 0) return error; - if (ce && ce->value) { + if (repo->is_worktree) { + char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE); + if (!gitlink) { + error = -1; + goto cleanup; + } + + git_buf_attach(&worktree, gitlink, 0); + + if ((git_path_dirname_r(&worktree, worktree.ptr)) < 0 || + git_path_to_dir(&worktree) < 0) { + error = -1; + goto cleanup; + } + + repo->workdir = git_buf_detach(&worktree); + } + else if (ce && ce->value) { if ((error = git_path_prettify_dir( - &worktree, ce->value, repo->path_repository)) < 0) + &worktree, ce->value, repo->gitdir)) < 0) goto cleanup; repo->workdir = git_buf_detach(&worktree); @@ -241,7 +318,7 @@ static int load_workdir(git_repository *repo, git_config *config, git_buf *paren else if (parent_path && git_path_isdir(parent_path->ptr)) repo->workdir = git_buf_detach(parent_path); else { - if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || + if (git_path_dirname_r(&worktree, repo->gitdir) < 0 || git_path_to_dir(&worktree) < 0) { error = -1; goto cleanup; @@ -252,6 +329,7 @@ static int load_workdir(git_repository *repo, git_config *config, git_buf *paren GITERR_CHECK_ALLOC(repo->workdir); cleanup: + git_buf_free(&path); git_config_entry_free(ce); return error; } @@ -332,7 +410,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path) memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { giterr_set(GITERR_REPOSITORY, - "The `.git` file at '%s' is malformed", file_path); + "the `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { @@ -348,9 +426,10 @@ static int read_gitfile(git_buf *path_out, const char *file_path) } static int find_repo( - git_buf *repo_path, - git_buf *parent_path, - git_buf *link_path, + git_buf *gitdir_path, + git_buf *workdir_path, + git_buf *gitlink_path, + git_buf *commondir_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) @@ -358,13 +437,14 @@ static int find_repo( int error; git_buf path = GIT_BUF_INIT; git_buf repo_link = GIT_BUF_INIT; + git_buf common_link = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; int min_iterations; bool in_dot_git; size_t ceiling_offset = 0; - git_buf_free(repo_path); + git_buf_clear(gitdir_path); error = git_path_prettify(&path, start_path, NULL); if (error < 0) @@ -404,21 +484,30 @@ static int find_repo( break; if (S_ISDIR(st.st_mode)) { - if (valid_repository_path(&path)) { + if (valid_repository_path(&path, &common_link)) { git_path_to_dir(&path); - git_buf_set(repo_path, path.ptr, path.size); + git_buf_set(gitdir_path, path.ptr, path.size); + + if (gitlink_path) + git_buf_attach(gitlink_path, + git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0); + if (commondir_path) + git_buf_swap(&common_link, commondir_path); + break; } } - else if (S_ISREG(st.st_mode)) { + else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) { error = read_gitfile(&repo_link, path.ptr); if (error < 0) break; - if (valid_repository_path(&repo_link)) { - git_buf_swap(repo_path, &repo_link); + if (valid_repository_path(&repo_link, &common_link)) { + git_buf_swap(gitdir_path, &repo_link); - if (link_path) - error = git_buf_put(link_path, path.ptr, path.size); + if (gitlink_path) + error = git_buf_put(gitlink_path, path.ptr, path.size); + if (commondir_path) + git_buf_swap(&common_link, commondir_path); } break; } @@ -444,27 +533,28 @@ static int find_repo( break; } - if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { - if (!git_buf_len(repo_path)) - git_buf_clear(parent_path); + if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { + if (!git_buf_len(gitdir_path)) + git_buf_clear(workdir_path); else { - git_path_dirname_r(parent_path, path.ptr); - git_path_to_dir(parent_path); + git_path_dirname_r(workdir_path, path.ptr); + git_path_to_dir(workdir_path); } - if (git_buf_oom(parent_path)) + if (git_buf_oom(workdir_path)) return -1; } /* If we didn't find the repository, and we don't have any other error * to report, report that. */ - if (!git_buf_len(repo_path) && !error) { + if (!git_buf_len(gitdir_path) && !error) { giterr_set(GITERR_REPOSITORY, - "Could not find repository from '%s'", start_path); + "could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; } git_buf_free(&path); git_buf_free(&repo_link); + git_buf_free(&common_link); return error; } @@ -473,26 +563,30 @@ int git_repository_open_bare( const char *bare_path) { int error; - git_buf path = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT; git_repository *repo = NULL; if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0) return error; - if (!valid_repository_path(&path)) { + if (!valid_repository_path(&path, &common_path)) { git_buf_free(&path); - giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path); + git_buf_free(&common_path); + giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path); return GIT_ENOTFOUND; } repo = repository_alloc(); GITERR_CHECK_ALLOC(repo); - repo->path_repository = git_buf_detach(&path); - GITERR_CHECK_ALLOC(repo->path_repository); + repo->gitdir = git_buf_detach(&path); + GITERR_CHECK_ALLOC(repo->gitdir); + repo->commondir = git_buf_detach(&common_path); + GITERR_CHECK_ALLOC(repo->commondir); /* of course we're bare! */ repo->is_bare = 1; + repo->is_worktree = 0; repo->workdir = NULL; *repo_ptr = repo; @@ -613,9 +707,10 @@ static int _git_repository_open_ext_from_env( git_repository_set_odb(repo, odb); error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES"); - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { giterr_clear(); - else if (error < 0) + error = 0; + } else if (error < 0) goto error; else { const char *end; @@ -638,9 +733,11 @@ static int _git_repository_open_ext_from_env( } } - error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf)); - if (error < 0) - goto error; + if (git_buf_len(&namespace_buf)) { + error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf)); + if (error < 0) + goto error; + } git_repository_set_index(repo, index); @@ -665,6 +762,29 @@ success: return error; } +static int repo_is_worktree(unsigned *out, const git_repository *repo) +{ + git_buf gitdir_link = GIT_BUF_INIT; + int error; + + /* Worktrees cannot have the same commondir and gitdir */ + if (repo->commondir && repo->gitdir + && !strcmp(repo->commondir, repo->gitdir)) { + *out = 0; + return 0; + } + + if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0) + return -1; + + /* A 'gitdir' file inside a git directory is currently + * only used when the repository is a working tree. */ + *out = !!git_path_exists(gitdir_link.ptr); + + git_buf_free(&gitdir_link); + return error; +} + int git_repository_open_ext( git_repository **repo_ptr, const char *start_path, @@ -672,8 +792,9 @@ int git_repository_open_ext( const char *ceiling_dirs) { int error; - git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT, - link_path = GIT_BUF_INIT; + unsigned is_worktree; + git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT, + gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT; git_repository *repo; git_config *config = NULL; @@ -684,7 +805,7 @@ int git_repository_open_ext( *repo_ptr = NULL; error = find_repo( - &path, &parent, &link_path, start_path, flags, ceiling_dirs); + &gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs); if (error < 0 || !repo_ptr) return error; @@ -692,14 +813,22 @@ int git_repository_open_ext( repo = repository_alloc(); GITERR_CHECK_ALLOC(repo); - repo->path_repository = git_buf_detach(&path); - GITERR_CHECK_ALLOC(repo->path_repository); + repo->gitdir = git_buf_detach(&gitdir); + GITERR_CHECK_ALLOC(repo->gitdir); - if (link_path.size) { - repo->path_gitlink = git_buf_detach(&link_path); - GITERR_CHECK_ALLOC(repo->path_gitlink); + if (gitlink.size) { + repo->gitlink = git_buf_detach(&gitlink); + GITERR_CHECK_ALLOC(repo->gitlink); + } + if (commondir.size) { + repo->commondir = git_buf_detach(&commondir); + GITERR_CHECK_ALLOC(repo->commondir); } + if ((error = repo_is_worktree(&is_worktree, repo)) < 0) + goto cleanup; + repo->is_worktree = is_worktree; + /* * We'd like to have the config, but git doesn't particularly * care if it's not there, so we need to deal with that. @@ -718,12 +847,13 @@ int git_repository_open_ext( if (config && ((error = load_config_data(repo, config)) < 0 || - (error = load_workdir(repo, config, &parent)) < 0)) + (error = load_workdir(repo, config, &workdir)) < 0)) goto cleanup; } cleanup: - git_buf_free(&parent); + git_buf_free(&gitdir); + git_buf_free(&workdir); git_config_free(config); if (error < 0) @@ -740,6 +870,36 @@ int git_repository_open(git_repository **repo_out, const char *path) repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL); } +int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt) +{ + git_buf path = GIT_BUF_INIT; + git_repository *repo = NULL; + int len, err; + + assert(repo_out && wt); + + *repo_out = NULL; + len = strlen(wt->gitlink_path); + + if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) { + err = -1; + goto out; + } + + if ((err = git_buf_set(&path, wt->gitlink_path, len - 4)) < 0) + goto out; + + if ((err = git_repository_open(&repo, path.ptr)) < 0) + goto out; + + *repo_out = repo; + +out: + git_buf_free(&path); + + return err; +} + int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb) { git_repository *repo; @@ -765,7 +925,7 @@ int git_repository_discover( git_buf_sanitize(out); - return find_repo(out, NULL, NULL, start_path, flags, ceiling_dirs); + return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs); } static int load_config( @@ -785,14 +945,10 @@ static int load_config( if ((error = git_config_new(&cfg)) < 0) return error; - error = git_buf_joinpath( - &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO); - if (error < 0) - goto on_error; + if ((error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0) + error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0); - if ((error = git_config_add_file_ondisk( - cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 && - error != GIT_ENOTFOUND) + if (error && error != GIT_ENOTFOUND) goto on_error; git_buf_free(&config_path); @@ -920,18 +1076,23 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) git_buf odb_path = GIT_BUF_INIT; git_odb *odb; - if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0) + if ((error = git_repository_item_path(&odb_path, repo, + GIT_REPOSITORY_ITEM_OBJECTS)) < 0 || + (error = git_odb_new(&odb)) < 0) return error; - error = git_odb_open(&odb, odb_path.ptr); - if (!error) { - GIT_REFCOUNT_OWN(odb, repo); + GIT_REFCOUNT_OWN(odb, repo); - odb = git__compare_and_swap(&repo->_odb, NULL, odb); - if (odb != NULL) { - GIT_REFCOUNT_OWN(odb, NULL); - git_odb_free(odb); - } + if ((error = git_odb__set_caps(odb, GIT_ODB_CAP_FROM_OWNER)) < 0 || + (error = git_odb__add_default_backends(odb, odb_path.ptr, 0, 0)) < 0) { + git_odb_free(odb); + return error; + } + + odb = git__compare_and_swap(&repo->_odb, NULL, odb); + if (odb != NULL) { + GIT_REFCOUNT_OWN(odb, NULL); + git_odb_free(odb); } git_buf_free(&odb_path); @@ -1006,7 +1167,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo) git_buf index_path = GIT_BUF_INIT; git_index *index; - if ((error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE)) < 0) + if ((error = git_buf_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0) return error; error = git_index_open(&index, index_path.ptr); @@ -1076,7 +1237,7 @@ static int reserved_names_add8dot3(git_repository *repo, const char *path) name_len = strlen(name); - if ((name_len == def_len && memcmp(name, def, def_len) == 0) || + if ((name_len == def_len && memcmp(name, def, def_len) == 0) || (name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) { git__free(name); return 0; @@ -1122,13 +1283,13 @@ bool git_repository__reserved_names( prefixcmp = (error || ignorecase) ? git__prefixcmp_icase : git__prefixcmp; - if (repo->path_gitlink && - reserved_names_add8dot3(repo, repo->path_gitlink) < 0) + if (repo->gitlink && + reserved_names_add8dot3(repo, repo->gitlink) < 0) goto on_error; - if (repo->path_repository && - prefixcmp(repo->path_repository, repo->workdir) == 0 && - reserved_names_add8dot3(repo, repo->path_repository) < 0) + if (repo->gitdir && + prefixcmp(repo->gitdir, repo->workdir) == 0 && + reserved_names_add8dot3(repo, repo->gitdir) < 0) goto on_error; } } @@ -1177,7 +1338,7 @@ static int check_repositoryformatversion(git_config *config) if (GIT_REPO_VERSION < version) { giterr_set(GITERR_REPOSITORY, - "Unsupported repository version %d. Only versions up to %d are supported.", + "unsupported repository version %d. Only versions up to %d are supported.", version, GIT_REPO_VERSION); return -1; } @@ -1185,7 +1346,7 @@ static int check_repositoryformatversion(git_config *config) return 0; } -static int repo_init_create_head(const char *git_dir, const char *ref_name) +int git_repository_create_head(const char *git_dir, const char *ref_name) { git_buf ref_path = GIT_BUF_INIT; git_filebuf ref = GIT_FILEBUF_INIT; @@ -1271,12 +1432,12 @@ static int create_empty_file(const char *path, mode_t mode) int fd; if ((fd = p_creat(path, mode)) < 0) { - giterr_set(GITERR_OS, "Error while creating '%s'", path); + giterr_set(GITERR_OS, "error while creating '%s'", path); return -1; } if (p_close(fd) < 0) { - giterr_set(GITERR_OS, "Error while closing '%s'", path); + giterr_set(GITERR_OS, "error while closing '%s'", path); return -1; } @@ -1505,7 +1666,7 @@ static int repo_write_template( if (error) giterr_set(GITERR_OS, - "Failed to initialize repository with template '%s'", file); + "failed to initialize repository with template '%s'", file); return error; } @@ -1536,7 +1697,7 @@ static int repo_write_gitlink( if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) { giterr_set(GITERR_REPOSITORY, - "Cannot overwrite gitlink file into path '%s'", in_dir); + "cannot overwrite gitlink file into path '%s'", in_dir); error = GIT_EEXISTS; goto cleanup; } @@ -1590,7 +1751,7 @@ static int repo_init_structure( if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { if (git_win32__set_hidden(repo_dir, true) < 0) { giterr_set(GITERR_OS, - "Failed to mark Git repository folder as hidden"); + "failed to mark Git repository folder as hidden"); return -1; } } @@ -1625,7 +1786,13 @@ static int repo_init_structure( default_template = true; } - if (tdir) { + /* + * If tdir was the empty string, treat it like tdir was a path to an + * empty directory (so, don't do any copying). This is the behavior + * that git(1) exhibits, although it doesn't seem to be officially + * documented. + */ + if (tdir && git__strcmp(tdir, "") != 0) { uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE | GIT_CPDIR_COPY_DOTFILES; @@ -1744,7 +1911,7 @@ static int repo_init_directories( if (git_path_dirname_r(wd_path, repo_path->ptr) < 0) return -1; } else { - giterr_set(GITERR_REPOSITORY, "Cannot pick working directory" + giterr_set(GITERR_REPOSITORY, "cannot pick working directory" " for non-bare repository that isn't a '.git' directory"); return -1; } @@ -1848,7 +2015,8 @@ int git_repository_init_ext( git_repository_init_options *opts) { int error; - git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT; + git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT, + common_path = GIT_BUF_INIT; const char *wd; assert(out && given_repo && opts); @@ -1860,11 +2028,11 @@ int git_repository_init_ext( goto cleanup; wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path); - if (valid_repository_path(&repo_path)) { + if (valid_repository_path(&repo_path, &common_path)) { if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) { giterr_set(GITERR_REPOSITORY, - "Attempt to reinitialize '%s'", given_repo); + "attempt to reinitialize '%s'", given_repo); error = GIT_EEXISTS; goto cleanup; } @@ -1881,7 +2049,7 @@ int git_repository_init_ext( repo_path.ptr, wd, opts)) && !(error = repo_init_config( repo_path.ptr, wd, opts->flags, opts->mode))) - error = repo_init_create_head( + error = git_repository_create_head( repo_path.ptr, opts->initial_head); } if (error < 0) @@ -1893,6 +2061,7 @@ int git_repository_init_ext( error = repo_init_create_origin(*out, opts->origin_url); cleanup: + git_buf_free(&common_path); git_buf_free(&repo_path); git_buf_free(&wd_path); @@ -1922,6 +2091,29 @@ int git_repository_head_detached(git_repository *repo) return exists; } +static int get_worktree_file_path(git_buf *out, git_repository *repo, const char *worktree, const char *file) +{ + git_buf_clear(out); + return git_buf_printf(out, "%s/worktrees/%s/%s", repo->commondir, worktree, file); +} + +int git_repository_head_detached_for_worktree(git_repository *repo, const char *name) +{ + git_reference *ref = NULL; + int error; + + assert(repo && name); + + if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0) + goto out; + + error = (git_reference_type(ref) != GIT_REF_SYMBOLIC); +out: + git_reference_free(ref); + + return error; +} + int git_repository_head(git_reference **head_out, git_repository *repo) { git_reference *head; @@ -1941,6 +2133,71 @@ int git_repository_head(git_reference **head_out, git_repository *repo) return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error; } +int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name) +{ + git_buf path = GIT_BUF_INIT; + git_reference *head = NULL; + int error; + + assert(out && repo && name); + + *out = NULL; + + if ((error = get_worktree_file_path(&path, repo, name, GIT_HEAD_FILE)) < 0 || + (error = git_reference__read_head(&head, repo, path.ptr)) < 0) + goto out; + + if (git_reference_type(head) != GIT_REF_OID) { + git_reference *resolved; + + error = git_reference_lookup_resolved(&resolved, repo, git_reference_symbolic_target(head), -1); + git_reference_free(head); + head = resolved; + } + + *out = head; + +out: + if (error) + git_reference_free(head); + + git_buf_free(&path); + + return error; +} + +int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload) +{ + git_strarray worktrees = GIT_VECTOR_INIT; + git_buf path = GIT_BUF_INIT; + int error; + size_t i; + + /* Execute callback for HEAD of commondir */ + if ((error = git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE)) < 0 || + (error = cb(repo, path.ptr, payload) != 0)) + goto out; + + if ((error = git_worktree_list(&worktrees, repo)) < 0) { + error = 0; + goto out; + } + + /* Execute callback for all worktree HEADs */ + for (i = 0; i < worktrees.count; i++) { + if (get_worktree_file_path(&path, repo, worktrees.strings[i], GIT_HEAD_FILE) < 0) + continue; + + if ((error = cb(repo, path.ptr, payload)) != 0) + goto out; + } + +out: + git_buf_free(&path); + git_strarray_free(&worktrees); + return error; +} + int git_repository_head_unborn(git_repository *repo) { git_reference *ref = NULL; @@ -1999,10 +2256,50 @@ int git_repository_is_empty(git_repository *repo) return is_empty; } +int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item) +{ + const char *parent; + + switch (items[item].parent) { + case GIT_REPOSITORY_ITEM_GITDIR: + parent = git_repository_path(repo); + break; + case GIT_REPOSITORY_ITEM_WORKDIR: + parent = git_repository_workdir(repo); + break; + case GIT_REPOSITORY_ITEM_COMMONDIR: + parent = git_repository_commondir(repo); + break; + default: + giterr_set(GITERR_INVALID, "invalid item directory"); + return -1; + } + + if (parent == NULL) { + giterr_set(GITERR_INVALID, "path cannot exist in repository"); + return GIT_ENOTFOUND; + } + + if (git_buf_sets(out, parent) < 0) + return -1; + + if (items[item].name) { + if (git_buf_joinpath(out, parent, items[item].name) < 0) + return -1; + } + + if (items[item].directory) { + if (git_path_to_dir(out) < 0) + return -1; + } + + return 0; +} + const char *git_repository_path(git_repository *repo) { assert(repo); - return repo->path_repository; + return repo->gitdir; } const char *git_repository_workdir(git_repository *repo) @@ -2015,6 +2312,12 @@ const char *git_repository_workdir(git_repository *repo) return repo->workdir; } +const char *git_repository_commondir(git_repository *repo) +{ + assert(repo); + return repo->commondir; +} + int git_repository_set_workdir( git_repository *repo, const char *workdir, int update_gitlink) { @@ -2065,6 +2368,12 @@ int git_repository_is_bare(git_repository *repo) return repo->is_bare; } +int git_repository_is_worktree(git_repository *repo) +{ + assert(repo); + return repo->is_worktree; +} + int git_repository_set_bare(git_repository *repo) { int error; @@ -2119,7 +2428,7 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head git_oid_fmt(orig_head_str, orig_head); - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 && + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 && (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0) error = git_filebuf_commit(&file); @@ -2140,13 +2449,13 @@ int git_repository_message(git_buf *out, git_repository *repo) git_buf_sanitize(out); - if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0) return -1; if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) { if (errno == ENOENT) error = GIT_ENOTFOUND; - giterr_set(GITERR_OS, "Could not access message file"); + giterr_set(GITERR_OS, "could not access message file"); } else { error = git_futils_readbuffer(out, git_buf_cstr(&path)); } @@ -2161,7 +2470,7 @@ int git_repository_message_remove(git_repository *repo) git_buf path = GIT_BUF_INIT; int error; - if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0) return -1; error = p_unlink(git_buf_cstr(&path)); @@ -2224,7 +2533,7 @@ int git_repository_hashfile( } if (!git__is_sizet(len)) { - giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); + giterr_set(GITERR_OS, "file size overflow for 32-bit systems"); error = -1; goto cleanup; } @@ -2251,7 +2560,9 @@ static int checkout_message(git_buf *out, git_reference *old, const char *new) git_buf_puts(out, " to "); - if (git_reference__is_branch(new)) + if (git_reference__is_branch(new) || + git_reference__is_tag(new) || + git_reference__is_remote(new)) git_buf_puts(out, git_reference__shorthand(new)); else git_buf_puts(out, new); @@ -2262,77 +2573,86 @@ static int checkout_message(git_buf *out, git_reference *old, const char *new) return 0; } -int git_repository_set_head( - git_repository* repo, - const char* refname) +static int detach(git_repository *repo, const git_oid *id, const char *new) { - git_reference *ref = NULL, *current = NULL, *new_head = NULL; - git_buf log_message = GIT_BUF_INIT; int error; + git_buf log_message = GIT_BUF_INIT; + git_object *object = NULL, *peeled = NULL; + git_reference *new_head = NULL, *current = NULL; - assert(repo && refname); + assert(repo && id); if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) return error; - if ((error = checkout_message(&log_message, current, refname)) < 0) + if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0) goto cleanup; - error = git_reference_lookup(&ref, repo, refname); - if (error < 0 && error != GIT_ENOTFOUND) + if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) goto cleanup; - if (!error) { - if (git_reference_is_branch(ref)) { - error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, - git_reference_name(ref), true, git_buf_cstr(&log_message)); - } else { - error = git_repository_set_head_detached(repo, git_reference_target(ref)); - } - } else if (git_reference__is_branch(refname)) { - error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, - true, git_buf_cstr(&log_message)); - } + if (new == NULL) + new = git_oid_tostr_s(git_object_id(peeled)); + + if ((error = checkout_message(&log_message, current, new)) < 0) + goto cleanup; + + error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message)); cleanup: git_buf_free(&log_message); + git_object_free(object); + git_object_free(peeled); git_reference_free(current); - git_reference_free(ref); git_reference_free(new_head); return error; } -static int detach(git_repository *repo, const git_oid *id, const char *from) +int git_repository_set_head( + git_repository* repo, + const char* refname) { - int error; + git_reference *ref = NULL, *current = NULL, *new_head = NULL; git_buf log_message = GIT_BUF_INIT; - git_object *object = NULL, *peeled = NULL; - git_reference *new_head = NULL, *current = NULL; + int error; - assert(repo && id); + assert(repo && refname); if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) return error; - if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0) + if ((error = checkout_message(&log_message, current, refname)) < 0) goto cleanup; - if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) + error = git_reference_lookup(&ref, repo, refname); + if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; - if (from == NULL) - from = git_oid_tostr_s(git_object_id(peeled)); - - if ((error = checkout_message(&log_message, current, from)) < 0) + if (ref && current->type == GIT_REF_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) && + git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) { + giterr_set(GITERR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD " + "of a linked repository.", git_reference_name(ref)); + error = -1; goto cleanup; + } - error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message)); + if (!error) { + if (git_reference_is_branch(ref)) { + error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, + git_reference_name(ref), true, git_buf_cstr(&log_message)); + } else { + error = detach(repo, git_reference_target(ref), + git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL); + } + } else if (git_reference__is_branch(refname)) { + error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, + true, git_buf_cstr(&log_message)); + } cleanup: git_buf_free(&log_message); - git_object_free(object); - git_object_free(peeled); git_reference_free(current); + git_reference_free(ref); git_reference_free(new_head); return error; } @@ -2397,7 +2717,7 @@ int git_repository_state(git_repository *repo) assert(repo); - if (git_buf_puts(&repo_path, repo->path_repository) < 0) + if (git_buf_puts(&repo_path, repo->gitdir) < 0) return -1; if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE)) @@ -2439,7 +2759,7 @@ int git_repository__cleanup_files( for (error = 0, i = 0; !error && i < files_len; ++i) { const char *path; - if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0) + if (git_buf_joinpath(&buf, repo->gitdir, files[i]) < 0) return -1; path = git_buf_cstr(&buf); @@ -2450,7 +2770,7 @@ int git_repository__cleanup_files( error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); } - + git_buf_clear(&buf); } @@ -2483,7 +2803,7 @@ int git_repository_is_shallow(git_repository *repo) struct stat st; int error; - if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0) + if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0) return error; error = git_path_lstat(path.ptr, &st); @@ -2538,3 +2858,31 @@ int git_repository_set_ident(git_repository *repo, const char *name, const char return 0; } + +int git_repository_submodule_cache_all(git_repository *repo) +{ + int error; + + assert(repo); + + if ((error = git_strmap_alloc(&repo->submodule_cache))) + return error; + + error = git_submodule__map(repo, repo->submodule_cache); + return error; +} + +int git_repository_submodule_cache_clear(git_repository *repo) +{ + git_submodule *sm; + assert(repo); + if (repo->submodule_cache == NULL) { + return 0; + } + git_strmap_foreach_value(repo->submodule_cache, sm, { + git_submodule_free(sm); + }); + git_strmap_free(repo->submodule_cache); + repo->submodule_cache = 0; + return 0; +} diff --git a/src/repository.h b/src/repository.h index fd679b483..fd6400cc1 100644 --- a/src/repository.h +++ b/src/repository.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_repository_h__ #define INCLUDE_repository_h__ +#include "common.h" + #include "git2/common.h" #include "git2/oid.h" #include "git2/odb.h" @@ -31,6 +33,8 @@ /* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */ #define GIT_DIR_SHORTNAME "GIT~1" +extern bool git_repository__fsync_gitdir; + /** Cvar cache identifiers */ typedef enum { GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */ @@ -46,6 +50,7 @@ typedef enum { GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */ GIT_CVAR_PROTECTHFS, /* core.protectHFS */ GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */ + GIT_CVAR_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */ GIT_CVAR_CACHE_MAX } git_cvar_cached; @@ -106,6 +111,8 @@ typedef enum { GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE, /* core.protectNTFS */ GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE, + /* core.fsyncObjectFiles */ + GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CVAR_FALSE, } git_cvar_value; /* internal repository init flags */ @@ -126,8 +133,9 @@ struct git_repository { git_attr_cache *attrcache; git_diff_driver_registry *diff_drivers; - char *path_repository; - char *path_gitlink; + char *gitlink; + char *gitdir; + char *commondir; char *workdir; char *namespace; @@ -137,12 +145,14 @@ struct git_repository { git_array_t(git_buf) reserved_names; unsigned is_bare:1; + unsigned is_worktree:1; unsigned int lru_counter; git_atomic attr_session_key; git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX]; + git_strmap *submodule_cache; }; GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) @@ -151,6 +161,27 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) } int git_repository_head_tree(git_tree **tree, git_repository *repo); +int git_repository_create_head(const char *git_dir, const char *ref_name); + +/* + * Called for each HEAD. + * + * Can return either 0, causing the iteration over HEADs to + * continue, or a non-0 value causing the iteration to abort. The + * return value is passed back to the caller of + * `git_repository_foreach_head` + */ +typedef int (*git_repository_foreach_head_cb)(git_repository *repo, const char *path, void *payload); + +/* + * Iterate over repository and all worktree HEADs. + * + * This function will be called for the repository HEAD and for + * all HEADS of linked worktrees. For each HEAD, the callback is + * executed with the given payload. The return value equals the + * return value of the last executed callback function. + */ +int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload); /* * Weak pointers to repository internals. @@ -182,7 +213,7 @@ GIT_INLINE(int) git_repository__ensure_not_bare( giterr_set( GITERR_REPOSITORY, - "Cannot %s. This operation is not allowed against bare repositories.", + "cannot %s. This operation is not allowed against bare repositories.", operation_name); return GIT_EBAREREPO; diff --git a/src/reset.c b/src/reset.c index db0bfb373..21596812f 100644 --- a/src/reset.c +++ b/src/reset.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "commit.h" #include "tag.h" #include "merge.h" @@ -20,8 +21,8 @@ int git_reset_default( git_repository *repo, - git_object *target, - git_strarray* pathspecs) + const git_object *target, + const git_strarray* pathspecs) { git_object *commit = NULL; git_tree *tree = NULL; @@ -100,7 +101,7 @@ cleanup: static int reset( git_repository *repo, - git_object *target, + const git_object *target, const char *to, git_reset_t reset_type, const git_checkout_options *checkout_opts) @@ -137,7 +138,7 @@ static int reset( (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE || git_index_has_conflicts(index))) { - giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG); + giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG); error = GIT_EUNMERGED; goto cleanup; } @@ -182,7 +183,7 @@ cleanup: int git_reset( git_repository *repo, - git_object *target, + const git_object *target, git_reset_t reset_type, const git_checkout_options *checkout_opts) { @@ -191,7 +192,7 @@ int git_reset( int git_reset_from_annotated( git_repository *repo, - git_annotated_commit *commit, + const git_annotated_commit *commit, git_reset_t reset_type, const git_checkout_options *checkout_opts) { diff --git a/src/revert.c b/src/revert.c index c481e7dea..54f6d48e4 100644 --- a/src/revert.c +++ b/src/revert.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "repository.h" #include "filebuf.h" #include "merge.h" @@ -27,7 +28,7 @@ static int write_revert_head( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_REVERT_HEAD_FILE)) >= 0 && + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_REVERT_HEAD_FILE)) >= 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) >= 0 && (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) error = git_filebuf_commit(&file); @@ -49,7 +50,7 @@ static int write_merge_msg( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) < 0 || (error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n", commit_msgline, commit_oidstr)) < 0) @@ -133,13 +134,13 @@ int git_revert_commit( if (git_commit_parentcount(revert_commit) > 1) { if (!mainline) return revert_seterr(revert_commit, - "Mainline branch is not specified but %s is a merge commit"); + "mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return revert_seterr(revert_commit, - "Mainline branch specified but %s is not a merge commit"); + "mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(revert_commit); } diff --git a/src/revparse.c b/src/revparse.c index e0ec3941d..4ab4fb96d 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -5,9 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #include <assert.h> -#include "common.h" #include "buffer.h" #include "tree.h" #include "refdb.h" @@ -46,11 +47,11 @@ static int build_regex(regex_t *regex, const char *pattern) int error; if (*pattern == '\0') { - giterr_set(GITERR_REGEX, "Empty pattern"); + giterr_set(GITERR_REGEX, "empty pattern"); return GIT_EINVALIDSPEC; } - error = regcomp(regex, pattern, REG_EXTENDED); + error = p_regcomp(regex, pattern, REG_EXTENDED); if (!error) return 0; @@ -118,7 +119,7 @@ static int revparse_lookup_object( if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND) return error; - giterr_set(GITERR_REFERENCE, "Revspec '%s' not found.", spec); + giterr_set(GITERR_REFERENCE, "revspec '%s' not found", spec); return GIT_ENOTFOUND; } @@ -245,7 +246,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide notfound: giterr_set( GITERR_REFERENCE, - "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, + "reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, git_reference_name(ref), numentries, identifier); git_reflog_free(reflog); @@ -757,7 +758,7 @@ int revparse__ext( * TODO: support merge-stage path lookup (":2:Makefile") * and plain index blob lookup (:i-am/a/blob) */ - giterr_set(GITERR_INVALID, "Unimplemented"); + giterr_set(GITERR_INVALID, "unimplemented"); error = GIT_ERROR; goto cleanup; } @@ -816,7 +817,7 @@ cleanup: if (error) { if (error == GIT_EINVALIDSPEC) giterr_set(GITERR_INVALID, - "Failed to parse revision specifier - Invalid pattern '%s'", spec); + "failed to parse revision specifier - Invalid pattern '%s'", spec); git_object_free(base_rev); git_reference_free(reference); @@ -892,6 +893,17 @@ int git_revparse( const char *rstr; revspec->flags = GIT_REVPARSE_RANGE; + /* + * Following git.git, don't allow '..' because it makes command line + * arguments which can be either paths or revisions ambiguous when the + * path is almost certainly intended. The empty range '...' is still + * allowed. + */ + if (!git__strcmp(spec, "..")) { + giterr_set(GITERR_INVALID, "Invalid pattern '..'"); + return GIT_EINVALIDSPEC; + } + lstr = git__substrdup(spec, dotdot - spec); rstr = dotdot + 2; if (dotdot[2] == '.') { @@ -899,9 +911,17 @@ int git_revparse( rstr++; } - error = git_revparse_single(&revspec->from, repo, lstr); - if (!error) - error = git_revparse_single(&revspec->to, repo, rstr); + error = git_revparse_single( + &revspec->from, + repo, + *lstr == '\0' ? "HEAD" : lstr); + + if (!error) { + error = git_revparse_single( + &revspec->to, + repo, + *rstr == '\0' ? "HEAD" : rstr); + } git__free((void*)lstr); } else { diff --git a/src/revwalk.c b/src/revwalk.c index 4815a1089..b1aa8f91a 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -5,16 +5,15 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "revwalk.h" + #include "commit.h" #include "odb.h" #include "pool.h" -#include "revwalk.h" #include "git2/revparse.h" #include "merge.h" - -GIT__USE_OIDMAP +#include "vector.h" git_commit_list_node *git_revwalk__commit_lookup( git_revwalk *walk, const git_oid *oid) @@ -24,9 +23,9 @@ git_commit_list_node *git_revwalk__commit_lookup( int ret; /* lookup and reserve space if not already present */ - pos = kh_get(oid, walk->commits, oid); - if (pos != kh_end(walk->commits)) - return kh_value(walk->commits, pos); + pos = git_oidmap_lookup_index(walk->commits, oid); + if (git_oidmap_valid_index(walk->commits, pos)) + return git_oidmap_value_at(walk->commits, pos); commit = git_commit_list_alloc_node(walk); if (commit == NULL) @@ -34,104 +33,13 @@ git_commit_list_node *git_revwalk__commit_lookup( git_oid_cpy(&commit->oid, oid); - pos = kh_put(oid, walk->commits, &commit->oid, &ret); + pos = git_oidmap_put(walk->commits, &commit->oid, &ret); assert(ret != 0); - kh_value(walk->commits, pos) = commit; + git_oidmap_set_value_at(walk->commits, pos, commit); return commit; } -typedef git_array_t(git_commit_list_node*) commit_list_node_array; - -static bool interesting_arr(commit_list_node_array arr) -{ - git_commit_list_node **n; - size_t i = 0, size; - - size = git_array_size(arr); - for (i = 0; i < size; i++) { - n = git_array_get(arr, i); - if (!*n) - break; - - if (!(*n)->uninteresting) - return true; - } - - return false; -} - -static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit) -{ - int error; - unsigned short i; - commit_list_node_array pending = GIT_ARRAY_INIT; - git_commit_list_node **tmp; - - assert(commit); - - do { - commit->uninteresting = 1; - - if ((error = git_commit_list_parse(walk, commit)) < 0) - return error; - - for (i = 0; i < commit->out_degree; ++i) - if (!commit->parents[i]->uninteresting) { - git_commit_list_node **node = git_array_alloc(pending); - GITERR_CHECK_ALLOC(node); - *node = commit->parents[i]; - } - - tmp = git_array_pop(pending); - commit = tmp ? *tmp : NULL; - - } while (commit != NULL && !interesting_arr(pending)); - - git_array_clear(pending); - - return 0; -} - -static int process_commit(git_revwalk *walk, git_commit_list_node *commit, int hide) -{ - int error; - - if (!hide && walk->hide_cb) - hide = walk->hide_cb(&commit->oid, walk->hide_cb_payload); - - if (hide && mark_uninteresting(walk, commit) < 0) - return -1; - - if (commit->seen) - return 0; - - commit->seen = 1; - - if ((error = git_commit_list_parse(walk, commit)) < 0) - return error; - - if (!hide) - return walk->enqueue(walk, commit); - - return 0; -} - -static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commit) -{ - unsigned short i, max; - int error = 0; - - max = commit->out_degree; - if (walk->first_parent && commit->out_degree) - max = 1; - - for (i = 0; i < max && !error; ++i) - error = process_commit(walk, commit->parents[i], commit->uninteresting); - - return error; -} - static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting, int from_glob) { git_oid commit_id; @@ -151,7 +59,7 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting, if (from_glob) return 0; - giterr_set(GITERR_INVALID, "Object is not a committish"); + giterr_set(GITERR_INVALID, "object is not a committish"); return -1; } if (error < 0) @@ -288,7 +196,7 @@ int git_revwalk_push_range(git_revwalk *walk, const char *range) if (revspec.flags & GIT_REVPARSE_MERGE_BASE) { /* TODO: support "<commit>...<commit>" */ - giterr_set(GITERR_INVALID, "Symmetric differences not implemented in revwalk"); + giterr_set(GITERR_INVALID, "symmetric differences not implemented in revwalk"); return GIT_EINVALIDSPEC; } @@ -321,17 +229,15 @@ static int revwalk_enqueue_unsorted(git_revwalk *walk, git_commit_list_node *com static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk *walk) { - int error; git_commit_list_node *next; - while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) + while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { + /* Some commits might become uninteresting after being added to the list */ if (!next->uninteresting) { - if ((error = process_commit_parents(walk, next)) < 0) - return error; - *object_out = next; return 0; } + } giterr_clear(); return GIT_ITEROVER; @@ -339,17 +245,15 @@ static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk *walk) { - int error; git_commit_list_node *next; - while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) + while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) { + /* Some commits might become uninteresting after being added to the list */ if (!next->uninteresting) { - if ((error = process_commit_parents(walk, next)) < 0) - return error; - *object_out = next; return 0; } + } giterr_clear(); return GIT_ITEROVER; @@ -358,121 +262,283 @@ static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk) { git_commit_list_node *next; - unsigned short i, max; - for (;;) { - next = git_commit_list_pop(&walk->iterator_topo); - if (next == NULL) { - giterr_clear(); - return GIT_ITEROVER; + while ((next = git_commit_list_pop(&walk->iterator_topo)) != NULL) { + /* Some commits might become uninteresting after being added to the list */ + if (!next->uninteresting) { + *object_out = next; + return 0; } + } - if (next->in_degree > 0) { - next->topo_delay = 1; - continue; + giterr_clear(); + return GIT_ITEROVER; +} + +static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk) +{ + *object_out = git_commit_list_pop(&walk->iterator_reverse); + return *object_out ? 0 : GIT_ITEROVER; +} + +static void mark_parents_uninteresting(git_commit_list_node *commit) +{ + unsigned short i; + git_commit_list *parents = NULL; + + for (i = 0; i < commit->out_degree; i++) + git_commit_list_insert(commit->parents[i], &parents); + + + while (parents) { + commit = git_commit_list_pop(&parents); + + while (commit) { + if (commit->uninteresting) + break; + + commit->uninteresting = 1; + /* + * If we've reached this commit some other way + * already, we need to mark its parents uninteresting + * as well. + */ + if (!commit->parents) + break; + + for (i = 0; i < commit->out_degree; i++) + git_commit_list_insert(commit->parents[i], &parents); + commit = commit->parents[0]; } + } +} + +static int add_parents_to_list(git_revwalk *walk, git_commit_list_node *commit, git_commit_list **list) +{ + unsigned short i; + int error; + if (commit->added) + return 0; - max = next->out_degree; - if (walk->first_parent && next->out_degree) - max = 1; + commit->added = 1; + + /* + * Go full on in the uninteresting case as we want to include + * as many of these as we can. + * + * Usually we haven't parsed the parent of a parent, but if we + * have it we reached it via other means so we want to mark + * its parents recursively too. + */ + if (commit->uninteresting) { + for (i = 0; i < commit->out_degree; i++) { + git_commit_list_node *p = commit->parents[i]; + p->uninteresting = 1; - for (i = 0; i < max; ++i) { - git_commit_list_node *parent = next->parents[i]; + /* git does it gently here, but we don't like missing objects */ + if ((error = git_commit_list_parse(walk, p)) < 0) + return error; - if (--parent->in_degree == 0 && parent->topo_delay) { - parent->topo_delay = 0; - if (git_commit_list_insert(parent, &walk->iterator_topo) == NULL) - return -1; - } + if (p->parents) + mark_parents_uninteresting(p); + + p->seen = 1; + git_commit_list_insert_by_date(p, list); } - *object_out = next; return 0; } + + /* + * Now on to what we do if the commit is indeed + * interesting. Here we do want things like first-parent take + * effect as this is what we'll be showing. + */ + for (i = 0; i < commit->out_degree; i++) { + git_commit_list_node *p = commit->parents[i]; + + if ((error = git_commit_list_parse(walk, p)) < 0) + return error; + + if (walk->hide_cb && walk->hide_cb(&p->oid, walk->hide_cb_payload)) + continue; + + if (!p->seen) { + p->seen = 1; + git_commit_list_insert_by_date(p, list); + } + + if (walk->first_parent) + break; + } + return 0; } -static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk) +static int everybody_uninteresting(git_commit_list *orig) { - *object_out = git_commit_list_pop(&walk->iterator_reverse); - return *object_out ? 0 : GIT_ITEROVER; + git_commit_list *list = orig; + + while (list) { + git_commit_list_node *commit = list->item; + list = list->next; + if (!commit->uninteresting) + return 0; + } + + return 1; } +/* How many unintersting commits we want to look at after we run out of interesting ones */ +#define SLOP 5 -static int interesting(git_pqueue *list) +static int still_interesting(git_commit_list *list, int64_t time, int slop) { - size_t i; + /* The empty list is pretty boring */ + if (!list) + return 0; - for (i = 0; i < git_pqueue_size(list); i++) { - git_commit_list_node *commit = git_pqueue_get(list, i); - if (!commit->uninteresting) - return 1; - } + /* + * If the destination list has commits with an earlier date + * than our source we want to continue looking. + */ + if (time <= list->item->time) + return SLOP; - return 0; + /* If we find interesting commits, we reset the slop count */ + if (!everybody_uninteresting(list)) + return SLOP; + + /* Everything's uninteresting, reduce the count */ + return slop - 1; } -static int contains(git_pqueue *list, git_commit_list_node *node) +static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits) { - size_t i; + int error, slop = SLOP; + int64_t time = ~0ll; + git_commit_list *list = commits; + git_commit_list *newlist = NULL; + git_commit_list **p = &newlist; + + while (list) { + git_commit_list_node *commit = git_commit_list_pop(&list); - for (i = 0; i < git_pqueue_size(list); i++) { - git_commit_list_node *commit = git_pqueue_get(list, i); - if (commit == node) - return 1; + if ((error = add_parents_to_list(walk, commit, &list)) < 0) + return error; + + if (commit->uninteresting) { + mark_parents_uninteresting(commit); + + slop = still_interesting(list, time, slop); + if (slop) + continue; + + break; + } + + if (!commit->uninteresting && walk->hide_cb && walk->hide_cb(&commit->oid, walk->hide_cb_payload)) + continue; + + time = commit->time; + p = &git_commit_list_insert(commit, p)->next; } + git_commit_list_free(&list); + *out = newlist; return 0; } -static int premark_uninteresting(git_revwalk *walk) +static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, git_commit_list *list) { - int error = 0; + git_commit_list *ll = NULL, *newlist, **pptr; + git_commit_list_node *next; + git_pqueue queue; + git_vector_cmp queue_cmp = NULL; unsigned short i; - git_pqueue q; - git_commit_list *list; - git_commit_list_node *commit, *parent; + int error; - if ((error = git_pqueue_init(&q, 0, 8, git_commit_list_time_cmp)) < 0) - return error; + if (walk->sorting & GIT_SORT_TIME) + queue_cmp = git_commit_list_time_cmp; - for (list = walk->user_input; list; list = list->next) { - if ((error = git_commit_list_parse(walk, list->item)) < 0) - goto cleanup; + if ((error = git_pqueue_init(&queue, 0, 8, queue_cmp))) + return error; - if ((error = git_pqueue_insert(&q, list->item)) < 0) - goto cleanup; + /* + * Start by resetting the in-degree to 1 for the commits in + * our list. We want to go through this list again, so we + * store it in the commit list as we extract it from the lower + * machinery. + */ + for (ll = list; ll; ll = ll->next) { + ll->item->in_degree = 1; } - while (interesting(&q)) { - commit = git_pqueue_pop(&q); - - for (i = 0; i < commit->out_degree; i++) { - parent = commit->parents[i]; + /* + * Count up how many children each commit has. We limit + * ourselves to those commits in the original list (in-degree + * of 1) avoiding setting it for any parent that was hidden. + */ + for(ll = list; ll; ll = ll->next) { + for (i = 0; i < ll->item->out_degree; ++i) { + git_commit_list_node *parent = ll->item->parents[i]; + if (parent->in_degree) + parent->in_degree++; + } + } - if ((error = git_commit_list_parse(walk, parent)) < 0) + /* + * Now we find the tips i.e. those not reachable from any other node + * i.e. those which still have an in-degree of 1. + */ + for(ll = list; ll; ll = ll->next) { + if (ll->item->in_degree == 1) { + if ((error = git_pqueue_insert(&queue, ll->item))) goto cleanup; + } + } - if (commit->uninteresting) - parent->uninteresting = 1; + /* + * We need to output the tips in the order that they came out of the + * traversal, so if we're not doing time-sorting, we need to reverse the + * pqueue in order to get them to come out as we inserted them. + */ + if ((walk->sorting & GIT_SORT_TIME) == 0) + git_pqueue_reverse(&queue); - if (contains(&q, parent)) + + pptr = &newlist; + newlist = NULL; + while ((next = git_pqueue_pop(&queue)) != NULL) { + for (i = 0; i < next->out_degree; ++i) { + git_commit_list_node *parent = next->parents[i]; + if (parent->in_degree == 0) continue; - if ((error = git_pqueue_insert(&q, parent)) < 0) - goto cleanup; + if (--parent->in_degree == 1) { + if ((error = git_pqueue_insert(&queue, parent))) + goto cleanup; + } } + + /* All the children of 'item' have been emitted (since we got to it via the priority queue) */ + next->in_degree = 0; + + pptr = &git_commit_list_insert(next, pptr)->next; } + *out = newlist; + error = 0; + cleanup: - git_pqueue_free(&q); + git_pqueue_free(&queue); return error; } static int prepare_walk(git_revwalk *walk) { int error; - git_commit_list *list; + git_commit_list *list, *commits = NULL; git_commit_list_node *next; /* If there were no pushes, we know that the walk is already over */ @@ -481,32 +547,42 @@ static int prepare_walk(git_revwalk *walk) return GIT_ITEROVER; } - if (walk->did_hide && (error = premark_uninteresting(walk)) < 0) - return error; - for (list = walk->user_input; list; list = list->next) { - if (process_commit(walk, list->item, list->item->uninteresting) < 0) - return -1; - } + git_commit_list_node *commit = list->item; + if ((error = git_commit_list_parse(walk, commit)) < 0) + return error; + if (commit->uninteresting) + mark_parents_uninteresting(commit); - if (walk->sorting & GIT_SORT_TOPOLOGICAL) { - unsigned short i; + if (!commit->seen) { + commit->seen = 1; + git_commit_list_insert(commit, &commits); + } + } - while ((error = walk->get_next(&next, walk)) == 0) { - for (i = 0; i < next->out_degree; ++i) { - git_commit_list_node *parent = next->parents[i]; - parent->in_degree++; - } + if ((error = limit_list(&commits, walk, commits)) < 0) + return error; - if (git_commit_list_insert(next, &walk->iterator_topo) == NULL) - return -1; - } + if (walk->sorting & GIT_SORT_TOPOLOGICAL) { + error = sort_in_topological_order(&walk->iterator_topo, walk, commits); + git_commit_list_free(&commits); - if (error != GIT_ITEROVER) + if (error < 0) return error; walk->get_next = &revwalk_next_toposort; + } else if (walk->sorting & GIT_SORT_TIME) { + for (list = commits; list && !error; list = list->next) + error = walk->enqueue(walk, list->item); + + git_commit_list_free(&commits); + + if (error < 0) + return error; + } else { + walk->iterator_rand = commits; + walk->get_next = revwalk_next_unsorted; } if (walk->sorting & GIT_SORT_REVERSE) { @@ -627,11 +703,12 @@ void git_revwalk_reset(git_revwalk *walk) assert(walk); - kh_foreach_value(walk->commits, commit, { + git_oidmap_foreach_value(walk->commits, commit, { commit->seen = 0; commit->in_degree = 0; commit->topo_delay = 0; commit->uninteresting = 0; + commit->added = 0; commit->flags = 0; }); @@ -657,7 +734,7 @@ int git_revwalk_add_hide_cb( if (walk->hide_cb) { /* There is already a callback added */ - giterr_set(GITERR_INVALID, "There is already a callback added to hide commits in revision walker."); + giterr_set(GITERR_INVALID, "there is already a callback added to hide commits in revwalk"); return -1; } diff --git a/src/revwalk.h b/src/revwalk.h index 6b363d40f..578328b72 100644 --- a/src/revwalk.h +++ b/src/revwalk.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_revwalk_h__ #define INCLUDE_revwalk_h__ +#include "common.h" + #include "git2/revwalk.h" #include "oidmap.h" #include "commit_list.h" diff --git a/src/settings.c b/src/settings.c index cb2317f74..ba2f6a4db 100644 --- a/src/settings.c +++ b/src/settings.c @@ -5,16 +5,20 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #ifdef GIT_OPENSSL # include <openssl/err.h> #endif #include <git2.h> -#include "common.h" #include "sysdir.h" #include "cache.h" #include "global.h" #include "object.h" +#include "odb.h" +#include "refs.h" +#include "transports/smart.h" void git_libgit2_version(int *major, int *minor, int *rev) { @@ -29,7 +33,9 @@ int git_libgit2_features(void) #ifdef GIT_THREADS | GIT_FEATURE_THREADS #endif +#ifdef GIT_HTTPS | GIT_FEATURE_HTTPS +#endif #if defined(GIT_SSH) | GIT_FEATURE_SSH #endif @@ -62,7 +68,7 @@ static int config_level_to_sysdir(int config_level) break; default: giterr_set( - GITERR_INVALID, "Invalid config path selector %d", config_level); + GITERR_INVALID, "invalid config path selector %d", config_level); } return val; @@ -191,6 +197,10 @@ int git_libgit2_opts(int key, ...) git_object__strict_input_validation = (va_arg(ap, int) != 0); break; + case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION: + git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0); + break; + case GIT_OPT_SET_SSL_CIPHERS: #ifdef GIT_OPENSSL { @@ -215,6 +225,30 @@ int git_libgit2_opts(int key, ...) } break; + case GIT_OPT_ENABLE_OFS_DELTA: + git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0); + break; + + case GIT_OPT_ENABLE_FSYNC_GITDIR: + git_repository__fsync_gitdir = (va_arg(ap, int) != 0); + break; + + case GIT_OPT_GET_WINDOWS_SHAREMODE: +#ifdef GIT_WIN32 + *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode; +#endif + break; + + case GIT_OPT_SET_WINDOWS_SHAREMODE: +#ifdef GIT_WIN32 + git_win32__createfile_sharemode = va_arg(ap, unsigned long); +#endif + break; + + case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION: + git_odb__strict_hash_verification = (va_arg(ap, int) != 0); + break; + default: giterr_set(GITERR_INVALID, "invalid option key"); error = -1; diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c index c6b561340..14fcb40e5 100644 --- a/src/sha1_lookup.c +++ b/src/sha1_lookup.c @@ -5,225 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include <stdio.h> - #include "sha1_lookup.h" -#include "common.h" -#include "oid.h" -/* - * Conventional binary search loop looks like this: - * - * unsigned lo, hi; - * do { - * unsigned mi = (lo + hi) / 2; - * int cmp = "entry pointed at by mi" minus "target"; - * if (!cmp) - * return (mi is the wanted one) - * if (cmp > 0) - * hi = mi; "mi is larger than target" - * else - * lo = mi+1; "mi is smaller than target" - * } while (lo < hi); - * - * The invariants are: - * - * - When entering the loop, lo points at a slot that is never - * above the target (it could be at the target), hi points at a - * slot that is guaranteed to be above the target (it can never - * be at the target). - * - * - We find a point 'mi' between lo and hi (mi could be the same - * as lo, but never can be as same as hi), and check if it hits - * the target. There are three cases: - * - * - if it is a hit, we are happy. - * - * - if it is strictly higher than the target, we set it to hi, - * and repeat the search. - * - * - if it is strictly lower than the target, we update lo to - * one slot after it, because we allow lo to be at the target. - * - * If the loop exits, there is no matching entry. - * - * When choosing 'mi', we do not have to take the "middle" but - * anywhere in between lo and hi, as long as lo <= mi < hi is - * satisfied. When we somehow know that the distance between the - * target and lo is much shorter than the target and hi, we could - * pick mi that is much closer to lo than the midway. - * - * Now, we can take advantage of the fact that SHA-1 is a good hash - * function, and as long as there are enough entries in the table, we - * can expect uniform distribution. An entry that begins with for - * example "deadbeef..." is much likely to appear much later than in - * the midway of the table. It can reasonably be expected to be near - * 87% (222/256) from the top of the table. - * - * However, we do not want to pick "mi" too precisely. If the entry at - * the 87% in the above example turns out to be higher than the target - * we are looking for, we would end up narrowing the search space down - * only by 13%, instead of 50% we would get if we did a simple binary - * search. So we would want to hedge our bets by being less aggressive. - * - * The table at "table" holds at least "nr" entries of "elem_size" - * bytes each. Each entry has the SHA-1 key at "key_offset". The - * table is sorted by the SHA-1 key of the entries. The caller wants - * to find the entry with "key", and knows that the entry at "lo" is - * not higher than the entry it is looking for, and that the entry at - * "hi" is higher than the entry it is looking for. - */ -int sha1_entry_pos(const void *table, - size_t elem_size, - size_t key_offset, - unsigned lo, unsigned hi, unsigned nr, - const unsigned char *key) -{ - const unsigned char *base = (const unsigned char*)table; - const unsigned char *hi_key, *lo_key; - unsigned ofs_0; - - if (!nr || lo >= hi) - return -1; - - if (nr == hi) - hi_key = NULL; - else - hi_key = base + elem_size * hi + key_offset; - lo_key = base + elem_size * lo + key_offset; - - ofs_0 = 0; - do { - int cmp; - unsigned ofs, mi, range; - unsigned lov, hiv, kyv; - const unsigned char *mi_key; - - range = hi - lo; - if (hi_key) { - for (ofs = ofs_0; ofs < 20; ofs++) - if (lo_key[ofs] != hi_key[ofs]) - break; - ofs_0 = ofs; - /* - * byte 0 thru (ofs-1) are the same between - * lo and hi; ofs is the first byte that is - * different. - * - * If ofs==20, then no bytes are different, - * meaning we have entries with duplicate - * keys. We know that we are in a solid run - * of this entry (because the entries are - * sorted, and our lo and hi are the same, - * there can be nothing but this single key - * in between). So we can stop the search. - * Either one of these entries is it (and - * we do not care which), or we do not have - * it. - * - * Furthermore, we know that one of our - * endpoints must be the edge of the run of - * duplicates. For example, given this - * sequence: - * - * idx 0 1 2 3 4 5 - * key A C C C C D - * - * If we are searching for "B", we might - * hit the duplicate run at lo=1, hi=3 - * (e.g., by first mi=3, then mi=0). But we - * can never have lo > 1, because B < C. - * That is, if our key is less than the - * run, we know that "lo" is the edge, but - * we can say nothing of "hi". Similarly, - * if our key is greater than the run, we - * know that "hi" is the edge, but we can - * say nothing of "lo". - * - * Therefore if we do not find it, we also - * know where it would go if it did exist: - * just on the far side of the edge that we - * know about. - */ - if (ofs == 20) { - mi = lo; - mi_key = base + elem_size * mi + key_offset; - cmp = memcmp(mi_key, key, 20); - if (!cmp) - return mi; - if (cmp < 0) - return -1 - hi; - else - return -1 - lo; - } - - hiv = hi_key[ofs_0]; - if (ofs_0 < 19) - hiv = (hiv << 8) | hi_key[ofs_0+1]; - } else { - hiv = 256; - if (ofs_0 < 19) - hiv <<= 8; - } - lov = lo_key[ofs_0]; - kyv = key[ofs_0]; - if (ofs_0 < 19) { - lov = (lov << 8) | lo_key[ofs_0+1]; - kyv = (kyv << 8) | key[ofs_0+1]; - } - assert(lov < hiv); - - if (kyv < lov) - return -1 - lo; - if (hiv < kyv) - return -1 - hi; - - /* - * Even if we know the target is much closer to 'hi' - * than 'lo', if we pick too precisely and overshoot - * (e.g. when we know 'mi' is closer to 'hi' than to - * 'lo', pick 'mi' that is higher than the target), we - * end up narrowing the search space by a smaller - * amount (i.e. the distance between 'mi' and 'hi') - * than what we would have (i.e. about half of 'lo' - * and 'hi'). Hedge our bets to pick 'mi' less - * aggressively, i.e. make 'mi' a bit closer to the - * middle than we would otherwise pick. - */ - kyv = (kyv * 6 + lov + hiv) / 8; - if (lov < hiv - 1) { - if (kyv == lov) - kyv++; - else if (kyv == hiv) - kyv--; - } - mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo; - -#ifdef INDEX_DEBUG_LOOKUP - printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi); - printf("ofs %u lov %x, hiv %x, kyv %x\n", - ofs_0, lov, hiv, kyv); -#endif - - if (!(lo <= mi && mi < hi)) { - giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false"); - return -1; - } +#include <stdio.h> - mi_key = base + elem_size * mi + key_offset; - cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0); - if (!cmp) - return mi; - if (cmp > 0) { - hi = mi; - hi_key = mi_key; - } else { - lo = mi + 1; - lo_key = mi_key + elem_size; - } - } while (lo < hi); - return -((int)lo)-1; -} +#include "oid.h" int sha1_position(const void *table, size_t stride, @@ -232,7 +18,7 @@ int sha1_position(const void *table, { const unsigned char *base = table; - do { + while (lo < hi) { unsigned mi = (lo + hi) / 2; int cmp = git_oid__hashcmp(base + mi * stride, key); @@ -243,7 +29,7 @@ int sha1_position(const void *table, hi = mi; else lo = mi+1; - } while (lo < hi); + } return -((int)lo)-1; } diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h index 3799620c7..841ea5b22 100644 --- a/src/sha1_lookup.h +++ b/src/sha1_lookup.h @@ -7,13 +7,9 @@ #ifndef INCLUDE_sha1_lookup_h__ #define INCLUDE_sha1_lookup_h__ -#include <stdlib.h> +#include "common.h" -int sha1_entry_pos(const void *table, - size_t elem_size, - size_t key_offset, - unsigned lo, unsigned hi, unsigned nr, - const unsigned char *key); +#include <stdlib.h> int sha1_position(const void *table, size_t stride, diff --git a/src/signature.c b/src/signature.c index dcc379717..bc3cf330a 100644 --- a/src/signature.c +++ b/src/signature.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "signature.h" + #include "repository.h" #include "git2/common.h" #include "posix.h" @@ -25,7 +25,7 @@ void git_signature_free(git_signature *sig) static int signature_error(const char *msg) { - giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg); + giterr_set(GITERR_INVALID, "failed to parse signature - %s", msg); return -1; } @@ -228,8 +228,12 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, const char *time_start = email_end + 2; const char *time_end; - if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) + if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) { + git__free(sig->name); + git__free(sig->email); + sig->name = sig->email = NULL; return signature_error("invalid Unix timestamp"); + } /* do we have a timezone? */ if (time_end + 1 < buffer_end) { @@ -251,7 +255,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, * only store timezone if it's not overflowing; * see http://www.worldtimezone.com/faq.html */ - if (hours < 14 && mins < 59) { + if (hours <= 14 && mins <= 59) { sig->when.offset = (hours * 60) + mins; if (tz_start[0] == '-') sig->when.offset = -sig->when.offset; diff --git a/src/signature.h b/src/signature.h index 75265df52..40d7c54f9 100644 --- a/src/signature.h +++ b/src/signature.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_signature_h__ #define INCLUDE_signature_h__ +#include "common.h" + #include "git2/common.h" #include "git2/signature.h" #include "repository.h" diff --git a/src/socket_stream.c b/src/socket_stream.c index 71f49118e..4c795e462 100644 --- a/src/socket_stream.c +++ b/src/socket_stream.c @@ -5,11 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "socket_stream.h" + #include "posix.h" #include "netops.h" #include "stream.h" -#include "socket_stream.h" #ifndef _WIN32 # include <sys/types.h> @@ -57,7 +57,7 @@ static int close_socket(GIT_SOCKET s) return -1; if (0 != WSACleanup()) { - giterr_set(GITERR_OS, "Winsock cleanup failed"); + giterr_set(GITERR_OS, "winsock cleanup failed"); return -1; } @@ -82,13 +82,13 @@ int socket_connect(git_stream *stream) WSADATA wsd; if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { - giterr_set(GITERR_OS, "Winsock init failed"); + giterr_set(GITERR_OS, "winsock init failed"); return -1; } if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { WSACleanup(); - giterr_set(GITERR_OS, "Winsock init failed"); + giterr_set(GITERR_OS, "winsock init failed"); return -1; } #endif @@ -99,17 +99,15 @@ int socket_connect(git_stream *stream) if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) { giterr_set(GITERR_NET, - "Failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); + "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); return -1; } for (p = info; p != NULL; p = p->ai_next) { s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (s == INVALID_SOCKET) { - net_set_error("error creating socket"); - break; - } + if (s == INVALID_SOCKET) + continue; if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) break; @@ -121,7 +119,7 @@ int socket_connect(git_stream *stream) /* Oops, we couldn't connect to any address */ if (s == INVALID_SOCKET && p == NULL) { - giterr_set(GITERR_OS, "Failed to connect to %s", st->host); + giterr_set(GITERR_OS, "failed to connect to %s", st->host); p_freeaddrinfo(info); return -1; } diff --git a/src/socket_stream.h b/src/socket_stream.h index 8e9949fcd..1f9ff78d1 100644 --- a/src/socket_stream.h +++ b/src/socket_stream.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_socket_stream_h__ #define INCLUDE_socket_stream_h__ +#include "common.h" + #include "netops.h" typedef struct { diff --git a/src/sortedcache.c b/src/sortedcache.c index 5c2a167a7..76672d9f7 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -1,6 +1,11 @@ -#include "sortedcache.h" +/* + * 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. + */ -GIT__USE_STRMAP +#include "sortedcache.h" int git_sortedcache_new( git_sortedcache **out, @@ -27,7 +32,7 @@ int git_sortedcache_new( goto fail; if (git_rwlock_init(&sc->lock)) { - giterr_set(GITERR_OS, "Failed to initialize lock"); + giterr_set(GITERR_OS, "failed to initialize lock"); goto fail; } @@ -162,7 +167,7 @@ int git_sortedcache_wlock(git_sortedcache *sc) GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ if (git_rwlock_wrlock(&sc->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire write lock on cache"); + giterr_set(GITERR_OS, "unable to acquire write lock on cache"); return -1; } return 0; @@ -181,7 +186,7 @@ int git_sortedcache_rlock(git_sortedcache *sc) GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ if (git_rwlock_rdlock(&sc->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire read lock on cache"); + giterr_set(GITERR_OS, "unable to acquire read lock on cache"); return -1; } return 0; @@ -200,6 +205,7 @@ void git_sortedcache_runlock(git_sortedcache *sc) int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf) { int error, fd; + struct stat st; if ((error = git_sortedcache_wlock(sc)) < 0) return error; @@ -207,19 +213,27 @@ int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf) if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0) goto unlock; - if (!git__is_sizet(sc->stamp.size)) { - giterr_set(GITERR_INVALID, "Unable to load file larger than size_t"); + if ((fd = git_futils_open_ro(sc->path)) < 0) { + error = fd; + goto unlock; + } + + if (p_fstat(fd, &st) < 0) { + giterr_set(GITERR_OS, "failed to stat file"); error = -1; + (void)p_close(fd); goto unlock; } - if ((fd = git_futils_open_ro(sc->path)) < 0) { - error = fd; + if (!git__is_sizet(st.st_size)) { + giterr_set(GITERR_INVALID, "unable to load file larger than size_t"); + error = -1; + (void)p_close(fd); goto unlock; } if (buf) - error = git_futils_readbuffer_fd(buf, fd, (size_t)sc->stamp.size); + error = git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size); (void)p_close(fd); @@ -285,13 +299,13 @@ int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key) item_key = ((char *)item) + sc->item_path_offset; memcpy(item_key, key, keylen); - pos = kh_put(str, sc->map, item_key, &error); + pos = git_strmap_put(sc->map, item_key, &error); if (error < 0) goto done; if (!error) - kh_key(sc->map, pos) = item_key; - kh_val(sc->map, pos) = item; + git_strmap_set_key_at(sc->map, pos, item_key); + git_strmap_set_value_at(sc->map, pos, item); error = git_vector_insert(&sc->items, item); if (error < 0) @@ -364,7 +378,7 @@ int git_sortedcache_remove(git_sortedcache *sc, size_t pos) */ if ((item = git_vector_get(&sc->items, pos)) == NULL) { - giterr_set(GITERR_INVALID, "Removing item out of range"); + giterr_set(GITERR_INVALID, "removing item out of range"); return GIT_ENOTFOUND; } diff --git a/src/sortedcache.h b/src/sortedcache.h index 4cacad62b..a53ff48a3 100644 --- a/src/sortedcache.h +++ b/src/sortedcache.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_sorted_cache_h__ #define INCLUDE_sorted_cache_h__ +#include "common.h" + #include "util.h" #include "fileops.h" #include "vector.h" diff --git a/src/stash.c b/src/stash.c index f5f4f36bf..8a8c57a83 100644 --- a/src/stash.c +++ b/src/stash.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "repository.h" #include "commit.h" #include "message.h" @@ -27,7 +28,7 @@ static int create_error(int error, const char *msg) { - giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg); + giterr_set(GITERR_STASH, "cannot stash changes - %s", msg); return error; } @@ -36,7 +37,7 @@ static int retrieve_head(git_reference **out, git_repository *repo) int error = git_repository_head(out, repo); if (error == GIT_EUNBORNBRANCH) - return create_error(error, "You do not have the initial commit yet."); + return create_error(error, "you do not have the initial commit yet."); return error; } @@ -198,7 +199,7 @@ static int stash_update_index_from_diff( /* Unimplemented */ giterr_set( GITERR_INVALID, - "Cannot update index. Unimplemented status (%d)", + "cannot update index. Unimplemented status (%d)", delta->status); return -1; } @@ -479,7 +480,7 @@ static int ensure_there_are_changes_to_stash( return 0; if (!error) - return create_error(GIT_ENOTFOUND, "There is nothing to stash."); + return create_error(GIT_ENOTFOUND, "there is nothing to stash."); return error; } @@ -593,7 +594,7 @@ static int retrieve_stash_commit( max = git_reflog_entrycount(reflog); if (!max || index > max - 1) { error = GIT_ENOTFOUND; - giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index); + giterr_set(GITERR_STASH, "no stashed state at position %" PRIuZ, index); goto cleanup; } @@ -1036,7 +1037,7 @@ int git_stash_drop( if (!max || index > max - 1) { error = GIT_ENOTFOUND; - giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index); + giterr_set(GITERR_STASH, "no stashed state at position %" PRIuZ, index); goto cleanup; } diff --git a/src/status.c b/src/status.c index e610f5fe1..03682bc72 100644 --- a/src/status.c +++ b/src/status.c @@ -5,13 +5,13 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "status.h" + #include "git2.h" #include "fileops.h" #include "hash.h" #include "vector.h" #include "tree.h" -#include "status.h" #include "git2/status.h" #include "repository.h" #include "ignore.h" @@ -243,13 +243,13 @@ static int status_validate_options(const git_status_options *opts) GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) { - giterr_set(GITERR_INVALID, "Unknown status 'show' option"); + giterr_set(GITERR_INVALID, "unknown status 'show' option"); return -1; } if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 && (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) { - giterr_set(GITERR_INVALID, "Updating index from status " + giterr_set(GITERR_INVALID, "updating index from status " "is not allowed when index refresh is disabled"); return -1; } @@ -510,13 +510,13 @@ int git_status_file( if (error < 0 && sfi.ambiguous) { giterr_set(GITERR_INVALID, - "Ambiguous path '%s' given to git_status_file", sfi.expected); + "ambiguous path '%s' given to git_status_file", sfi.expected); error = GIT_EAMBIGUOUS; } if (!error && !sfi.count) { giterr_set(GITERR_INVALID, - "Attempt to get status of nonexistent file '%s'", path); + "attempt to get status of nonexistent file '%s'", path); error = GIT_ENOTFOUND; } diff --git a/src/status.h b/src/status.h index 33008b89c..907479a22 100644 --- a/src/status.h +++ b/src/status.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_status_h__ #define INCLUDE_status_h__ +#include "common.h" + #include "diff.h" #include "git2/status.h" #include "git2/diff.h" diff --git a/src/stransport_stream.c b/src/stransport_stream.c index 50ed9452c..18371ef92 100644 --- a/src/stransport_stream.c +++ b/src/stransport_stream.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "stransport_stream.h" + #ifdef GIT_SECURE_TRANSPORT #include <CoreFoundation/CoreFoundation.h> diff --git a/src/stransport_stream.h b/src/stransport_stream.h index 714f90273..f5bbfd952 100644 --- a/src/stransport_stream.h +++ b/src/stransport_stream.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_stransport_stream_h__ #define INCLUDE_stransport_stream_h__ +#include "common.h" + #include "git2/sys/stream.h" extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port); diff --git a/src/strmap.c b/src/strmap.c index b26a13d1f..de6826d03 100644 --- a/src/strmap.c +++ b/src/strmap.c @@ -7,6 +7,101 @@ #include "strmap.h" +__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) + +int git_strmap_alloc(git_strmap **map) +{ + if ((*map = kh_init(str)) == NULL) { + giterr_set_oom(); + return -1; + } + + return 0; +} + +void git_strmap_free(git_strmap *map) +{ + kh_destroy(str, map); +} + +void git_strmap_clear(git_strmap *map) +{ + kh_clear(str, map); +} + +size_t git_strmap_num_entries(git_strmap *map) +{ + return kh_size(map); +} + +size_t git_strmap_lookup_index(git_strmap *map, const char *key) +{ + return kh_get(str, map, key); +} + +int git_strmap_valid_index(git_strmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_strmap_exists(git_strmap *map, const char *key) +{ + return kh_get(str, map, key) != kh_end(map); +} + +int git_strmap_has_data(git_strmap *map, size_t idx) +{ + return kh_exist(map, idx); +} + +const char *git_strmap_key(git_strmap *map, size_t idx) +{ + return kh_key(map, idx); +} + +void git_strmap_set_key_at(git_strmap *map, size_t idx, char *key) +{ + kh_val(map, idx) = key; +} + +void *git_strmap_value_at(git_strmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +void git_strmap_set_value_at(git_strmap *map, size_t idx, void *value) +{ + kh_val(map, idx) = value; +} + +void git_strmap_delete_at(git_strmap *map, size_t idx) +{ + kh_del(str, map, idx); +} + +int git_strmap_put(git_strmap *map, const char *key, int *err) +{ + return kh_put(str, map, key, err); +} + +void git_strmap_insert(git_strmap *map, const char *key, void *value, int *rval) +{ + khiter_t idx = kh_put(str, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_strmap_delete(git_strmap *map, const char *key) +{ + khiter_t idx = git_strmap_lookup_index(map, key); + if (git_strmap_valid_index(map, idx)) + git_strmap_delete_at(map, idx); +} + int git_strmap_next( void **data, git_strmap_iter* iter, diff --git a/src/strmap.h b/src/strmap.h index 520984744..802b92494 100644 --- a/src/strmap.h +++ b/src/strmap.h @@ -20,49 +20,27 @@ __KHASH_TYPE(str, const char *, void *) typedef khash_t(str) git_strmap; typedef khiter_t git_strmap_iter; -#define GIT__USE_STRMAP \ - __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) +int git_strmap_alloc(git_strmap **map); +void git_strmap_free(git_strmap *map); +void git_strmap_clear(git_strmap *map); -#define git_strmap_alloc(hp) \ - ((*(hp) = kh_init(str)) == NULL) ? giterr_set_oom(), -1 : 0 +size_t git_strmap_num_entries(git_strmap *map); -#define git_strmap_free(h) kh_destroy(str, h), h = NULL -#define git_strmap_clear(h) kh_clear(str, h) +size_t git_strmap_lookup_index(git_strmap *map, const char *key); +int git_strmap_valid_index(git_strmap *map, size_t idx); -#define git_strmap_num_entries(h) kh_size(h) +int git_strmap_exists(git_strmap *map, const char *key); +int git_strmap_has_data(git_strmap *map, size_t idx); -#define git_strmap_lookup_index(h, k) kh_get(str, h, k) -#define git_strmap_valid_index(h, idx) (idx != kh_end(h)) +const char *git_strmap_key(git_strmap *map, size_t idx); +void git_strmap_set_key_at(git_strmap *map, size_t idx, char *key); +void *git_strmap_value_at(git_strmap *map, size_t idx); +void git_strmap_set_value_at(git_strmap *map, size_t idx, void *value); +void git_strmap_delete_at(git_strmap *map, size_t idx); -#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h)) -#define git_strmap_has_data(h, idx) kh_exist(h, idx) - -#define git_strmap_key(h, idx) kh_key(h, idx) -#define git_strmap_value_at(h, idx) kh_val(h, idx) -#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v -#define git_strmap_delete_at(h, idx) kh_del(str, h, idx) - -#define git_strmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(str, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_strmap_insert2(h, key, val, oldv, rval) do { \ - khiter_t __pos = kh_put(str, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) { \ - oldv = kh_val(h, __pos); \ - kh_key(h, __pos) = key; \ - } else { oldv = NULL; } \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_strmap_delete(h, key) do { \ - khiter_t __pos = git_strmap_lookup_index(h, key); \ - if (git_strmap_valid_index(h, __pos)) \ - git_strmap_delete_at(h, __pos); } while (0) +int git_strmap_put(git_strmap *map, const char *key, int *err); +void git_strmap_insert(git_strmap *map, const char *key, void *value, int *rval); +void git_strmap_delete(git_strmap *map, const char *key); #define git_strmap_foreach kh_foreach #define git_strmap_foreach_value kh_foreach_value diff --git a/src/submodule.c b/src/submodule.c index 86ad53be0..6c3e5f6bd 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -5,7 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "submodule.h" + #include "git2/config.h" #include "git2/sys/config.h" #include "git2/types.h" @@ -17,11 +18,11 @@ #include "config_file.h" #include "config.h" #include "repository.h" -#include "submodule.h" #include "tree.h" #include "iterator.h" #include "path.h" #include "index.h" +#include "worktree.h" #define GIT_MODULES_FILE ".gitmodules" @@ -124,8 +125,8 @@ static void submodule_set_lookup_error(int error, const char *name) return; giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? - "No submodule named '%s'" : - "Submodule '%s' has not been added yet", name); + "no submodule named '%s'" : + "submodule '%s' has not been added yet", name); } typedef struct { @@ -149,40 +150,52 @@ static int find_by_path(const git_config_entry *entry, void *payload) } /** - * Find out the name of a submodule from its path + * Release the name map returned by 'load_submodule_names'. + */ +static void free_submodule_names(git_strmap *names) +{ + git_buf *name = 0; + if (names == NULL) + return; + git_strmap_foreach_value(names, name, { + git__free(name); + }); + git_strmap_free(names); + return; +} + +/** + * Map submodule paths to names. + * TODO: for some use-cases, this might need case-folding on a + * case-insensitive filesystem */ -static int name_from_path(git_buf *out, git_config *cfg, const char *path) +static int load_submodule_names(git_strmap *out, git_config *cfg) { const char *key = "submodule\\..*\\.path"; git_config_iterator *iter; git_config_entry *entry; - int error; + git_buf buf = GIT_BUF_INIT; + int rval; + int error = 0; if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0) return error; - while ((error = git_config_next(&entry, iter)) == 0) { + while (git_config_next(&entry, iter) == 0) { const char *fdot, *ldot; - /* TODO: this should maybe be strcasecmp on a case-insensitive fs */ - if (strcmp(path, entry->value) != 0) - continue; - fdot = strchr(entry->name, '.'); ldot = strrchr(entry->name, '.'); - git_buf_clear(out); - git_buf_put(out, fdot + 1, ldot - fdot - 1); - goto cleanup; - } - - if (error == GIT_ITEROVER) { - giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path); - error = GIT_ENOTFOUND; + git_buf_put(&buf, fdot + 1, ldot - fdot - 1); + git_strmap_insert(out, entry->value, git_buf_detach(&buf), &rval); + if (rval < 0) { + giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table"); + return -1; + } } -cleanup: git_config_iterator_free(iter); - return error; + return 0; } int git_submodule_lookup( @@ -196,6 +209,22 @@ int git_submodule_lookup( assert(repo && name); + if (repo->is_bare) { + giterr_set(GITERR_SUBMODULE, "cannot get submodules without a working tree"); + return -1; + } + + if (repo->submodule_cache != NULL) { + khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name); + if (git_strmap_valid_index(repo->submodule_cache, pos)) { + if (out) { + *out = git_strmap_value_at(repo->submodule_cache, pos); + GIT_REFCOUNT_INC(*out); + } + return 0; + } + } + if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; @@ -306,7 +335,7 @@ static int submodule_get_or_create(git_submodule **out, git_repository *repo, gi if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; - pos = kh_put(str, map, sm->name, &error); + pos = git_strmap_put(map, sm->name, &error); /* nobody can beat us to adding it */ assert(error != 0); if (error < 0) { @@ -324,89 +353,108 @@ done: static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg) { - int error; - git_iterator *i; - const git_index_entry *entry; - git_buf name = GIT_BUF_INIT; - - if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) - return error; + int error; + git_iterator *i = NULL; + const git_index_entry *entry; + git_strmap *names = 0; - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(map, entry->path); - git_submodule *sm; + git_strmap_alloc(&names); + if ((error = load_submodule_names(names, cfg))) + goto done; - git_buf_clear(&name); - if (!name_from_path(&name, cfg, entry->path)) { - git_strmap_lookup_index(map, name.ptr); - } + if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) + goto done; - if (git_strmap_valid_index(map, pos)) { - sm = git_strmap_value_at(map, pos); + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; + + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + + if (S_ISGITLINK(entry->mode)) + submodule_update_from_index_entry(sm, entry); + else + sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + khiter_t name_pos; + const char *name; + + name_pos = git_strmap_lookup_index(names, entry->path); + if (git_strmap_valid_index(names, name_pos)) { + name = git_strmap_value_at(names, name_pos); + } else { + name = entry->path; + } - if (S_ISGITLINK(entry->mode)) - submodule_update_from_index_entry(sm, entry); - else - sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) { - submodule_update_from_index_entry(sm, entry); - git_submodule_free(sm); - } - } - } + if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) { + submodule_update_from_index_entry(sm, entry); + git_submodule_free(sm); + } + } + } - if (error == GIT_ITEROVER) - error = 0; + if (error == GIT_ITEROVER) + error = 0; - git_buf_free(&name); - git_iterator_free(i); +done: + git_iterator_free(i); + free_submodule_names(names); - return error; + return error; } static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg) { - int error; - git_iterator *i; - const git_index_entry *entry; - git_buf name = GIT_BUF_INIT; - - if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) - return error; - - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(map, entry->path); - git_submodule *sm; + int error; + git_iterator *i = NULL; + const git_index_entry *entry; + git_strmap *names = 0; + git_strmap_alloc(&names); + if ((error = load_submodule_names(names, cfg))) + goto done; - git_buf_clear(&name); - if (!name_from_path(&name, cfg, entry->path)) { - git_strmap_lookup_index(map, name.ptr); - } + if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) + goto done; - if (git_strmap_valid_index(map, pos)) { - sm = git_strmap_value_at(map, pos); + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; + + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + + if (S_ISGITLINK(entry->mode)) + submodule_update_from_head_data(sm, entry->mode, &entry->id); + else + sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + khiter_t name_pos; + const char *name; + + name_pos = git_strmap_lookup_index(names, entry->path); + if (git_strmap_valid_index(names, name_pos)) { + name = git_strmap_value_at(names, name_pos); + } else { + name = entry->path; + } - if (S_ISGITLINK(entry->mode)) - submodule_update_from_head_data(sm, entry->mode, &entry->id); - else - sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) { - submodule_update_from_head_data( - sm, entry->mode, &entry->id); - git_submodule_free(sm); - } - } - } + if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) { + submodule_update_from_head_data( + sm, entry->mode, &entry->id); + git_submodule_free(sm); + } + } + } - if (error == GIT_ITEROVER) - error = 0; + if (error == GIT_ITEROVER) + error = 0; - git_buf_free(&name); - git_iterator_free(i); +done: + git_iterator_free(i); + free_submodule_names(names); - return error; + return error; } /* If have_sm is true, sm is populated, otherwise map an repo are. */ @@ -416,7 +464,7 @@ typedef struct { git_repository *repo; } lfc_data; -static int all_submodules(git_repository *repo, git_strmap *map) +int git_submodule__map(git_repository *repo, git_strmap *map) { int error = 0; git_index *idx = NULL; @@ -470,12 +518,12 @@ static int all_submodules(git_repository *repo, git_strmap *map) goto cleanup; } /* add back submodule information from index */ - if (idx) { + if (mods && idx) { if ((error = submodules_from_index(map, idx, mods)) < 0) goto cleanup; } /* add submodule information from HEAD */ - if (head) { + if (mods && head) { if ((error = submodules_from_head(map, head, mods)) < 0) goto cleanup; } @@ -506,14 +554,19 @@ int git_submodule_foreach( int error; size_t i; + if (repo->is_bare) { + giterr_set(GITERR_SUBMODULE, "cannot get submodules without a working tree"); + return -1; + } + if ((error = git_strmap_alloc(&submodules)) < 0) return error; - if ((error = all_submodules(repo, submodules)) < 0) + if ((error = git_submodule__map(repo, submodules)) < 0) goto done; if (!(error = git_vector_init( - &snapshot, kh_size(submodules), submodule_cmp))) { + &snapshot, git_strmap_num_entries(submodules), submodule_cmp))) { git_strmap_foreach_value(submodules, sm, { if ((error = git_vector_insert(&snapshot, sm)) < 0) @@ -574,8 +627,10 @@ static int submodule_repo_init( * Old style: sub-repo goes directly into repo/<name>/.git/ */ if (use_gitlink) { - error = git_buf_join3( - &repodir, '/', git_repository_path(parent_repo), "modules", path); + error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES); + if (error < 0) + goto cleanup; + error = git_buf_joinpath(&repodir, repodir.ptr, path); if (error < 0) goto cleanup; @@ -618,7 +673,7 @@ int git_submodule_add_setup( giterr_clear(); else { giterr_set(GITERR_SUBMODULE, - "Attempt to add submodule '%s' that already exists", path); + "attempt to add submodule '%s' that already exists", path); return GIT_EEXISTS; } @@ -628,7 +683,7 @@ int git_submodule_add_setup( path += strlen(git_repository_workdir(repo)); if (git_path_root(path) >= 0) { - giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path"); + giterr_set(GITERR_SUBMODULE, "submodule path must be a relative path"); error = -1; goto cleanup; } @@ -637,7 +692,7 @@ int git_submodule_add_setup( if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) { giterr_set(GITERR_SUBMODULE, - "Adding submodules to a bare repository is not supported"); + "adding submodules to a bare repository is not supported"); return -1; } @@ -758,7 +813,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) /* read stat information for submodule working directory */ if (p_stat(path.ptr, &st) < 0) { giterr_set(GITERR_SUBMODULE, - "Cannot add submodule without working directory"); + "cannot add submodule without working directory"); error = -1; goto cleanup; } @@ -771,7 +826,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) /* calling git_submodule_open will have set sm->wd_oid if possible */ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) { giterr_set(GITERR_SUBMODULE, - "Cannot add submodule without HEAD to index"); + "cannot add submodule without HEAD to index"); error = -1; goto cleanup; } @@ -861,7 +916,7 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur } else if (strchr(url, ':') != NULL || url[0] == '/') { error = git_buf_sets(out, url); } else { - giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL"); + giterr_set(GITERR_SUBMODULE, "invalid format for submodule URL"); error = -1; } @@ -1042,8 +1097,10 @@ static int submodule_repo_create( * <repo-dir>/modules/<name>/ with a gitlink in the * sub-repo workdir directory to that repository. */ - error = git_buf_join3( - &repodir, '/', git_repository_path(parent_repo), "modules", path); + error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES); + if (error < 0) + goto cleanup; + error = git_buf_joinpath(&repodir, repodir.ptr, path); if (error < 0) goto cleanup; @@ -1133,7 +1190,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio goto done; if (!init) { - giterr_set(GITERR_SUBMODULE, "Submodule is not initialized."); + giterr_set(GITERR_SUBMODULE, "submodule is not initialized"); error = GIT_ERROR; goto done; } @@ -1160,13 +1217,14 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio * will checkout the specific commit manually. */ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; - update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy; if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 || (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 || (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0) goto done; } else { + const git_oid *oid; + /** * Work dir is initialized - look up the commit in the parent repository's index, * update the workdir contents of the subrepository, and set the subrepository's @@ -1175,8 +1233,14 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio if ((error = git_submodule_open(&sub_repo, sm)) < 0) goto done; + if ((oid = git_submodule_index_id(sm)) == NULL) { + giterr_set(GITERR_SUBMODULE, "could not get ID of submodule in index"); + error = -1; + goto done; + } + /* Look up the target commit in the submodule. */ - if ((error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0) { + if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJ_COMMIT)) < 0) { /* If it isn't found then fetch and try again. */ if (error != GIT_ENOTFOUND || !update_options.allow_fetch || (error = lookup_default_remote(&remote, sub_repo)) < 0 || @@ -1215,7 +1279,7 @@ int git_submodule_init(git_submodule *sm, int overwrite) if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", sm->name); + "no URL configured for submodule '%s'", sm->name); return -1; } @@ -1259,7 +1323,7 @@ int git_submodule_sync(git_submodule *sm) if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", sm->name); + "no URL configured for submodule '%s'", sm->name); return -1; } @@ -1502,13 +1566,22 @@ int git_submodule__status( return 0; } - /* refresh the index OID */ - if (submodule_update_index(sm) < 0) - return -1; + /* If the user has requested caching submodule state, performing these + * expensive operations (especially `submodule_update_head`, which is + * bottlenecked on `git_repository_head_tree`) eliminates much of the + * advantage. We will, therefore, interpret the request for caching to + * apply here to and skip them. + */ - /* refresh the HEAD OID */ - if (submodule_update_head(sm) < 0) - return -1; + if (sm->repo->submodule_cache == NULL) { + /* refresh the index OID */ + if (submodule_update_index(sm) < 0) + return -1; + + /* refresh the HEAD OID */ + if (submodule_update_head(sm) < 0) + return -1; + } /* for ignore == dirty, don't scan the working directory */ if (ign == GIT_SUBMODULE_IGNORE_DIRTY) { @@ -1566,7 +1639,6 @@ int git_submodule_location(unsigned int *location, git_submodule *sm) location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL); } - /* * INTERNAL FUNCTIONS */ @@ -1578,7 +1650,7 @@ static int submodule_alloc( git_submodule *sm; if (!name || !(namelen = strlen(name))) { - giterr_set(GITERR_SUBMODULE, "Invalid submodule name"); + giterr_set(GITERR_SUBMODULE, "invalid submodule name"); return -1; } @@ -1630,7 +1702,7 @@ void git_submodule_free(git_submodule *sm) static int submodule_config_error(const char *property, const char *value) { giterr_set(GITERR_INVALID, - "Invalid value for submodule '%s' property: '%s'", property, value); + "invalid value for submodule '%s' property: '%s'", property, value); return -1; } @@ -1813,7 +1885,7 @@ static int submodule_load_each(const git_config_entry *entry, void *payload) goto done; } - git_strmap_insert(map, sm->name, sm, error); + git_strmap_insert(map, sm->name, sm, &error); assert(error != 0); if (error < 0) goto done; @@ -1968,7 +2040,7 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo) if (error == GIT_ENOTFOUND) giterr_set( GITERR_SUBMODULE, - "Cannot get default remote for submodule - no local tracking " + "cannot get default remote for submodule - no local tracking " "branch for HEAD and origin does not exist"); return error; @@ -1977,17 +2049,28 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo) static int get_url_base(git_buf *url, git_repository *repo) { int error; + git_worktree *wt = NULL; git_remote *remote = NULL; - if (!(error = lookup_default_remote(&remote, repo))) { + if ((error = lookup_default_remote(&remote, repo)) == 0) { error = git_buf_sets(url, git_remote_url(remote)); - git_remote_free(remote); - } - else if (error == GIT_ENOTFOUND) { - /* if repository does not have a default remote, use workdir instead */ + goto out; + } else if (error != GIT_ENOTFOUND) + goto out; + else giterr_clear(); + + /* if repository does not have a default remote, use workdir instead */ + if (git_repository_is_worktree(repo)) { + if ((error = git_worktree_open_from_repository(&wt, repo)) < 0) + goto out; + error = git_buf_sets(url, wt->parent_path); + } else error = git_buf_sets(url, git_repository_workdir(repo)); - } + +out: + git_remote_free(remote); + git_worktree_free(wt); return error; } diff --git a/src/submodule.h b/src/submodule.h index 2ef2031b3..72867a322 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_submodule_h__ #define INCLUDE_submodule_h__ +#include "common.h" + #include "git2/submodule.h" #include "git2/repository.h" #include "fileops.h" @@ -143,4 +145,7 @@ extern int git_submodule_parse_ignore( extern int git_submodule_parse_update( git_submodule_update_t *out, const char *value); +extern int git_submodule__map( + git_repository *repo, + git_strmap *map); #endif diff --git a/src/sysdir.c b/src/sysdir.c index 29e53e239..7480e82fd 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "sysdir.h" + #include "global.h" #include "buffer.h" #include "path.h" @@ -150,7 +150,7 @@ int git_sysdir_get_str( GITERR_CHECK_ERROR(git_sysdir_get(&path, which)); if (!out || path->size >= outlen) { - giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); + giterr_set(GITERR_NOMEMORY, "buffer is too short for the path"); return GIT_EBUFS; } @@ -171,7 +171,7 @@ int git_sysdir_set(git_sysdir_t which, const char *search_path) expand_path = strstr(search_path, PATH_MAGIC); /* reset the default if this path has been cleared */ - if (!search_path || expand_path) + if (!search_path) git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf); /* if $PATH is not referenced, then just set the path */ @@ -241,7 +241,7 @@ static int git_sysdir_find_in_dirlist( done: git_buf_free(path); - giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name); + giterr_set(GITERR_OS, "the %s file '%s' doesn't exist", label, name); return GIT_ENOTFOUND; } @@ -275,3 +275,14 @@ int git_sysdir_find_template_dir(git_buf *path) path, NULL, GIT_SYSDIR_TEMPLATE, "template"); } +int git_sysdir_expand_global_file(git_buf *path, const char *filename) +{ + int error; + + if ((error = git_sysdir_find_global_file(path, NULL)) == 0) { + if (filename) + error = git_buf_joinpath(path, path->ptr, filename); + } + + return error; +} diff --git a/src/sysdir.h b/src/sysdir.h index 11878981c..8f4466b46 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -8,6 +8,7 @@ #define INCLUDE_sysdir_h__ #include "common.h" + #include "posix.h" #include "buffer.h" @@ -55,6 +56,18 @@ extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename) */ extern int git_sysdir_find_template_dir(git_buf *path); +/** + * Expand the name of a "global" file (i.e. one in a user's home + * directory). Unlike `find_global_file` (above), this makes no + * attempt to check for the existence of the file, and is useful if + * you want the full path regardless of existence. + * + * @param path buffer to write the full path into + * @param filename name of file in the home directory + * @return 0 on success or -1 on error + */ +extern int git_sysdir_expand_global_file(git_buf *path, const char *filename); + typedef enum { GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_GLOBAL = 1, @@ -5,9 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" -#include "commit.h" #include "tag.h" + +#include "commit.h" #include "signature.h" #include "message.h" #include "git2/object.h" @@ -61,7 +61,7 @@ const char *git_tag_message(const git_tag *t) static int tag_error(const char *str) { - giterr_set(GITERR_TAG, "Failed to parse tag. %s", str); + giterr_set(GITERR_TAG, "failed to parse tag: %s", str); return -1; } @@ -76,13 +76,13 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) char *search; if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0) - return tag_error("Object field invalid"); + return tag_error("object field invalid"); if (buffer + 5 >= buffer_end) - return tag_error("Object too short"); + return tag_error("object too short"); if (memcmp(buffer, "type ", 5) != 0) - return tag_error("Type field not found"); + return tag_error("type field not found"); buffer += 5; tag->type = GIT_OBJ_BAD; @@ -91,7 +91,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) size_t type_length = strlen(tag_types[i]); if (buffer + type_length >= buffer_end) - return tag_error("Object too short"); + return tag_error("object too short"); if (memcmp(buffer, tag_types[i], type_length) == 0) { tag->type = i; @@ -101,19 +101,19 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) } if (tag->type == GIT_OBJ_BAD) - return tag_error("Invalid object type"); + return tag_error("invalid object type"); if (buffer + 4 >= buffer_end) - return tag_error("Object too short"); + return tag_error("object too short"); if (memcmp(buffer, "tag ", 4) != 0) - return tag_error("Tag field not found"); + return tag_error("tag field not found"); buffer += 4; search = memchr(buffer, '\n', buffer_end - buffer); if (search == NULL) - return tag_error("Object too short"); + return tag_error("object too short"); text_len = search - buffer; @@ -234,7 +234,7 @@ static int write_tag_annotation( on_error: git_buf_free(&tag); - giterr_set(GITERR_OBJECT, "Failed to create tag annotation."); + giterr_set(GITERR_OBJECT, "failed to create tag annotation"); return -1; } @@ -257,7 +257,7 @@ static int git_tag_create__internal( assert(!create_tag_annotation || (tagger && message)); if (git_object_owner(target) != repo) { - giterr_set(GITERR_INVALID, "The given target does not belong to this repository"); + giterr_set(GITERR_INVALID, "the given target does not belong to this repository"); return -1; } @@ -269,7 +269,7 @@ static int git_tag_create__internal( * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { git_buf_free(&ref_name); - giterr_set(GITERR_TAG, "Tag already exists"); + giterr_set(GITERR_TAG, "tag already exists"); return GIT_EEXISTS; } @@ -349,7 +349,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu goto on_error; if (tag.type != target_obj->cached.type) { - giterr_set(GITERR_TAG, "The type for the given target is invalid"); + giterr_set(GITERR_TAG, "the type for the given target is invalid"); goto on_error; } @@ -366,7 +366,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu /** Ensure the tag name doesn't conflict with an already existing * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { - giterr_set(GITERR_TAG, "Tag already exists"); + giterr_set(GITERR_TAG, "tag already exists"); return GIT_EEXISTS; } @@ -7,6 +7,8 @@ #ifndef INCLUDE_tag_h__ #define INCLUDE_tag_h__ +#include "common.h" + #include "git2/tag.h" #include "repository.h" #include "odb.h" diff --git a/src/thread-utils.c b/src/thread-utils.c index dc9b2f09e..e5ec6a843 100644 --- a/src/thread-utils.c +++ b/src/thread-utils.c @@ -4,6 +4,7 @@ * 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 "common.h" #include "thread-utils.h" diff --git a/src/thread-utils.h b/src/thread-utils.h index f0161989f..2df2aeb99 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -7,6 +7,12 @@ #ifndef INCLUDE_thread_utils_h__ #define INCLUDE_thread_utils_h__ +#if defined(__GNUC__) && defined(GIT_THREADS) +# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)) +# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF +# endif +#endif + /* Common operations even if threading has been disabled */ typedef struct { #if defined(GIT_WIN32) diff --git a/src/tls_stream.c b/src/tls_stream.c index 83e2d064a..fefb92f26 100644 --- a/src/tls_stream.c +++ b/src/tls_stream.c @@ -5,8 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "tls_stream.h" + #include "git2/errors.h" -#include "common.h" #include "openssl_stream.h" #include "stransport_stream.h" diff --git a/src/tls_stream.h b/src/tls_stream.h index 98a704174..8971f7efc 100644 --- a/src/tls_stream.h +++ b/src/tls_stream.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_tls_stream_h__ #define INCLUDE_tls_stream_h__ +#include "common.h" + #include "git2/sys/stream.h" /** diff --git a/src/trace.c b/src/trace.c index ee5039f56..080d1e891 100644 --- a/src/trace.c +++ b/src/trace.c @@ -5,10 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "trace.h" + #include "buffer.h" -#include "common.h" #include "global.h" -#include "trace.h" #include "git2/trace.h" #ifdef GIT_TRACE @@ -32,7 +32,7 @@ int git_trace_set(git_trace_level_t level, git_trace_callback callback) GIT_UNUSED(callback); giterr_set(GITERR_INVALID, - "This version of libgit2 was not built with tracing."); + "this version of libgit2 was not built with tracing."); return -1; #endif } diff --git a/src/trace.h b/src/trace.h index 486084d01..498944035 100644 --- a/src/trace.h +++ b/src/trace.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_trace_h__ #define INCLUDE_trace_h__ +#include "common.h" + #include <git2/trace.h> #include "buffer.h" diff --git a/src/transaction.c b/src/transaction.c index 2c8a1e8bd..675023afd 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -5,7 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "transaction.h" + #include "repository.h" #include "strmap.h" #include "refdb.h" @@ -19,8 +20,6 @@ #include "git2/sys/refs.h" #include "git2/sys/refdb_backend.h" -GIT__USE_STRMAP - typedef enum { TRANSACTION_NONE, TRANSACTION_REFS, @@ -120,7 +119,7 @@ int git_transaction_lock_ref(git_transaction *tx, const char *refname) if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0) return error; - git_strmap_insert(tx->locks, node->name, node, error); + git_strmap_insert(tx->locks, node->name, node, &error); if (error < 0) goto cleanup; @@ -323,7 +322,6 @@ static int update_target(git_refdb *db, transaction_node *node) int git_transaction_commit(git_transaction *tx) { transaction_node *node; - git_strmap_iter pos; int error = 0; assert(tx); @@ -335,11 +333,7 @@ int git_transaction_commit(git_transaction *tx) return error; } - for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { - if (!git_strmap_has_data(tx->locks, pos)) - continue; - - node = git_strmap_value_at(tx->locks, pos); + git_strmap_foreach_value(tx->locks, node, { if (node->reflog) { if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0) return error; @@ -349,7 +343,7 @@ int git_transaction_commit(git_transaction *tx) if ((error = update_target(tx->db, node)) < 0) return error; } - } + }); return 0; } @@ -358,7 +352,6 @@ void git_transaction_free(git_transaction *tx) { transaction_node *node; git_pool pool; - git_strmap_iter pos; assert(tx); @@ -373,16 +366,12 @@ void git_transaction_free(git_transaction *tx) } /* start by unlocking the ones we've left hanging, if any */ - for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { - if (!git_strmap_has_data(tx->locks, pos)) - continue; - - node = git_strmap_value_at(tx->locks, pos); + git_strmap_foreach_value(tx->locks, node, { if (node->committed) continue; git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); - } + }); git_refdb_free(tx->db); git_strmap_free(tx->locks); diff --git a/src/transport.c b/src/transport.c index f08d2dc68..f1e18b323 100644 --- a/src/transport.c +++ b/src/transport.c @@ -4,7 +4,9 @@ * 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 "common.h" + #include "git2/types.h" #include "git2/remote.h" #include "git2/net.h" @@ -121,7 +123,7 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url) int error; if ((error = transport_find_fn(&fn, url, ¶m)) == GIT_ENOTFOUND) { - giterr_set(GITERR_NET, "Unsupported URL protocol"); + giterr_set(GITERR_NET, "unsupported URL protocol"); return -1; } else if (error < 0) return error; diff --git a/src/transports/auth.c b/src/transports/auth.c index c1154db34..9597cc249 100644 --- a/src/transports/auth.c +++ b/src/transports/auth.c @@ -5,9 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "auth.h" + #include "git2.h" #include "buffer.h" -#include "auth.h" static int basic_next_token( git_buf *out, git_http_auth_context *ctx, git_cred *c) diff --git a/src/transports/auth.h b/src/transports/auth.h index 52138cf8f..06af79dec 100644 --- a/src/transports/auth.h +++ b/src/transports/auth.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_http_auth_h__ #define INCLUDE_http_auth_h__ +#include "common.h" + #include "git2.h" #include "netops.h" diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c index 8b99fc735..c9bc3043d 100644 --- a/src/transports/auth_negotiate.c +++ b/src/transports/auth_negotiate.c @@ -5,10 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "auth_negotiate.h" + #ifdef GIT_GSSAPI #include "git2.h" -#include "common.h" #include "buffer.h" #include "auth.h" @@ -107,13 +108,13 @@ static int negotiate_next_token( challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; if (challenge_len < 9) { - giterr_set(GITERR_NET, "No negotiate challenge sent from server"); + giterr_set(GITERR_NET, "no negotiate challenge sent from server"); error = -1; goto done; } else if (challenge_len > 9) { if (git_buf_decode_base64(&input_buf, ctx->challenge + 10, challenge_len - 10) < 0) { - giterr_set(GITERR_NET, "Invalid negotiate challenge from server"); + giterr_set(GITERR_NET, "invalid negotiate challenge from server"); error = -1; goto done; } @@ -122,7 +123,7 @@ static int negotiate_next_token( input_token.length = input_buf.size; input_token_ptr = &input_token; } else if (ctx->gss_context != GSS_C_NO_CONTEXT) { - giterr_set(GITERR_NET, "Could not restart authentication"); + giterr_set(GITERR_NET, "could not restart authentication"); error = -1; goto done; } @@ -228,7 +229,7 @@ static int negotiate_init_context( gss_release_oid_set(&status_minor, &mechanism_list); if (!ctx->oid) { - giterr_set(GITERR_NET, "Negotiate authentication is not supported"); + giterr_set(GITERR_NET, "negotiate authentication is not supported"); return -1; } diff --git a/src/transports/auth_negotiate.h b/src/transports/auth_negotiate.h index d7270b7ab..5866c19c3 100644 --- a/src/transports/auth_negotiate.h +++ b/src/transports/auth_negotiate.h @@ -8,6 +8,7 @@ #ifndef INCLUDE_auth_negotiate_h__ #define INCLUDE_auth_negotiate_h__ +#include "common.h" #include "git2.h" #include "auth.h" diff --git a/src/transports/cred.c b/src/transports/cred.c index 49ede48bf..8055e2d65 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "cred.h" + #include "git2.h" #include "smart.h" #include "git2/cred_helpers.h" @@ -216,7 +218,7 @@ int git_cred_ssh_key_memory_new( GIT_UNUSED(passphrase); giterr_set(GITERR_INVALID, - "This version of libgit2 was not built with ssh memory credentials."); + "this version of libgit2 was not built with ssh memory credentials."); return -1; #endif } diff --git a/src/transports/cred.h b/src/transports/cred.h index 2de8deee8..dceab9aa3 100644 --- a/src/transports/cred.h +++ b/src/transports/cred.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_git_cred_h__ #define INCLUDE_git_cred_h__ +#include "common.h" + #include "git2/transport.h" const char *git_cred__username(git_cred *cred); diff --git a/src/transports/cred_helpers.c b/src/transports/cred_helpers.c index 5cc9b0869..fdc56b17e 100644 --- a/src/transports/cred_helpers.c +++ b/src/transports/cred_helpers.c @@ -6,6 +6,7 @@ */ #include "common.h" + #include "git2/cred_helpers.h" int git_cred_userpass( diff --git a/src/transports/git.c b/src/transports/git.c index 6c6acf9c5..e019878b5 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #include "git2.h" #include "buffer.h" #include "netops.h" @@ -45,7 +47,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) delim = strchr(url, '/'); if (delim == NULL) { - giterr_set(GITERR_NET, "Malformed URL"); + giterr_set(GITERR_NET, "malformed URL"); return -1; } @@ -240,7 +242,7 @@ static int _git_uploadpack( return 0; } - giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); + giterr_set(GITERR_NET, "must call UPLOADPACK_LS before UPLOADPACK"); return -1; } @@ -296,7 +298,7 @@ static int _git_receivepack( return 0; } - giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK"); + giterr_set(GITERR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK"); return -1; } diff --git a/src/transports/http.c b/src/transports/http.c index ca1f5042f..332e6280b 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -4,6 +4,9 @@ * 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 "common.h" + #ifndef GIT_WINHTTP #include "git2.h" @@ -208,7 +211,7 @@ static int gen_request( git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url); - git_buf_printf(buf, "User-Agent: git/1.0 (%s)\r\n", user_agent()); + git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent()); git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host); if (s->chunked || content_length > 0) { @@ -403,7 +406,7 @@ static int on_headers_complete(http_parser *parser) t->location) { if (s->redirect_count >= 7) { - giterr_set(GITERR_NET, "Too many redirects"); + giterr_set(GITERR_NET, "too many redirects"); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -428,14 +431,14 @@ static int on_headers_complete(http_parser *parser) /* Check for a 200 HTTP status code. */ if (parser->status_code != 200) { giterr_set(GITERR_NET, - "Unexpected HTTP status code: %d", + "unexpected HTTP status code: %d", parser->status_code); return t->parse_error = PARSE_ERROR_GENERIC; } /* The response must contain a Content-Type header. */ if (!t->content_type) { - giterr_set(GITERR_NET, "No Content-Type header in response"); + giterr_set(GITERR_NET, "no Content-Type header in response"); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -455,7 +458,7 @@ static int on_headers_complete(http_parser *parser) if (strcmp(t->content_type, git_buf_cstr(&buf))) { git_buf_free(&buf); giterr_set(GITERR_NET, - "Invalid Content-Type: %s", + "invalid Content-Type: %s", t->content_type); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -488,7 +491,7 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) return 0; if (ctx->buf_size < len) { - giterr_set(GITERR_NET, "Can't fit data in the buffer"); + giterr_set(GITERR_NET, "can't fit data in the buffer"); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -575,6 +578,9 @@ static int apply_proxy_config(http_subtransport *t) if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0) return error; + opts.credentials = t->owner->proxy.credentials; + opts.certificate_check = t->owner->proxy.certificate_check; + opts.payload = t->owner->proxy.payload; opts.type = GIT_PROXY_SPECIFIED; opts.url = url; error = git_stream_set_proxy(t->io, &opts); @@ -624,13 +630,12 @@ static int http_connect(http_subtransport *t) if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL && git_stream_is_encrypted(t->io)) { git_cert *cert; - int is_valid; + int is_valid = (error == GIT_OK); if ((error = git_stream_certificate(&cert, t->io)) < 0) return error; giterr_clear(); - is_valid = error != GIT_ECERTIFICATE; error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload); if (error < 0) { @@ -857,7 +862,7 @@ static int http_stream_write_single( assert(t->connected); if (s->sent_request) { - giterr_set(GITERR_NET, "Subtransport configured for only one write"); + giterr_set(GITERR_NET, "subtransport configured for only one write"); return -1; } diff --git a/src/transports/local.c b/src/transports/local.c index 4eae9dead..733ed2c20 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -4,7 +4,9 @@ * 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 "common.h" + #include "git2/types.h" #include "git2/net.h" #include "git2/repository.h" @@ -16,6 +18,7 @@ #include "git2/pack.h" #include "git2/commit.h" #include "git2/revparse.h" + #include "pack-objects.h" #include "refs.h" #include "posix.h" @@ -252,7 +255,7 @@ static int local_ls(const git_remote_head ***out, size_t *size, git_transport *t transport_local *t = (transport_local *)transport; if (!t->have_refs) { - giterr_set(GITERR_NET, "The transport has not yet loaded the refs"); + giterr_set(GITERR_NET, "the transport has not yet loaded the refs"); return -1; } @@ -371,11 +374,12 @@ static int local_push( but we forbid all pushes just in case */ if (!remote_repo->is_bare) { error = GIT_EBAREREPO; - giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos."); + giterr_set(GITERR_INVALID, "local push doesn't (yet) support pushing to non-bare repos."); goto on_error; } - if ((error = git_buf_joinpath(&odb_path, git_repository_path(remote_repo), "objects/pack")) < 0) + if ((error = git_repository_item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 + || (error = git_buf_joinpath(&odb_path, odb_path.ptr, "pack")) < 0) goto on_error; error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs); diff --git a/src/transports/smart.c b/src/transports/smart.c index 7a35c39d8..6d5d95fbf 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -4,8 +4,10 @@ * 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 "git2.h" + #include "smart.h" + +#include "git2.h" #include "refs.h" #include "refspec.h" #include "proxy.h" @@ -231,7 +233,7 @@ static int git_smart__connect( else if (GIT_DIRECTION_PUSH == t->direction) service = GIT_SERVICE_RECEIVEPACK_LS; else { - giterr_set(GITERR_NET, "Invalid direction"); + giterr_set(GITERR_NET, "invalid direction"); return -1; } @@ -252,7 +254,7 @@ static int git_smart__connect( pkt = (git_pkt *)git_vector_get(&t->refs, 0); if (!pkt || GIT_PKT_COMMENT != pkt->type) { - giterr_set(GITERR_NET, "Invalid response"); + giterr_set(GITERR_NET, "invalid response"); return -1; } else { /* Remove the comment pkt from the list */ @@ -299,7 +301,7 @@ static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transpo transport_smart *t = (transport_smart *)transport; if (!t->have_refs) { - giterr_set(GITERR_NET, "The transport has not yet loaded the refs"); + giterr_set(GITERR_NET, "the transport has not yet loaded the refs"); return -1; } @@ -319,7 +321,7 @@ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len return -1; if (GIT_DIRECTION_FETCH != t->direction) { - giterr_set(GITERR_NET, "This operation is only valid for fetch"); + giterr_set(GITERR_NET, "this operation is only valid for fetch"); return -1; } @@ -348,7 +350,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream return -1; if (GIT_DIRECTION_PUSH != t->direction) { - giterr_set(GITERR_NET, "This operation is only valid for push"); + giterr_set(GITERR_NET, "this operation is only valid for push"); return -1; } @@ -472,6 +474,12 @@ int git_transport_smart_credentials(git_cred **out, git_transport *transport, co return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload); } +int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport) +{ + transport_smart *t = (transport_smart *) transport; + return git_proxy_options_dup(out, &t->proxy); +} + int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; diff --git a/src/transports/smart.h b/src/transports/smart.h index 0a0c3fc1b..f1ad70479 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -4,6 +4,9 @@ * 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 "common.h" + #include "git2.h" #include "vector.h" #include "netops.h" @@ -26,6 +29,8 @@ #define GIT_CAP_THIN_PACK "thin-pack" #define GIT_CAP_SYMREF "symref" +extern bool git_smart__ofs_delta_enabled; + enum git_pkt_type { GIT_PKT_CMD, GIT_PKT_FLUSH, diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index 2297cc94f..a661dfe13 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -226,7 +226,7 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) /* Check for a bit of consistency */ if (line[GIT_OID_HEXSZ] != ' ') { - giterr_set(GITERR_NET, "Error parsing pkt-line"); + giterr_set(GITERR_NET, "error parsing pkt-line"); error = -1; goto error_out; } @@ -270,7 +270,7 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) line += 3; /* skip "ok " */ if (!(ptr = strchr(line, '\n'))) { - giterr_set(GITERR_NET, "Invalid packet line"); + giterr_set(GITERR_NET, "invalid packet line"); git__free(pkt); return -1; } @@ -327,7 +327,7 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) return 0; out_err: - giterr_set(GITERR_NET, "Invalid packet line"); + giterr_set(GITERR_NET, "invalid packet line"); git__free(pkt->ref); git__free(pkt); return -1; @@ -427,15 +427,23 @@ int git_pkt_parse_line( if (bufflen > 0 && bufflen < (size_t)len) return GIT_EBUFS; + /* + * The length has to be exactly 0 in case of a flush + * packet or greater than PKT_LEN_SIZE, as the decoded + * length includes its own encoded length of four bytes. + */ + if (len != 0 && len < PKT_LEN_SIZE) + return GIT_ERROR; + line += PKT_LEN_SIZE; /* - * TODO: How do we deal with empty lines? Try again? with the next - * line? + * The Git protocol does not specify empty lines as part + * of the protocol. Not knowing what to do with an empty + * line, we should return an error upon hitting one. */ if (len == PKT_LEN_SIZE) { - *head = NULL; - *out = line; - return 0; + giterr_set_str(GITERR_NET, "Invalid empty packet"); + return GIT_ERROR; } if (len == 0) { /* Flush pkt */ @@ -535,7 +543,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca if (len > 0xffff) { giterr_set(GITERR_NET, - "Tried to produce packet with invalid length %" PRIuZ, len); + "tried to produce packet with invalid length %" PRIuZ, len); return -1; } diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 3448fa7fb..d51238f12 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -4,6 +4,9 @@ * 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 "common.h" + #include "git2.h" #include "git2/odb_backend.h" @@ -19,6 +22,8 @@ /* The minimal interval between progress updates (in seconds). */ #define MIN_PROGRESS_UPDATE_INTERVAL 0.5 +bool git_smart__ofs_delta_enabled = true; + int git_smart__store_refs(transport_smart *t, int flushes) { gitno_buffer *buf = &t->buffer; @@ -50,7 +55,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) if ((recvd = gitno_recv(buf)) < 0) return recvd; - if (recvd == 0 && !flush) { + if (recvd == 0) { giterr_set(GITERR_NET, "early EOF"); return GIT_EEOF; } @@ -60,7 +65,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) gitno_consume(buf, line_end); if (pkt->type == GIT_PKT_ERR) { - giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error); + giterr_set(GITERR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error); git__free(pkt); return -1; } @@ -138,7 +143,7 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec if (*ptr == ' ') ptr++; - if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { + if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { caps->common = caps->ofs_delta = 1; ptr += strlen(GIT_CAP_OFS_DELTA); continue; @@ -222,8 +227,12 @@ static int recv_pkt(git_pkt **out, gitno_buffer *buf) if (error < 0 && error != GIT_EBUFS) return error; - if ((ret = gitno_recv(buf)) < 0) + if ((ret = gitno_recv(buf)) < 0) { return ret; + } else if (ret == 0) { + giterr_set(GITERR_NET, "early EOF"); + return GIT_EEOF; + } } while (error); gitno_consume(buf, line_end); @@ -319,7 +328,8 @@ static int wait_while_ack(gitno_buffer *buf) if (pkt->type == GIT_PKT_ACK && (pkt->status != GIT_ACK_CONTINUE && - pkt->status != GIT_ACK_COMMON)) { + pkt->status != GIT_ACK_COMMON && + pkt->status != GIT_ACK_READY)) { git__free(pkt); return 0; } @@ -408,12 +418,12 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c if (i % 20 == 0 && t->rpc) { git_pkt_ack *pkt; - unsigned int i; + unsigned int j; if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) goto on_error; - git_vector_foreach(&t->common, i, pkt) { + git_vector_foreach(&t->common, j, pkt) { if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0) goto on_error; } @@ -428,12 +438,12 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c /* Tell the other end that we're done negotiating */ if (t->rpc && t->common.length > 0) { git_pkt_ack *pkt; - unsigned int i; + unsigned int j; if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) goto on_error; - git_vector_foreach(&t->common, i, pkt) { + git_vector_foreach(&t->common, j, pkt) { if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0) goto on_error; } @@ -724,7 +734,7 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt) static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf) { git_pkt *pkt; - const char *line, *line_end; + const char *line, *line_end = NULL; size_t line_len; int error; int reading_from_buf = data_pkt_buf->size > 0; @@ -759,14 +769,6 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, line_len -= (line_end - line); line = line_end; - /* When a valid packet with no content has been - * read, git_pkt_parse_line does not report an - * error, but the pkt pointer has not been set. - * Handle this by skipping over empty packets. - */ - if (pkt == NULL) - continue; - error = add_push_report_pkt(push, pkt); git_pkt_free(pkt); @@ -821,9 +823,6 @@ static int parse_report(transport_smart *transport, git_push *push) error = 0; - if (pkt == NULL) - continue; - switch (pkt->type) { case GIT_PKT_DATA: /* This is a sideband packet which contains other packets */ diff --git a/src/transports/ssh.c b/src/transports/ssh.c index cfd573665..47d328c41 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -5,17 +5,19 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "ssh.h" + #ifdef GIT_SSH #include <libssh2.h> #endif +#include "global.h" #include "git2.h" #include "buffer.h" #include "netops.h" #include "smart.h" #include "cred.h" #include "socket_stream.h" -#include "ssh.h" #ifdef GIT_SSH @@ -83,7 +85,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) done: if (!repo) { - giterr_set(GITERR_NET, "Malformed git protocol URL"); + giterr_set(GITERR_NET, "malformed git protocol URL"); return -1; } @@ -274,7 +276,7 @@ static int git_ssh_extract_url_parts( } if (colon == NULL || (colon < start)) { - giterr_set(GITERR_NET, "Malformed URL"); + giterr_set(GITERR_NET, "malformed URL"); return -1; } @@ -445,7 +447,7 @@ static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, else if (error < 0) return error; else if (!cred) { - giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials"); + giterr_set(GITERR_SSH, "callback failed to initialize SSH credentials"); return -1; } } @@ -478,7 +480,7 @@ static int _git_ssh_session_create( s = libssh2_session_init(); if (!s) { - giterr_set(GITERR_NET, "Failed to initialize SSH session"); + giterr_set(GITERR_NET, "failed to initialize SSH session"); return -1; } @@ -487,7 +489,7 @@ static int _git_ssh_session_create( } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { - ssh_error(s, "Failed to start SSH session"); + ssh_error(s, "failed to start SSH session"); libssh2_session_free(s); return -1; } @@ -685,7 +687,7 @@ static int ssh_uploadpack( return 0; } - giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); + giterr_set(GITERR_NET, "must call UPLOADPACK_LS before UPLOADPACK"); return -1; } @@ -712,7 +714,7 @@ static int ssh_receivepack( return 0; } - giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK"); + giterr_set(GITERR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK"); return -1; } @@ -844,7 +846,7 @@ int git_smart_subtransport_ssh( assert(out); *out = NULL; - giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); + giterr_set(GITERR_INVALID, "cannot create SSH transport. Library was built without SSH support"); return -1; #endif } @@ -888,16 +890,27 @@ int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *p assert(out); *out = NULL; - giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); + giterr_set(GITERR_INVALID, "cannot create SSH transport. Library was built without SSH support"); return -1; #endif } +#ifdef GIT_SSH +static void shutdown_ssh(void) +{ + libssh2_exit(); +} +#endif + int git_transport_ssh_global_init(void) { #ifdef GIT_SSH + if (libssh2_init(0) < 0) { + giterr_set(GITERR_SSH, "unable to initialize libssh2"); + return -1; + } - libssh2_init(0); + git__on_shutdown(shutdown_ssh); return 0; #else diff --git a/src/transports/ssh.h b/src/transports/ssh.h index 2db2cc5df..e36e7246e 100644 --- a/src/transports/ssh.h +++ b/src/transports/ssh.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_ssh_h__ #define INCLUDE_ssh_h__ +#include "common.h" + int git_transport_ssh_global_init(void); #endif diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 78e42cf3b..6689c8ce3 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #ifdef GIT_WINHTTP #include "git2.h" @@ -68,7 +70,9 @@ static const IID IID_IInternetSecurityManager_mingw = typedef enum { GIT_WINHTTP_AUTH_BASIC = 1, - GIT_WINHTTP_AUTH_NEGOTIATE = 2, + GIT_WINHTTP_AUTH_NTLM = 2, + GIT_WINHTTP_AUTH_NEGOTIATE = 4, + GIT_WINHTTP_AUTH_DIGEST = 8, } winhttp_authmechanism_t; typedef struct { @@ -95,79 +99,71 @@ typedef struct { git_cred *cred; git_cred *url_cred; git_cred *proxy_cred; - int auth_mechanism; + int auth_mechanisms; HINTERNET session; HINTERNET connection; } winhttp_subtransport; -static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred) +static int _apply_userpass_credential(HINTERNET request, DWORD target, DWORD scheme, git_cred *cred) { git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; wchar_t *user, *pass; - int error; + int user_len = 0, pass_len = 0, error = 0; - if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0) - return error; + if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0) + goto done; - if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0) - return error; + if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0) + goto done; - if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC, - user, pass, NULL)) { - giterr_set(GITERR_OS, "failed to set proxy auth"); + if (!WinHttpSetCredentials(request, target, scheme, user, pass, NULL)) { + giterr_set(GITERR_OS, "failed to set credentials"); error = -1; } +done: + if (user_len > 0) + git__memzero(user, user_len * sizeof(wchar_t)); + + if (pass_len > 0) + git__memzero(pass, pass_len * sizeof(wchar_t)); + git__free(user); git__free(pass); return error; } -static int apply_basic_credential(HINTERNET request, git_cred *cred) +static int apply_userpass_credential_proxy(HINTERNET request, git_cred *cred, int mechanisms) { - git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT; - wchar_t *wide = NULL; - int error = -1, wide_len; - - git_buf_printf(&raw, "%s:%s", c->username, c->password); - - if (git_buf_oom(&raw) || - git_buf_puts(&buf, "Authorization: Basic ") < 0 || - git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0) - goto on_error; - - if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); - goto on_error; - } - - if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); - goto on_error; + if (GIT_WINHTTP_AUTH_DIGEST & mechanisms) { + return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY, + WINHTTP_AUTH_SCHEME_DIGEST, cred); } - error = 0; - -on_error: - /* We were dealing with plaintext passwords, so clean up after ourselves a bit. */ - if (wide) - memset(wide, 0x0, wide_len * sizeof(wchar_t)); + return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY, + WINHTTP_AUTH_SCHEME_BASIC, cred); +} - if (buf.size) - memset(buf.ptr, 0x0, buf.size); +static int apply_userpass_credential(HINTERNET request, int mechanisms, git_cred *cred) +{ + DWORD native_scheme; - if (raw.size) - memset(raw.ptr, 0x0, raw.size); + if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) || + (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE)) { + native_scheme = WINHTTP_AUTH_SCHEME_NTLM; + } else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) { + native_scheme = WINHTTP_AUTH_SCHEME_BASIC; + } else { + giterr_set(GITERR_NET, "invalid authentication scheme"); + return -1; + } - git__free(wide); - git_buf_free(&buf); - git_buf_free(&raw); - return error; + return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_SERVER, + native_scheme, cred); } -static int apply_default_credentials(HINTERNET request) +static int apply_default_credentials(HINTERNET request, int mechanisms) { /* Either the caller explicitly requested that default credentials be passed, * or our fallback credential callback was invoked and checked that the target @@ -177,6 +173,12 @@ static int apply_default_credentials(HINTERNET request) * to Internet Explorer security zones, but in fact does not. */ DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; + if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 && + (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) { + giterr_set(GITERR_NET, "invalid authentication scheme"); + return -1; + } + if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD))) return -1; @@ -202,7 +204,7 @@ static int fallback_cred_acquire_cb( /* Convert URL to wide characters */ if (git__utf8_to_16_alloc(&wide_url, url) < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); + giterr_set(GITERR_OS, "failed to convert string to wide form"); return -1; } @@ -248,8 +250,12 @@ static int certificate_check(winhttp_stream *s, int valid) git_cert_x509 cert; /* If there is no override, we should fail if WinHTTP doesn't think it's fine */ - if (t->owner->certificate_check_cb == NULL && !valid) + if (t->owner->certificate_check_cb == NULL && !valid) { + if (!giterr_last()) + giterr_set(GITERR_NET, "unknown certificate check failure"); + return GIT_ECERTIFICATE; + } if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl) return 0; @@ -351,7 +357,7 @@ static int winhttp_stream_connect(winhttp_stream *s) /* Convert URL to wide characters */ if (git__utf8_to_16_alloc(&s->request_uri, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); + giterr_set(GITERR_OS, "failed to convert string to wide form"); goto on_error; } @@ -366,12 +372,12 @@ static int winhttp_stream_connect(winhttp_stream *s) t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0); if (!s->request) { - giterr_set(GITERR_OS, "Failed to open request"); + giterr_set(GITERR_OS, "failed to open request"); goto on_error; } if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { - giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP"); + giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP"); goto on_error; } @@ -425,7 +431,6 @@ static int winhttp_stream_connect(winhttp_stream *s) git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port); if (git_buf_oom(&processed_url)) { - giterr_set_oom(); error = -1; goto on_error; } @@ -444,7 +449,7 @@ static int winhttp_stream_connect(winhttp_stream *s) WINHTTP_OPTION_PROXY, &proxy_info, sizeof(WINHTTP_PROXY_INFO))) { - giterr_set(GITERR_OS, "Failed to set proxy"); + giterr_set(GITERR_OS, "failed to set proxy"); git__free(proxy_wide); goto on_error; } @@ -453,7 +458,7 @@ static int winhttp_stream_connect(winhttp_stream *s) if (t->proxy_cred) { if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) { - if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0) + if ((error = apply_userpass_credential_proxy(s->request, t->proxy_cred, t->auth_mechanisms)) < 0) goto on_error; } } @@ -467,7 +472,7 @@ static int winhttp_stream_connect(winhttp_stream *s) WINHTTP_OPTION_DISABLE_FEATURE, &disable_redirects, sizeof(disable_redirects))) { - giterr_set(GITERR_OS, "Failed to disable redirects"); + giterr_set(GITERR_OS, "failed to disable redirects"); goto on_error; } @@ -481,7 +486,7 @@ static int winhttp_stream_connect(winhttp_stream *s) /* Send Pragma: no-cache header */ if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } @@ -494,13 +499,13 @@ static int winhttp_stream_connect(winhttp_stream *s) goto on_error; if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert content-type to wide characters"); + giterr_set(GITERR_OS, "failed to convert content-type to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } @@ -511,13 +516,13 @@ static int winhttp_stream_connect(winhttp_stream *s) goto on_error; if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert accept header to wide characters"); + giterr_set(GITERR_OS, "failed to convert accept header to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } } @@ -527,13 +532,13 @@ static int winhttp_stream_connect(winhttp_stream *s) git_buf_clear(&buf); git_buf_puts(&buf, t->owner->custom_headers.strings[i]); if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); + giterr_set(GITERR_OS, "failed to convert custom header to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } } @@ -550,13 +555,11 @@ static int winhttp_stream_connect(winhttp_stream *s) /* If we have a credential on the subtransport, apply it to the request */ if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT && - t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC && - apply_basic_credential(s->request, t->cred) < 0) + apply_userpass_credential(s->request, t->auth_mechanisms, t->cred) < 0) goto on_error; else if (t->cred && t->cred->credtype == GIT_CREDTYPE_DEFAULT && - t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE && - apply_default_credentials(s->request) < 0) + apply_default_credentials(s->request, t->auth_mechanisms) < 0) goto on_error; /* If no other credentials have been applied and the URL has username and @@ -565,7 +568,7 @@ static int winhttp_stream_connect(winhttp_stream *s) if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0) goto on_error; - if (apply_basic_credential(s->request, t->url_cred) < 0) + if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0) goto on_error; } @@ -585,30 +588,40 @@ on_error: static int parse_unauthorized_response( HINTERNET request, int *allowed_types, - int *auth_mechanism) + int *allowed_mechanisms) { DWORD supported, first, target; *allowed_types = 0; - *auth_mechanism = 0; + *allowed_mechanisms = 0; - /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). - * We can assume this was already done, since we know we are unauthorized. + /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). + * We can assume this was already done, since we know we are unauthorized. */ if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) { - giterr_set(GITERR_OS, "Failed to parse supported auth schemes"); + giterr_set(GITERR_OS, "failed to parse supported auth schemes"); return -1; } - if (WINHTTP_AUTH_SCHEME_BASIC & supported) { + if (WINHTTP_AUTH_SCHEME_NTLM & supported) { *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; - *auth_mechanism = GIT_WINHTTP_AUTH_BASIC; + *allowed_types |= GIT_CREDTYPE_DEFAULT; + *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE; } - if ((WINHTTP_AUTH_SCHEME_NTLM & supported) || - (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) { + if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) { *allowed_types |= GIT_CREDTYPE_DEFAULT; - *auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE; + *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE; + } + + if (WINHTTP_AUTH_SCHEME_BASIC & supported) { + *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; + *allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC; + } + + if (WINHTTP_AUTH_SCHEME_DIGEST & supported) { + *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; + *allowed_mechanisms |= GIT_WINHTTP_AUTH_DIGEST; } return 0; @@ -629,7 +642,7 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) git_buf_cstr(&buf), (DWORD)git_buf_len(&buf), &bytes_written)) { git_buf_free(&buf); - giterr_set(GITERR_OS, "Failed to write chunk header"); + giterr_set(GITERR_OS, "failed to write chunk header"); return -1; } @@ -639,7 +652,7 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) if (!WinHttpWriteData(request, buffer, (DWORD)len, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write chunk"); + giterr_set(GITERR_OS, "failed to write chunk"); return -1; } @@ -647,7 +660,7 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) if (!WinHttpWriteData(request, "\r\n", 2, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write chunk footer"); + giterr_set(GITERR_OS, "failed to write chunk footer"); return -1; } @@ -660,7 +673,7 @@ static int winhttp_close_connection(winhttp_subtransport *t) if (t->connection) { if (!WinHttpCloseHandle(t->connection)) { - giterr_set(GITERR_OS, "Unable to close connection"); + giterr_set(GITERR_OS, "unable to close connection"); ret = -1; } @@ -669,7 +682,7 @@ static int winhttp_close_connection(winhttp_subtransport *t) if (t->session) { if (!WinHttpCloseHandle(t->session)) { - giterr_set(GITERR_OS, "Unable to close session"); + giterr_set(GITERR_OS, "unable to close session"); ret = -1; } @@ -694,6 +707,38 @@ static int user_agent(git_buf *ua) return git_buf_putc(ua, ')'); } +static void CALLBACK winhttp_status( + HINTERNET connection, + DWORD_PTR ctx, + DWORD code, + LPVOID info, + DWORD info_len) +{ + DWORD status; + + if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) + return; + + status = *((DWORD *)info); + + if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)) + giterr_set(GITERR_NET, "SSL certificate issued for different common name"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)) + giterr_set(GITERR_NET, "SSL certificate has expired"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)) + giterr_set(GITERR_NET, "SSL certificate signed by unknown CA"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)) + giterr_set(GITERR_NET, "SSL certificate is invalid"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)) + giterr_set(GITERR_NET, "certificate revocation check failed"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)) + giterr_set(GITERR_NET, "SSL certificate was revoked"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)) + giterr_set(GITERR_NET, "security libraries could not be loaded"); + else + giterr_set(GITERR_NET, "unknown security error %d", status); +} + static int winhttp_connect( winhttp_subtransport *t) { @@ -714,7 +759,7 @@ static int winhttp_connect( /* Prepare host */ if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) { - giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + giterr_set(GITERR_OS, "unable to convert host to wide characters"); return -1; } @@ -724,7 +769,7 @@ static int winhttp_connect( } if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) { - giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + giterr_set(GITERR_OS, "unable to convert host to wide characters"); git__free(wide_host); git_buf_free(&ua); return -1; @@ -741,16 +786,16 @@ static int winhttp_connect( 0); if (!t->session) { - giterr_set(GITERR_OS, "Failed to init WinHTTP"); + giterr_set(GITERR_OS, "failed to init WinHTTP"); goto on_error; } if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { - giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP"); + giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP"); goto on_error; } - + /* Establish connection */ t->connection = WinHttpConnect( t->session, @@ -759,7 +804,12 @@ static int winhttp_connect( 0); if (!t->connection) { - giterr_set(GITERR_OS, "Failed to connect to host"); + giterr_set(GITERR_OS, "failed to connect to host"); + goto on_error; + } + + if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) { + giterr_set(GITERR_OS, "failed to set status callback"); goto on_error; } @@ -801,16 +851,15 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length) int request_failed = 0, cert_valid = 1, error = 0; DWORD ignore_flags; - if ((error = do_send_request(s, len, ignore_length)) < 0) - request_failed = 1; - - if (request_failed) { + giterr_clear(); + if ((error = do_send_request(s, len, ignore_length)) < 0) { if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) { giterr_set(GITERR_OS, "failed to send request"); return -1; - } else { - cert_valid = 0; } + + request_failed = 1; + cert_valid = 0; } giterr_clear(); @@ -826,14 +875,14 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length) return 0; ignore_flags = no_check_cert_flags; - + if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) { giterr_set(GITERR_OS, "failed to set security options"); return -1; } if ((error = do_send_request(s, len, ignore_length)) < 0) - giterr_set(GITERR_OS, "failed to send request"); + giterr_set(GITERR_OS, "failed to send request with unchecked certificate"); return error; } @@ -853,7 +902,7 @@ static int winhttp_stream_read( replay: /* Enforce a reasonable cap on the number of replays */ if (++replay_count >= 7) { - giterr_set(GITERR_NET, "Too many redirects or authentication replays"); + giterr_set(GITERR_NET, "too many redirects or authentication replays"); return -1; } @@ -888,7 +937,7 @@ replay: if (!WinHttpWriteData(s->request, "0\r\n\r\n", 5, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write final chunk"); + giterr_set(GITERR_OS, "failed to write final chunk"); return -1; } } @@ -899,7 +948,7 @@ replay: if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body, 0, 0, FILE_BEGIN) && NO_ERROR != GetLastError()) { - giterr_set(GITERR_OS, "Failed to reset file pointer"); + giterr_set(GITERR_OS, "failed to reset file pointer"); return -1; } @@ -913,14 +962,14 @@ replay: &bytes_read, NULL) || !bytes_read) { git__free(buffer); - giterr_set(GITERR_OS, "Failed to read from temp file"); + giterr_set(GITERR_OS, "failed to read from temp file"); return -1; } if (!WinHttpWriteData(s->request, buffer, bytes_read, &bytes_written)) { git__free(buffer); - giterr_set(GITERR_OS, "Failed to write data"); + giterr_set(GITERR_OS, "failed to write data"); return -1; } @@ -936,7 +985,7 @@ replay: } if (!WinHttpReceiveResponse(s->request, 0)) { - giterr_set(GITERR_OS, "Failed to receive response"); + giterr_set(GITERR_OS, "failed to receive response"); return -1; } @@ -948,7 +997,7 @@ replay: WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_length, WINHTTP_NO_HEADER_INDEX)) { - giterr_set(GITERR_OS, "Failed to retrieve status code"); + giterr_set(GITERR_OS, "failed to retrieve status code"); return -1; } @@ -978,7 +1027,7 @@ replay: &location_length, WINHTTP_NO_HEADER_INDEX) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - giterr_set(GITERR_OS, "Failed to read Location header"); + giterr_set(GITERR_OS, "failed to read Location header"); return -1; } @@ -991,14 +1040,14 @@ replay: location, &location_length, WINHTTP_NO_HEADER_INDEX)) { - giterr_set(GITERR_OS, "Failed to read Location header"); + giterr_set(GITERR_OS, "failed to read Location header"); git__free(location); return -1; } /* Convert the Location header to UTF-8 */ if (git__utf16_to_8_alloc(&location8, location) < 0) { - giterr_set(GITERR_OS, "Failed to convert Location header to UTF-8"); + giterr_set(GITERR_OS, "failed to convert Location header to UTF-8"); git__free(location); return -1; } @@ -1029,13 +1078,13 @@ replay: if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) { int allowed_types; - if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) + if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0) return -1; /* TODO: extract the username from the url, no payload? */ if (t->owner->proxy.credentials) { int cred_error = 1; - cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL); + cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, t->owner->proxy.payload); if (cred_error < 0) return cred_error; @@ -1049,7 +1098,7 @@ replay: if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) { int allowed_types; - if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) + if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0) return -1; if (allowed_types) { @@ -1090,7 +1139,7 @@ replay: } if (HTTP_STATUS_OK != status_code) { - giterr_set(GITERR_NET, "Request failed with status code: %d", status_code); + giterr_set(GITERR_NET, "request failed with status code: %d", status_code); return -1; } @@ -1101,7 +1150,7 @@ replay: p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service); if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) { - giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters"); + giterr_set(GITERR_OS, "failed to convert expected content-type to wide characters"); return -1; } @@ -1112,12 +1161,12 @@ replay: WINHTTP_HEADER_NAME_BY_INDEX, &content_type, &content_type_length, WINHTTP_NO_HEADER_INDEX)) { - giterr_set(GITERR_OS, "Failed to retrieve response content-type"); + giterr_set(GITERR_OS, "failed to retrieve response content-type"); return -1; } if (wcscmp(expected_content_type, content_type)) { - giterr_set(GITERR_NET, "Received unexpected content-type"); + giterr_set(GITERR_NET, "received unexpected content-type"); return -1; } @@ -1129,7 +1178,7 @@ replay: (DWORD)buf_size, &dw_bytes_read)) { - giterr_set(GITERR_OS, "Failed to read data"); + giterr_set(GITERR_OS, "failed to read data"); return -1; } @@ -1152,7 +1201,7 @@ static int winhttp_stream_write_single( /* This implementation of write permits only a single call. */ if (s->sent_request) { - giterr_set(GITERR_NET, "Subtransport configured for only one write"); + giterr_set(GITERR_NET, "subtransport configured for only one write"); return -1; } @@ -1165,7 +1214,7 @@ static int winhttp_stream_write_single( (LPCVOID)buffer, (DWORD)len, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write data"); + giterr_set(GITERR_OS, "failed to write data"); return -1; } @@ -1183,12 +1232,12 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch) if (RPC_S_OK != status && RPC_S_UUID_LOCAL_ONLY != status && RPC_S_UUID_NO_ADDRESS != status) { - giterr_set(GITERR_NET, "Unable to generate name for temp file"); + giterr_set(GITERR_NET, "unable to generate name for temp file"); return -1; } if (buffer_len_cch < UUID_LENGTH_CCH + 1) { - giterr_set(GITERR_NET, "Buffer too small for name of temp file"); + giterr_set(GITERR_NET, "buffer too small for name of temp file"); return -1; } @@ -1203,7 +1252,7 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch) uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]); if (result < UUID_LENGTH_CCH) { - giterr_set(GITERR_OS, "Unable to generate name for temp file"); + giterr_set(GITERR_OS, "unable to generate name for temp file"); return -1; } @@ -1215,7 +1264,7 @@ static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch) size_t len; if (!GetTempPathW(buffer_len_cch, buffer)) { - giterr_set(GITERR_OS, "Failed to get temp path"); + giterr_set(GITERR_OS, "failed to get temp path"); return -1; } @@ -1258,13 +1307,13 @@ static int winhttp_stream_write_buffered( if (INVALID_HANDLE_VALUE == s->post_body) { s->post_body = NULL; - giterr_set(GITERR_OS, "Failed to create temporary file"); + giterr_set(GITERR_OS, "failed to create temporary file"); return -1; } } if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) { - giterr_set(GITERR_OS, "Failed to write to temporary file"); + giterr_set(GITERR_OS, "failed to write to temporary file"); return -1; } @@ -1291,7 +1340,7 @@ static int winhttp_stream_write_chunked( if (!WinHttpAddRequestHeaders(s->request, transfer_encoding, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); return -1; } diff --git a/src/tree-cache.c b/src/tree-cache.c index b37be0f0d..b331d22a2 100644 --- a/src/tree-cache.c +++ b/src/tree-cache.c @@ -6,6 +6,7 @@ */ #include "tree-cache.h" + #include "pool.h" #include "tree.h" @@ -137,7 +138,7 @@ static int read_tree_internal(git_tree_cache **out, return 0; corrupted: - giterr_set(GITERR_INDEX, "Corrupted TREE extension in index"); + giterr_set(GITERR_INDEX, "corrupted TREE extension in index"); return -1; } @@ -149,7 +150,7 @@ int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer return -1; if (buffer < buffer_end) { - giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)"); + giterr_set(GITERR_INDEX, "corrupted TREE extension in index (unexpected trailing data)"); return -1; } diff --git a/src/tree-cache.h b/src/tree-cache.h index c44ca7cf5..e02300e6e 100644 --- a/src/tree-cache.h +++ b/src/tree-cache.h @@ -9,6 +9,7 @@ #define INCLUDE_tree_cache_h__ #include "common.h" + #include "pool.h" #include "buffer.h" #include "git2/oid.h" diff --git a/src/tree.c b/src/tree.c index 5db2446bf..fcee7f3b3 100644 --- a/src/tree.c +++ b/src/tree.c @@ -5,9 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" -#include "commit.h" #include "tree.h" + +#include "commit.h" #include "git2/repository.h" #include "git2/object.h" #include "fileops.h" @@ -20,8 +20,6 @@ #define TREE_ENTRY_CHECK_NAMELEN(n) \ if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); } -GIT__USE_STRMAP - static bool valid_filemode(const int filemode) { return (filemode == GIT_FILEMODE_TREE @@ -447,7 +445,12 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL) return tree_error("Failed to parse tree. Object is corrupted", NULL); - filename_len = nul - buffer; + if ((filename_len = nul - buffer) == 0) + return tree_error("Failed to parse tree. Can't parse filename", NULL); + + if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ) + return tree_error("Failed to parse tree. Can't parse OID", NULL); + /* Allocate the entry */ { entry = git_array_alloc(tree->entries); @@ -500,7 +503,7 @@ static int append_entry( entry->attr = (uint16_t)filemode; - git_strmap_insert(bld->map, entry->filename, entry, error); + git_strmap_insert(bld->map, entry->filename, entry, &error); if (error < 0) { git_tree_entry_free(entry); giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename); @@ -515,7 +518,8 @@ static int write_tree( git_repository *repo, git_index *index, const char *dirname, - size_t start) + size_t start, + git_buf *shared_buf) { git_treebuilder *bld = NULL; size_t i, entries = git_index_entrycount(index); @@ -568,7 +572,7 @@ static int write_tree( GITERR_CHECK_ALLOC(subdir); /* Write out the subtree */ - written = write_tree(&sub_oid, repo, index, subdir, i); + written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf); if (written < 0) { git__free(subdir); goto on_error; @@ -600,7 +604,7 @@ static int write_tree( } } - if (git_treebuilder_write(oid, bld) < 0) + if (git_treebuilder_write_with_buffer(oid, bld, shared_buf) < 0) goto on_error; git_treebuilder_free(bld); @@ -616,13 +620,14 @@ int git_tree__write_index( { int ret; git_tree *tree; + git_buf shared_buf = GIT_BUF_INIT; bool old_ignore_case = false; assert(oid && index && repo); if (git_index_has_conflicts(index)) { giterr_set(GITERR_INDEX, - "Cannot create a tree from a not fully merged index."); + "cannot create a tree from a not fully merged index."); return GIT_EUNMERGED; } @@ -641,7 +646,8 @@ int git_tree__write_index( git_index__set_ignore_case(index, false); } - ret = write_tree(oid, repo, index, "", 0); + ret = write_tree(oid, repo, index, "", 0, &shared_buf); + git_buf_free(&shared_buf); if (old_ignore_case) git_index__set_ignore_case(index, true); @@ -746,7 +752,7 @@ int git_treebuilder_insert( entry = alloc_entry(filename, strlen(filename), id); GITERR_CHECK_ALLOC(entry); - git_strmap_insert(bld->map, entry->filename, entry, error); + git_strmap_insert(bld->map, entry->filename, entry, &error); if (error < 0) { git_tree_entry_free(entry); @@ -797,46 +803,60 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) int git_treebuilder_write(git_oid *oid, git_treebuilder *bld) { + int error; + git_buf buffer = GIT_BUF_INIT; + + error = git_treebuilder_write_with_buffer(oid, bld, &buffer); + + git_buf_free(&buffer); + return error; +} + +int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *tree) +{ int error = 0; size_t i, entrycount; - git_buf tree = GIT_BUF_INIT; git_odb *odb; git_tree_entry *entry; - git_vector entries; + git_vector entries = GIT_VECTOR_INIT; assert(bld); + assert(tree); + + git_buf_clear(tree); entrycount = git_strmap_num_entries(bld->map); - if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0) - return -1; + if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0) + goto out; + + if (tree->asize == 0 && + (error = git_buf_grow(tree, entrycount * 72)) < 0) + goto out; git_strmap_foreach_value(bld->map, entry, { - if (git_vector_insert(&entries, entry) < 0) - return -1; + if ((error = git_vector_insert(&entries, entry)) < 0) + goto out; }); git_vector_sort(&entries); - /* Grow the buffer beforehand to an estimated size */ - error = git_buf_grow(&tree, entrycount * 72); - for (i = 0; i < entries.length && !error; ++i) { - git_tree_entry *entry = git_vector_get(&entries, i); + entry = git_vector_get(&entries, i); - git_buf_printf(&tree, "%o ", entry->attr); - git_buf_put(&tree, entry->filename, entry->filename_len + 1); - git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ); + git_buf_printf(tree, "%o ", entry->attr); + git_buf_put(tree, entry->filename, entry->filename_len + 1); + git_buf_put(tree, (char *)entry->oid->id, GIT_OID_RAWSZ); - if (git_buf_oom(&tree)) + if (git_buf_oom(tree)) { error = -1; + goto out; + } } + if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0) + error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE); - if (!error && - !(error = git_repository_odb__weakptr(&odb, bld->repo))) - error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); - - git_buf_free(&tree); +out: git_vector_free(&entries); return error; @@ -904,7 +924,7 @@ int git_tree_entry_bypath( filename_len = subpath_len(path); if (filename_len == 0) { - giterr_set(GITERR_TREE, "Invalid tree path given"); + giterr_set(GITERR_TREE, "invalid tree path given"); return GIT_ENOTFOUND; } @@ -912,7 +932,7 @@ int git_tree_entry_bypath( if (entry == NULL) { giterr_set(GITERR_TREE, - "the path '%.*s' does not exist in the given tree", filename_len, path); + "the path '%.*s' does not exist in the given tree", (int) filename_len, path); return GIT_ENOTFOUND; } @@ -922,7 +942,7 @@ int git_tree_entry_bypath( * then this entry *must* be a tree */ if (!git_tree_entry__is_tree(entry)) { giterr_set(GITERR_TREE, - "the path '%.*s' exists but is not a tree", filename_len, path); + "the path '%.*s' exists but is not a tree", (int) filename_len, path); return GIT_ENOTFOUND; } @@ -1022,7 +1042,7 @@ int git_tree_walk( git_buf root_path = GIT_BUF_INIT; if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) { - giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk"); + giterr_set(GITERR_INVALID, "invalid walking mode for tree walk"); return -1; } @@ -1159,8 +1179,8 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli goto cleanup; for (i = 0; i < nupdates; i++) { - const git_tree_update *last_update = i == 0 ? NULL : &updates[i-1]; - const git_tree_update *update = &updates[i]; + const git_tree_update *last_update = i == 0 ? NULL : git_vector_get(&entries, i-1); + const git_tree_update *update = git_vector_get(&entries, i); size_t common_prefix = 0, steps_up, j; const char *path; @@ -1195,6 +1215,9 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli last = git_array_last(stack); entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL; + if (!entry) + entry = treebuilder_get(last->bld, component.ptr); + if (entry && git_tree_entry_type(entry) != GIT_OBJ_TREE) { giterr_set(GITERR_TREE, "D/F conflict when updating tree"); error = -1; @@ -1229,7 +1252,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli const git_tree_entry *e = git_treebuilder_get(last->bld, basename); if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) { git__free(basename); - giterr_set(GITERR_TREE, "Cannot replace '%s' with '%s' at '%s'", + giterr_set(GITERR_TREE, "cannot replace '%s' with '%s' at '%s'", git_object_type2string(git_tree_entry_type(e)), git_object_type2string(git_object__type_from_filemode(update->filemode)), update->path); @@ -1249,7 +1272,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli break; } default: - giterr_set(GITERR_TREE, "unkown action for update"); + giterr_set(GITERR_TREE, "unknown action for update"); error = -1; goto cleanup; } diff --git a/src/tree.h b/src/tree.h index 5e7a66e04..00f4b06eb 100644 --- a/src/tree.h +++ b/src/tree.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_tree_h__ #define INCLUDE_tree_h__ +#include "common.h" + #include "git2/tree.h" #include "repository.h" #include "odb.h" diff --git a/src/tsort.c b/src/tsort.c index e59819204..8d1ed9787 100644 --- a/src/tsort.c +++ b/src/tsort.c @@ -310,7 +310,6 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, #define PUSH_NEXT() do {\ len = count_run(dst, curr, size, store);\ run = minrun;\ - if (run < minrun) run = minrun;\ if (run > (ssize_t)size - curr) run = size - curr;\ if (run > len) {\ bisort(&dst[curr], len, run, cmp, payload);\ diff --git a/src/unix/map.c b/src/unix/map.c index c55ad1aa7..8a07fcff9 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -4,7 +4,10 @@ * 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 <git2/common.h> + +#include "common.h" + +#include "git2/common.h" #if !defined(GIT_WIN32) && !defined(NO_MMAP) @@ -52,7 +55,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs out->data = mmap(NULL, len, mprot, mflag, fd, offset); if (!out->data || out->data == MAP_FAILED) { - giterr_set(GITERR_OS, "Failed to mmap. Could not write data"); + giterr_set(GITERR_OS, "failed to mmap. Could not write data"); return -1; } diff --git a/src/unix/posix.h b/src/unix/posix.h index 482d2c803..ee36ea0f3 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -7,6 +7,10 @@ #ifndef INCLUDE_posix__unix_h__ #define INCLUDE_posix__unix_h__ +#ifndef LIBGIT2_NO_FEATURES_H +# include "git2/sys/features.h" +#endif + #include <stdio.h> #include <dirent.h> #include <sys/param.h> @@ -40,9 +44,14 @@ typedef int GIT_SOCKET; #define p_link(o,n) link(o, n) #define p_unlink(p) unlink(p) #define p_mkdir(p,m) mkdir(p, m) -#define p_fsync(fd) fsync(fd) extern char *p_realpath(const char *, char *); +GIT_INLINE(int) p_fsync(int fd) +{ + p_fsync__cnt++; + return fsync(fd); +} + #define p_recv(s,b,l,f) recv(s,b,l,f) #define p_send(s,b,l,f) send(s,b,l,f) #define p_inet_pton(a, b, c) inet_pton(a, b, c) @@ -50,7 +59,7 @@ extern char *p_realpath(const char *, char *); #define p_strcasecmp(s1, s2) strcasecmp(s1, s2) #define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c) #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) -#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__) +#define p_snprintf(b, c, ...) snprintf(b, c, __VA_ARGS__) #define p_mkstemp(p) mkstemp(p) #define p_chdir(p) chdir(p) #define p_chmod(p,m) chmod(p, m) @@ -66,7 +75,7 @@ extern char *p_realpath(const char *, char *); #define p_timeval timeval -#ifdef HAVE_FUTIMENS +#ifdef GIT_USE_FUTIMENS GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2]) { struct timespec s[2]; @@ -80,4 +89,14 @@ GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2]) # define p_futimes futimes #endif +#ifdef GIT_USE_REGCOMP_L +#include <xlocale.h> +GIT_INLINE(int) p_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + return regcomp_l(preg, pattern, cflags, (locale_t) 0); +} +#else +# define p_regcomp regcomp +#endif + #endif diff --git a/src/unix/pthread.h b/src/unix/pthread.h index 0f3f17927..3f23d10d5 100644 --- a/src/unix/pthread.h +++ b/src/unix/pthread.h @@ -17,6 +17,8 @@ typedef struct { pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg) #define git_thread_join(git_thread_ptr, status) \ pthread_join((git_thread_ptr)->thread, status) +#define git_thread_currentid() ((size_t)(pthread_self())) +#define git_thread_exit(retval) pthread_exit(retval) /* Git Mutex */ #define git_mutex pthread_mutex_t diff --git a/src/unix/realpath.c b/src/unix/realpath.c index 2e49150c2..893bac87b 100644 --- a/src/unix/realpath.c +++ b/src/unix/realpath.c @@ -4,7 +4,10 @@ * 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 <git2/common.h> + +#include "common.h" + +#include "git2/common.h" #ifndef GIT_WIN32 diff --git a/src/util.c b/src/util.c index 76ca711b2..6ae5cdaec 100644 --- a/src/util.c +++ b/src/util.c @@ -4,8 +4,10 @@ * 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 <git2.h> -#include "common.h" + +#include "util.h" + +#include "git2.h" #include <stdio.h> #include <ctype.h> #include "posix.h" @@ -136,7 +138,7 @@ int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const cha Return: if (ndig == 0) { - giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number"); + giterr_set(GITERR_INVALID, "failed to convert string to long: not a number"); return -1; } @@ -144,7 +146,7 @@ Return: *endptr = p; if (ovfl) { - giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error"); + giterr_set(GITERR_INVALID, "failed to convert string to long: overflow error"); return -1; } @@ -169,7 +171,7 @@ int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const cha tmp_int = tmp_long & 0xFFFFFFFF; if (tmp_int != tmp_long) { - giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr); + giterr_set(GITERR_INVALID, "failed to convert: '%s' is too large", nptr); return -1; } diff --git a/src/util.h b/src/util.h index eb15250d8..b85e03b6d 100644 --- a/src/util.h +++ b/src/util.h @@ -7,39 +7,11 @@ #ifndef INCLUDE_util_h__ #define INCLUDE_util_h__ +#include "common.h" + #include "git2/buffer.h" #include "buffer.h" - -#if defined(GIT_MSVC_CRTDBG) -/* Enable MSVC CRTDBG memory leak reporting. - * - * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC - * documentation because all allocs/frees in libgit2 already go through - * the "git__" routines defined in this file. Simply using the normal - * reporting mechanism causes all leaks to be attributed to a routine - * here in util.h (ie, the actual call to calloc()) rather than the - * caller of git__calloc(). - * - * Therefore, we declare a set of "git__crtdbg__" routines to replace - * the corresponding "git__" routines and re-define the "git__" symbols - * as macros. This allows us to get and report the file:line info of - * the real caller. - * - * We DO NOT replace the "git__free" routine because it needs to remain - * a function pointer because it is used as a function argument when - * setting up various structure "destructors". - * - * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes - * "free" to be remapped to "_free_dbg" and this causes problems for - * structures which define a field named "free". - * - * Finally, CRTDBG must be explicitly enabled and configured at program - * startup. See tests/main.c for an example. - */ -#include <stdlib.h> -#include <crtdbg.h> -#include "win32/w32_crtdbg_stacktrace.h" -#endif +#include "thread-utils.h" #include "common.h" #include "strnlen.h" @@ -67,79 +39,33 @@ #if defined(GIT_MSVC_CRTDBG) -GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line) -{ - void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); - if (!ptr) giterr_set_oom(); - return ptr; -} - -GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line) -{ - void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); - if (!ptr) giterr_set_oom(); - return ptr; -} - -GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line) -{ - char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); - if (!ptr) giterr_set_oom(); - return ptr; -} - -GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line) -{ - size_t length = 0, alloclength; - char *ptr; - - length = p_strnlen(str, n); - - if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) || - !(ptr = git__crtdbg__malloc(alloclength, file, line))) - return NULL; - - if (length) - memcpy(ptr, str, length); - - ptr[length] = '\0'; - - return ptr; -} - -GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line) -{ - char *ptr; - size_t alloclen; - - if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) || - !(ptr = git__crtdbg__malloc(alloclen, file, line))) - return NULL; - - memcpy(ptr, start, n); - ptr[n] = '\0'; - return ptr; -} - -GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line) -{ - void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); - if (!new_ptr) giterr_set_oom(); - return new_ptr; -} - -GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line) -{ - size_t newsize; - - return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ? - NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); -} +/* Enable MSVC CRTDBG memory leak reporting. + * + * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC + * documentation because all allocs/frees in libgit2 already go through + * the "git__" routines defined in this file. Simply using the normal + * reporting mechanism causes all leaks to be attributed to a routine + * here in util.h (ie, the actual call to calloc()) rather than the + * caller of git__calloc(). + * + * Therefore, we declare a set of "git__crtdbg__" routines to replace + * the corresponding "git__" routines and re-define the "git__" symbols + * as macros. This allows us to get and report the file:line info of + * the real caller. + * + * We DO NOT replace the "git__free" routine because it needs to remain + * a function pointer because it is used as a function argument when + * setting up various structure "destructors". + * + * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes + * "free" to be remapped to "_free_dbg" and this causes problems for + * structures which define a field named "free". + * + * Finally, CRTDBG must be explicitly enabled and configured at program + * startup. See tests/main.c for an example. + */ -GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line) -{ - return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line); -} +#include "win32/w32_crtdbg_stacktrace.h" #define git__malloc(len) git__crtdbg__malloc(len, __FILE__, __LINE__) #define git__calloc(nelem, elsize) git__crtdbg__calloc(nelem, elsize, __FILE__, __LINE__) @@ -363,8 +289,6 @@ extern int git__strncasecmp(const char *a, const char *b, size_t sz); extern int git__strcasesort_cmp(const char *a, const char *b); -#include "thread-utils.h" - typedef struct { git_atomic refcount; void *owner; diff --git a/src/varint.c b/src/varint.c index 2f868607c..9ffc1d744 100644 --- a/src/varint.c +++ b/src/varint.c @@ -5,7 +5,6 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "varint.h" uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len) @@ -36,7 +35,7 @@ int git_encode_varint(unsigned char *buf, size_t bufsize, uintmax_t value) while (value >>= 7) varint[--pos] = 128 | (--value & 127); if (buf) { - if (bufsize < pos) + if (bufsize < (sizeof(varint) - pos)) return -1; memcpy(buf, varint + pos, sizeof(varint) - pos); } diff --git a/src/varint.h b/src/varint.h index 650ec7d2a..652e22486 100644 --- a/src/varint.h +++ b/src/varint.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_varint_h__ #define INCLUDE_varint_h__ +#include "common.h" + #include <stdint.h> extern int git_encode_varint(unsigned char *, size_t, uintmax_t); diff --git a/src/vector.c b/src/vector.c index db1dcf89a..b12fa942d 100644 --- a/src/vector.c +++ b/src/vector.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "vector.h" + #include "integer.h" /* In elements, not bytes */ @@ -401,3 +401,22 @@ int git_vector_verify_sorted(const git_vector *v) return 0; } + +void git_vector_reverse(git_vector *v) +{ + size_t a, b; + + if (v->length == 0) + return; + + a = 0; + b = v->length - 1; + + while (a < b) { + void *tmp = v->contents[a]; + v->contents[a] = v->contents[b]; + v->contents[b] = tmp; + a++; + b--; + } +} diff --git a/src/vector.h b/src/vector.h index 96d149e16..cc4c314d5 100644 --- a/src/vector.h +++ b/src/vector.h @@ -118,4 +118,9 @@ GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp) /* Just use this in tests, not for realz. returns -1 if not sorted */ int git_vector_verify_sorted(const git_vector *v); +/** + * Reverse the vector in-place. + */ +void git_vector_reverse(git_vector *v); + #endif diff --git a/src/win32/dir.c b/src/win32/dir.c index c15757085..1d37874e4 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -4,6 +4,9 @@ * 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 "dir.h" + #define GIT__WIN32_NO_WRAP_DIR #include "posix.h" @@ -28,7 +31,7 @@ git__DIR *git__opendir(const char *dir) new->h = FindFirstFileW(filter_w, &new->f); if (new->h == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Could not open directory '%s'", dir); + giterr_set(GITERR_OS, "could not open directory '%s'", dir); git__free(new); return NULL; } @@ -53,7 +56,7 @@ int git__readdir_ext( else if (!FindNextFileW(d->h, &d->f)) { if (GetLastError() == ERROR_NO_MORE_FILES) return 0; - giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir); + giterr_set(GITERR_OS, "could not read from directory '%s'", d->dir); return -1; } @@ -98,7 +101,7 @@ void git__rewinddir(git__DIR *d) d->h = FindFirstFileW(filter_w, &d->f); if (d->h == INVALID_HANDLE_VALUE) - giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir); + giterr_set(GITERR_OS, "could not open directory '%s'", d->dir); else d->first = 1; } diff --git a/src/win32/dir.h b/src/win32/dir.h index bef39d774..704a9a8a9 100644 --- a/src/win32/dir.h +++ b/src/win32/dir.h @@ -8,6 +8,7 @@ #define INCLUDE_dir_h__ #include "common.h" + #include "w32_util.h" struct git__dirent { diff --git a/src/win32/error.c b/src/win32/error.c index 6b450093f..3a52fb5a9 100644 --- a/src/win32/error.c +++ b/src/win32/error.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "error.h" + #include "utf-conv.h" #ifdef GIT_WINHTTP diff --git a/src/win32/error.h b/src/win32/error.h index 12947a2e6..a2ecf6a6a 100644 --- a/src/win32/error.h +++ b/src/win32/error.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_git_win32_error_h__ #define INCLUDE_git_win32_error_h__ +#include "common.h" + extern char *git_win32_get_error_message(DWORD error_code); #endif diff --git a/src/win32/findfile.c b/src/win32/findfile.c index 58c22279e..d56aa1fd2 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -5,10 +5,11 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "findfile.h" + #include "path_w32.h" #include "utf-conv.h" #include "path.h" -#include "findfile.h" #define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" @@ -38,7 +39,7 @@ static int win32_path_to_8(git_buf *dest, const wchar_t *src) git_win32_utf8_path utf8_path; if (git_win32_path_to_utf8(utf8_path, src) < 0) { - giterr_set(GITERR_OS, "Unable to convert path to UTF-8"); + giterr_set(GITERR_OS, "unable to convert path to UTF-8"); return -1; } diff --git a/src/win32/findfile.h b/src/win32/findfile.h index 3d5fff439..1eae4691c 100644 --- a/src/win32/findfile.h +++ b/src/win32/findfile.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_git_findfile_h__ #define INCLUDE_git_findfile_h__ +#include "common.h" + extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath); extern int git_win32__find_global_dirs(git_buf *out); extern int git_win32__find_xdg_dirs(git_buf *out); diff --git a/src/win32/map.c b/src/win32/map.c index 03a3646a6..6a17aeb64 100644 --- a/src/win32/map.c +++ b/src/win32/map.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" + #include "map.h" #include <errno.h> @@ -67,7 +69,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs if (fh == INVALID_HANDLE_VALUE) { errno = EBADF; - giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value"); + giterr_set(GITERR_OS, "failed to mmap. Invalid handle value"); return -1; } @@ -86,13 +88,13 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs if (page_offset != 0) { /* offset must be multiple of the allocation granularity */ errno = EINVAL; - giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of allocation granularity"); + giterr_set(GITERR_OS, "failed to mmap. Offset must be multiple of allocation granularity"); return -1; } out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL); if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value"); + giterr_set(GITERR_OS, "failed to mmap. Invalid handle value"); out->fmh = NULL; return -1; } @@ -103,7 +105,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs off_hi = (DWORD)(page_start >> 32); out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len); if (!out->data) { - giterr_set(GITERR_OS, "Failed to mmap. No data written"); + giterr_set(GITERR_OS, "failed to mmap. No data written"); CloseHandle(out->fmh); out->fmh = NULL; return -1; @@ -121,7 +123,7 @@ int p_munmap(git_map *map) if (map->data) { if (!UnmapViewOfFile(map->data)) { - giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file"); + giterr_set(GITERR_OS, "failed to munmap. Could not unmap view of file"); error = -1; } map->data = NULL; @@ -129,7 +131,7 @@ int p_munmap(git_map *map) if (map->fmh) { if (!CloseHandle(map->fmh)) { - giterr_set(GITERR_OS, "Failed to munmap. Could not close handle"); + giterr_set(GITERR_OS, "failed to munmap. Could not close handle"); error = -1; } map->fmh = NULL; diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c index 40b95c33b..f8416b848 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -5,9 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" -#include "path.h" #include "path_w32.h" + +#include "path.h" #include "utf-conv.h" #include "posix.h" #include "reparse.h" diff --git a/src/win32/path_w32.h b/src/win32/path_w32.h index 3d9f82860..ac1fb4b9e 100644 --- a/src/win32/path_w32.h +++ b/src/win32/path_w32.h @@ -8,6 +8,7 @@ #define INCLUDE_git_path_w32_h__ #include "common.h" + #include "vector.h" /* diff --git a/src/win32/posix.h b/src/win32/posix.h index 5fab267c2..64769ecd3 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -14,6 +14,9 @@ #include "utf-conv.h" #include "dir.h" +extern unsigned long git_win32__createfile_sharemode; +extern int git_win32__retries; + typedef SOCKET GIT_SOCKET; #define p_lseek(f,n,w) _lseeki64(f, n, w) @@ -57,4 +60,7 @@ extern int p_lstat_posixly(const char *filename, struct stat *buf); extern struct tm * p_localtime_r(const time_t *timer, struct tm *result); extern struct tm * p_gmtime_r(const time_t *timer, struct tm *result); +/* Use the bundled regcomp */ +#define p_regcomp regcomp + #endif diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index fea634b00..f51e1e546 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -4,6 +4,9 @@ * 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 "common.h" + #include "../posix.h" #include "../fileops.h" #include "path.h" @@ -26,15 +29,6 @@ #define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif -/* Options which we always provide to _wopen. - * - * _O_BINARY - Raw access; no translation of CR or LF characters - * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes. - * The Windows default is 'not inheritable', but the CRT's default (following - * POSIX convention) is 'inheritable'. We have no desire for our handles to be - * inheritable on Windows, so specify the flag to get default behavior back. */ -#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT) - /* Allowable mode bits on Win32. Using mode bits that are not supported on * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it * so we simply remove them. @@ -44,6 +38,167 @@ /* GetFinalPathNameByHandleW signature */ typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD); +unsigned long git_win32__createfile_sharemode = + FILE_SHARE_READ | FILE_SHARE_WRITE; +int git_win32__retries = 10; + +GIT_INLINE(void) set_errno(void) +{ + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_NO_MORE_FILES: + case ERROR_BAD_NETPATH: + case ERROR_BAD_NET_NAME: + case ERROR_BAD_PATHNAME: + case ERROR_FILENAME_EXCED_RANGE: + errno = ENOENT; + break; + case ERROR_BAD_ENVIRONMENT: + errno = E2BIG; + break; + case ERROR_BAD_FORMAT: + case ERROR_INVALID_STARTING_CODESEG: + case ERROR_INVALID_STACKSEG: + case ERROR_INVALID_MODULETYPE: + case ERROR_INVALID_EXE_SIGNATURE: + case ERROR_EXE_MARKED_INVALID: + case ERROR_BAD_EXE_FORMAT: + case ERROR_ITERATED_DATA_EXCEEDS_64k: + case ERROR_INVALID_MINALLOCSIZE: + case ERROR_DYNLINK_FROM_INVALID_RING: + case ERROR_IOPL_NOT_ENABLED: + case ERROR_INVALID_SEGDPL: + case ERROR_AUTODATASEG_EXCEEDS_64k: + case ERROR_RING2SEG_MUST_BE_MOVABLE: + case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: + case ERROR_INFLOOP_IN_RELOC_CHAIN: + errno = ENOEXEC; + break; + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_TARGET_HANDLE: + case ERROR_DIRECT_ACCESS_HANDLE: + errno = EBADF; + break; + case ERROR_WAIT_NO_CHILDREN: + case ERROR_CHILD_NOT_COMPLETE: + errno = ECHILD; + break; + case ERROR_NO_PROC_SLOTS: + case ERROR_MAX_THRDS_REACHED: + case ERROR_NESTING_NOT_ALLOWED: + errno = EAGAIN; + break; + case ERROR_ARENA_TRASHED: + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_INVALID_BLOCK: + case ERROR_NOT_ENOUGH_QUOTA: + errno = ENOMEM; + break; + case ERROR_ACCESS_DENIED: + case ERROR_CURRENT_DIRECTORY: + case ERROR_WRITE_PROTECT: + case ERROR_BAD_UNIT: + case ERROR_NOT_READY: + case ERROR_BAD_COMMAND: + case ERROR_CRC: + case ERROR_BAD_LENGTH: + case ERROR_SEEK: + case ERROR_NOT_DOS_DISK: + case ERROR_SECTOR_NOT_FOUND: + case ERROR_OUT_OF_PAPER: + case ERROR_WRITE_FAULT: + case ERROR_READ_FAULT: + case ERROR_GEN_FAILURE: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_WRONG_DISK: + case ERROR_SHARING_BUFFER_EXCEEDED: + case ERROR_NETWORK_ACCESS_DENIED: + case ERROR_CANNOT_MAKE: + case ERROR_FAIL_I24: + case ERROR_DRIVE_LOCKED: + case ERROR_SEEK_ON_DEVICE: + case ERROR_NOT_LOCKED: + case ERROR_LOCK_FAILED: + errno = EACCES; + break; + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + errno = EEXIST; + break; + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + break; + case ERROR_INVALID_FUNCTION: + case ERROR_INVALID_ACCESS: + case ERROR_INVALID_DATA: + case ERROR_INVALID_PARAMETER: + case ERROR_NEGATIVE_SEEK: + errno = EINVAL; + break; + case ERROR_TOO_MANY_OPEN_FILES: + errno = EMFILE; + break; + case ERROR_DISK_FULL: + errno = ENOSPC; + break; + case ERROR_BROKEN_PIPE: + errno = EPIPE; + break; + case ERROR_DIR_NOT_EMPTY: + errno = ENOTEMPTY; + break; + default: + errno = EINVAL; + } +} + +GIT_INLINE(bool) last_error_retryable(void) +{ + int os_error = GetLastError(); + + return (os_error == ERROR_SHARING_VIOLATION || + os_error == ERROR_ACCESS_DENIED); +} + +#define do_with_retries(fn, remediation) \ + do { \ + int __retry, __ret; \ + for (__retry = git_win32__retries; __retry; __retry--) { \ + if ((__ret = (fn)) != GIT_RETRY) \ + return __ret; \ + if (__retry > 1 && (__ret = (remediation)) != 0) { \ + if (__ret == GIT_RETRY) \ + continue; \ + return __ret; \ + } \ + Sleep(5); \ + } \ + return -1; \ + } while (0) \ + +static int ensure_writable(wchar_t *path) +{ + DWORD attrs; + + if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES) + goto on_error; + + if ((attrs & FILE_ATTRIBUTE_READONLY) == 0) + return 0; + + if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY))) + goto on_error; + + return GIT_RETRY; + +on_error: + set_errno(); + return -1; +} + /** * Truncate or extend file. * @@ -89,30 +244,34 @@ int p_link(const char *old, const char *new) return -1; } -int p_unlink(const char *path) +GIT_INLINE(int) unlink_once(const wchar_t *path) { - git_win32_path buf; - int error; + if (DeleteFileW(path)) + return 0; - if (git_win32_path_from_utf8(buf, path) < 0) - return -1; + if (last_error_retryable()) + return GIT_RETRY; - error = _wunlink(buf); + set_errno(); + return -1; +} - /* If the file could not be deleted because it was - * read-only, clear the bit and try again */ - if (error == -1 && errno == EACCES) { - _wchmod(buf, 0666); - error = _wunlink(buf); - } +int p_unlink(const char *path) +{ + git_win32_path wpath; - return error; + if (git_win32_path_from_utf8(wpath, path) < 0) + return -1; + + do_with_retries(unlink_once(wpath), ensure_writable(wpath)); } int p_fsync(int fd) { HANDLE fh = (HANDLE)_get_osfhandle(fd); + p_fsync__cnt++; + if (fh == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; @@ -210,44 +369,6 @@ int p_lstat_posixly(const char *filename, struct stat *buf) return do_lstat(filename, buf, true); } -int p_utimes(const char *filename, const struct p_timeval times[2]) -{ - int fd, error; - - if ((fd = p_open(filename, O_RDWR)) < 0) - return fd; - - error = p_futimes(fd, times); - - close(fd); - return error; -} - -int p_futimes(int fd, const struct p_timeval times[2]) -{ - HANDLE handle; - FILETIME atime = {0}, mtime = {0}; - - if (times == NULL) { - SYSTEMTIME st; - - GetSystemTime(&st); - SystemTimeToFileTime(&st, &atime); - SystemTimeToFileTime(&st, &mtime); - } else { - git_win32__timeval_to_filetime(&atime, times[0]); - git_win32__timeval_to_filetime(&mtime, times[1]); - } - - if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) - return -1; - - if (SetFileTime(handle, NULL, &atime, &mtime) == 0) - return -1; - - return 0; -} - int p_readlink(const char *path, char *buf, size_t bufsiz) { git_win32_path path_w, target_w; @@ -280,12 +401,91 @@ int p_symlink(const char *old, const char *new) return git_futils_fake_symlink(old, new); } +struct open_opts { + DWORD access; + DWORD sharing; + SECURITY_ATTRIBUTES security; + DWORD creation_disposition; + DWORD attributes; + int osf_flags; +}; + +GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode) +{ + memset(opts, 0, sizeof(struct open_opts)); + + switch (flags & (O_WRONLY | O_RDWR)) { + case O_WRONLY: + opts->access = GENERIC_WRITE; + break; + case O_RDWR: + opts->access = GENERIC_READ | GENERIC_WRITE; + break; + default: + opts->access = GENERIC_READ; + break; + } + + opts->sharing = (DWORD)git_win32__createfile_sharemode; + + switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) { + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + opts->creation_disposition = CREATE_NEW; + break; + case O_CREAT | O_TRUNC: + opts->creation_disposition = CREATE_ALWAYS; + break; + case O_TRUNC: + opts->creation_disposition = TRUNCATE_EXISTING; + break; + case O_CREAT: + opts->creation_disposition = OPEN_ALWAYS; + break; + default: + opts->creation_disposition = OPEN_EXISTING; + break; + } + + opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ? + FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL; + opts->osf_flags = flags & (O_RDONLY | O_APPEND); + + opts->security.nLength = sizeof(SECURITY_ATTRIBUTES); + opts->security.lpSecurityDescriptor = NULL; + opts->security.bInheritHandle = 0; +} + +GIT_INLINE(int) open_once( + const wchar_t *path, + struct open_opts *opts) +{ + int fd; + + HANDLE handle = CreateFileW(path, opts->access, opts->sharing, + &opts->security, opts->creation_disposition, opts->attributes, 0); + + if (handle == INVALID_HANDLE_VALUE) { + if (last_error_retryable()) + return GIT_RETRY; + + set_errno(); + return -1; + } + + if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0) + CloseHandle(handle); + + return fd; +} + int p_open(const char *path, int flags, ...) { - git_win32_path buf; + git_win32_path wpath; mode_t mode = 0; + struct open_opts opts = {0}; - if (git_win32_path_from_utf8(buf, path) < 0) + if (git_win32_path_from_utf8(wpath, path) < 0) return -1; if (flags & O_CREAT) { @@ -296,19 +496,83 @@ int p_open(const char *path, int flags, ...) va_end(arg_list); } - return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK); + open_opts_from_posix(&opts, flags, mode); + + do_with_retries( + open_once(wpath, &opts), + 0); } int p_creat(const char *path, mode_t mode) { - git_win32_path buf; + return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); +} - if (git_win32_path_from_utf8(buf, path) < 0) +int p_utimes(const char *path, const struct p_timeval times[2]) +{ + git_win32_path wpath; + int fd, error; + DWORD attrs_orig, attrs_new = 0; + struct open_opts opts = { 0 }; + + if (git_win32_path_from_utf8(wpath, path) < 0) return -1; - return _wopen(buf, - _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, - mode & WIN32_MODE_MASK); + attrs_orig = GetFileAttributesW(wpath); + + if (attrs_orig & FILE_ATTRIBUTE_READONLY) { + attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY; + + if (!SetFileAttributesW(wpath, attrs_new)) { + giterr_set(GITERR_OS, "failed to set attributes"); + return -1; + } + } + + open_opts_from_posix(&opts, O_RDWR, 0); + + if ((fd = open_once(wpath, &opts)) < 0) { + error = -1; + goto done; + } + + error = p_futimes(fd, times); + close(fd); + +done: + if (attrs_orig != attrs_new) { + DWORD os_error = GetLastError(); + SetFileAttributesW(wpath, attrs_orig); + SetLastError(os_error); + } + + return error; +} + +int p_futimes(int fd, const struct p_timeval times[2]) +{ + HANDLE handle; + FILETIME atime = { 0 }, mtime = { 0 }; + + if (times == NULL) { + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &atime); + SystemTimeToFileTime(&st, &mtime); + } + else { + git_win32__timeval_to_filetime(&atime, times[0]); + git_win32__timeval_to_filetime(&mtime, times[1]); + } + + if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) + return -1; + + if (SetFileTime(handle, NULL, &atime, &mtime) == 0) + return -1; + + return 0; } int p_getcwd(char *buffer_out, size_t size) @@ -581,62 +845,27 @@ int p_access(const char* path, mode_t mode) return _waccess(buf, mode & WIN32_MODE_MASK); } -static int ensure_writable(wchar_t *fpath) +GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to) { - DWORD attrs; - - attrs = GetFileAttributesW(fpath); - if (attrs == INVALID_FILE_ATTRIBUTES) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - return 0; - - giterr_set(GITERR_OS, "failed to get attributes"); - return -1; - } - - if (!(attrs & FILE_ATTRIBUTE_READONLY)) + if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) return 0; - attrs &= ~FILE_ATTRIBUTE_READONLY; - if (!SetFileAttributesW(fpath, attrs)) { - giterr_set(GITERR_OS, "failed to set attributes"); - return -1; - } + if (last_error_retryable()) + return GIT_RETRY; - return 0; + set_errno(); + return -1; } int p_rename(const char *from, const char *to) { - git_win32_path wfrom; - git_win32_path wto; - int rename_tries; - int rename_succeeded; - int error; + git_win32_path wfrom, wto; if (git_win32_path_from_utf8(wfrom, from) < 0 || git_win32_path_from_utf8(wto, to) < 0) return -1; - /* wait up to 50ms if file is locked by another thread or process */ - rename_tries = 0; - rename_succeeded = 0; - while (rename_tries < 10) { - if (ensure_writable(wto) == 0 && - MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) { - rename_succeeded = 1; - break; - } - - error = GetLastError(); - if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) { - Sleep(5); - rename_tries++; - } else - break; - } - - return rename_succeeded ? 0 : -1; + do_with_retries(rename_once(wfrom, wto), ensure_writable(wto)); } int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags) diff --git a/src/win32/precompiled.h b/src/win32/precompiled.h index 10ca0b80c..851a083d5 100644 --- a/src/win32/precompiled.h +++ b/src/win32/precompiled.h @@ -1,3 +1,5 @@ +#include "common.h" + #include <assert.h> #include <errno.h> #include <limits.h> @@ -20,4 +22,3 @@ #endif #include "git2.h" -#include "common.h" diff --git a/src/win32/thread.c b/src/win32/thread.c index 80d56ce5d..2d9600515 100644 --- a/src/win32/thread.c +++ b/src/win32/thread.c @@ -6,6 +6,7 @@ */ #include "thread.h" + #include "../global.h" #define CLEAN_THREAD_EXIT 0x6F012842 @@ -26,6 +27,9 @@ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter) { git_thread *thread = lpParameter; + /* Set the current thread for `git_thread_exit` */ + GIT_GLOBAL->current_thread = thread; + thread->result = thread->proc(thread->param); git__free_tls_data(); @@ -95,6 +99,21 @@ int git_thread_join( return 0; } +void git_thread_exit(void *value) +{ + assert(GIT_GLOBAL->current_thread); + GIT_GLOBAL->current_thread->result = value; + + git__free_tls_data(); + + ExitThread(CLEAN_THREAD_EXIT); +} + +size_t git_thread_currentid(void) +{ + return GetCurrentThreadId(); +} + int git_mutex_init(git_mutex *GIT_RESTRICT mutex) { InitializeCriticalSection(mutex); diff --git a/src/win32/thread.h b/src/win32/thread.h index 0d01822a6..d217722ec 100644 --- a/src/win32/thread.h +++ b/src/win32/thread.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_win32_thread_h__ #define INCLUDE_win32_thread_h__ -#include "../common.h" +#include "common.h" #if defined (_MSC_VER) # define GIT_RESTRICT __restrict @@ -41,6 +41,8 @@ int git_thread_create(git_thread *GIT_RESTRICT, void *(*) (void *), void *GIT_RESTRICT); int git_thread_join(git_thread *, void **); +size_t git_thread_currentid(void); +void git_thread_exit(void *); int git_mutex_init(git_mutex *GIT_RESTRICT mutex); int git_mutex_free(git_mutex *); diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c index 96fd4606e..4bde3023a 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -5,7 +5,6 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "utf-conv.h" GIT_INLINE(void) git__set_errno(void) diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h index 33b95f59f..bab7ba9fd 100644 --- a/src/win32/utf-conv.h +++ b/src/win32/utf-conv.h @@ -7,9 +7,10 @@ #ifndef INCLUDE_git_utfconv_h__ #define INCLUDE_git_utfconv_h__ -#include <wchar.h> #include "common.h" +#include <wchar.h> + #ifndef WC_ERR_INVALID_CHARS # define WC_ERR_INVALID_CHARS 0x80 #endif diff --git a/src/win32/w32_buffer.c b/src/win32/w32_buffer.c index 9122baaa6..45c024d31 100644 --- a/src/win32/w32_buffer.c +++ b/src/win32/w32_buffer.c @@ -5,8 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" #include "w32_buffer.h" + #include "../buffer.h" #include "utf-conv.h" diff --git a/src/win32/w32_buffer.h b/src/win32/w32_buffer.h index 62243986f..e15ea6864 100644 --- a/src/win32/w32_buffer.h +++ b/src/win32/w32_buffer.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_git_win32_buffer_h__ #define INCLUDE_git_win32_buffer_h__ +#include "common.h" + #include "../buffer.h" /** diff --git a/src/win32/w32_crtdbg_stacktrace.c b/src/win32/w32_crtdbg_stacktrace.c index a778f4164..7b3c3fb4c 100644 --- a/src/win32/w32_crtdbg_stacktrace.c +++ b/src/win32/w32_crtdbg_stacktrace.c @@ -5,9 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "w32_crtdbg_stacktrace.h" + #if defined(GIT_MSVC_CRTDBG) #include "w32_stack.h" -#include "w32_crtdbg_stacktrace.h" #define CRTDBG_STACKTRACE__UID_LEN (15) @@ -253,11 +254,11 @@ int git_win32__crtdbg_stacktrace__dump( bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET); if (b_leaks_since_mark && b_leaks_total) { - giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL."); + giterr_set(GITERR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL."); return GIT_ERROR; } if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) { - giterr_set(GITERR_INVALID, "Nothing to do."); + giterr_set(GITERR_INVALID, "nothing to do."); return GIT_ERROR; } diff --git a/src/win32/w32_crtdbg_stacktrace.h b/src/win32/w32_crtdbg_stacktrace.h index 40ca60d53..bb869b347 100644 --- a/src/win32/w32_crtdbg_stacktrace.h +++ b/src/win32/w32_crtdbg_stacktrace.h @@ -7,8 +7,16 @@ #ifndef INCLUDE_w32_crtdbg_stacktrace_h__ #define INCLUDE_w32_crtdbg_stacktrace_h__ +#include "common.h" + #if defined(GIT_MSVC_CRTDBG) +#include <stdlib.h> +#include <crtdbg.h> + +#include "git2/errors.h" +#include "strnlen.h" + /** * Initialize our memory leak tracking and de-dup data structures. * This should ONLY be called by git_libgit2_init(). @@ -89,5 +97,80 @@ GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump( */ const char *git_win32__crtdbg_stacktrace(int skip, const char *file); +GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line) +{ + void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); + if (!ptr) giterr_set_oom(); + return ptr; +} + +GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line) +{ + void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); + if (!ptr) giterr_set_oom(); + return ptr; +} + +GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line) +{ + char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); + if (!ptr) giterr_set_oom(); + return ptr; +} + +GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line) +{ + size_t length = 0, alloclength; + char *ptr; + + length = p_strnlen(str, n); + + if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) || + !(ptr = git__crtdbg__malloc(alloclength, file, line))) + return NULL; + + if (length) + memcpy(ptr, str, length); + + ptr[length] = '\0'; + + return ptr; +} + +GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line) +{ + char *ptr; + size_t alloclen; + + if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) || + !(ptr = git__crtdbg__malloc(alloclen, file, line))) + return NULL; + + memcpy(ptr, start, n); + ptr[n] = '\0'; + return ptr; +} + +GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line) +{ + void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); + if (!new_ptr) giterr_set_oom(); + return new_ptr; +} + +GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line) +{ + size_t newsize; + + return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ? + NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); +} + +GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line) +{ + return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line); +} + + #endif #endif diff --git a/src/win32/w32_stack.c b/src/win32/w32_stack.c index 15af3dcb7..b40f9d2b4 100644 --- a/src/win32/w32_stack.c +++ b/src/win32/w32_stack.c @@ -5,11 +5,12 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "w32_stack.h" + #if defined(GIT_MSVC_CRTDBG) #include "Windows.h" #include "Dbghelp.h" #include "win32/posix.h" -#include "w32_stack.h" #include "hash.h" /** diff --git a/src/win32/w32_stack.h b/src/win32/w32_stack.h index 21170bd2f..a514ace6f 100644 --- a/src/win32/w32_stack.h +++ b/src/win32/w32_stack.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_w32_stack_h__ #define INCLUDE_w32_stack_h__ +#include "common.h" + #if defined(GIT_MSVC_CRTDBG) /** diff --git a/src/win32/w32_util.c b/src/win32/w32_util.c index 60311bb50..b7b1ffa10 100644 --- a/src/win32/w32_util.c +++ b/src/win32/w32_util.c @@ -68,7 +68,7 @@ int git_win32__set_hidden(const char *path, bool hidden) newattrs = attrs & ~FILE_ATTRIBUTE_HIDDEN; if (attrs != newattrs && !SetFileAttributesW(buf, newattrs)) { - giterr_set(GITERR_OS, "Failed to %s hidden bit for '%s'", + giterr_set(GITERR_OS, "failed to %s hidden bit for '%s'", hidden ? "set" : "unset", path); return -1; } diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 2e475e5e9..d81e55a00 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_w32_util_h__ #define INCLUDE_w32_util_h__ +#include "common.h" + #include "utf-conv.h" #include "posix.h" #include "path_w32.h" @@ -174,7 +176,7 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( /* st_size gets the UTF-8 length of the target name, in bytes, * not counting the NULL terminator */ if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) { - giterr_set(GITERR_OS, "Could not convert reparse point name for '%s'", path); + giterr_set(GITERR_OS, "could not convert reparse point name for '%ls'", path); return -1; } } diff --git a/src/worktree.c b/src/worktree.c new file mode 100644 index 000000000..5a814a2ec --- /dev/null +++ b/src/worktree.c @@ -0,0 +1,557 @@ +/* + * 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 "worktree.h" + +#include "git2/branch.h" +#include "git2/commit.h" +#include "git2/worktree.h" + +#include "repository.h" + +static bool is_worktree_dir(const char *dir) +{ + git_buf buf = GIT_BUF_INIT; + int error; + + if (git_buf_sets(&buf, dir) < 0) + return -1; + + error = git_path_contains_file(&buf, "commondir") + && git_path_contains_file(&buf, "gitdir") + && git_path_contains_file(&buf, "HEAD"); + + git_buf_free(&buf); + return error; +} + +int git_worktree_list(git_strarray *wts, git_repository *repo) +{ + git_vector worktrees = GIT_VECTOR_INIT; + git_buf path = GIT_BUF_INIT; + char *worktree; + unsigned i, len; + int error; + + assert(wts && repo); + + wts->count = 0; + wts->strings = NULL; + + if ((error = git_buf_printf(&path, "%s/worktrees/", repo->commondir)) < 0) + goto exit; + if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr)) + goto exit; + if ((error = git_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0) + goto exit; + + len = path.size; + + git_vector_foreach(&worktrees, i, worktree) { + git_buf_truncate(&path, len); + git_buf_puts(&path, worktree); + + if (!is_worktree_dir(path.ptr)) { + git_vector_remove(&worktrees, i); + git__free(worktree); + } + } + + wts->strings = (char **)git_vector_detach(&wts->count, NULL, &worktrees); + +exit: + git_buf_free(&path); + + return error; +} + +char *git_worktree__read_link(const char *base, const char *file) +{ + git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT; + + assert(base && file); + + if (git_buf_joinpath(&path, base, file) < 0) + goto err; + if (git_futils_readbuffer(&buf, path.ptr) < 0) + goto err; + git_buf_free(&path); + + git_buf_rtrim(&buf); + + if (!git_path_is_relative(buf.ptr)) + return git_buf_detach(&buf); + + if (git_buf_sets(&path, base) < 0) + goto err; + if (git_path_apply_relative(&path, buf.ptr) < 0) + goto err; + git_buf_free(&buf); + + return git_buf_detach(&path); + +err: + git_buf_free(&buf); + git_buf_free(&path); + + return NULL; +} + +static int write_wtfile(const char *base, const char *file, const git_buf *buf) +{ + git_buf path = GIT_BUF_INIT; + int err; + + assert(base && file && buf); + + if ((err = git_buf_joinpath(&path, base, file)) < 0) + goto out; + + if ((err = git_futils_writebuffer(buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) + goto out; + +out: + git_buf_free(&path); + + return err; +} + +static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name) +{ + git_buf gitdir = GIT_BUF_INIT; + git_worktree *wt = NULL; + int error = 0; + + if (!is_worktree_dir(dir)) { + error = -1; + goto out; + } + + if ((wt = git__calloc(1, sizeof(struct git_repository))) == NULL) { + error = -1; + goto out; + } + + if ((wt->name = git__strdup(name)) == NULL + || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL + || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL + || (wt->parent_path = git__strdup(parent)) == NULL) { + error = -1; + goto out; + } + + if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0) + goto out; + wt->gitdir_path = git_buf_detach(&gitdir); + + wt->locked = !!git_worktree_is_locked(NULL, wt); + + *out = wt; + +out: + if (error) + git_worktree_free(wt); + git_buf_free(&gitdir); + + return error; +} + +int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name) +{ + git_buf path = GIT_BUF_INIT; + git_worktree *wt = NULL; + int error; + + assert(repo && name); + + *out = NULL; + + if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0) + goto out; + + if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0) + goto out; + +out: + git_buf_free(&path); + + if (error) + git_worktree_free(wt); + + return error; +} + +int git_worktree_open_from_repository(git_worktree **out, git_repository *repo) +{ + git_buf parent = GIT_BUF_INIT; + const char *gitdir, *commondir; + char *name = NULL; + int error = 0; + + if (!git_repository_is_worktree(repo)) { + giterr_set(GITERR_WORKTREE, "cannot open worktree of a non-worktree repo"); + error = -1; + goto out; + } + + gitdir = git_repository_path(repo); + commondir = git_repository_commondir(repo); + + if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0) + goto out; + + /* The name is defined by the last component in '.git/worktree/%s' */ + name = git_path_basename(gitdir); + + if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0) + goto out; + +out: + git__free(name); + git_buf_free(&parent); + + return error; +} + +void git_worktree_free(git_worktree *wt) +{ + if (!wt) + return; + + git__free(wt->commondir_path); + git__free(wt->gitlink_path); + git__free(wt->gitdir_path); + git__free(wt->parent_path); + git__free(wt->name); + git__free(wt); +} + +int git_worktree_validate(const git_worktree *wt) +{ + git_buf buf = GIT_BUF_INIT; + int err = 0; + + assert(wt); + + git_buf_puts(&buf, wt->gitdir_path); + if (!is_worktree_dir(buf.ptr)) { + giterr_set(GITERR_WORKTREE, + "Worktree gitdir ('%s') is not valid", + wt->gitlink_path); + err = -1; + goto out; + } + + if (!git_path_exists(wt->parent_path)) { + giterr_set(GITERR_WORKTREE, + "Worktree parent directory ('%s') does not exist ", + wt->parent_path); + err = -2; + goto out; + } + + if (!git_path_exists(wt->commondir_path)) { + giterr_set(GITERR_WORKTREE, + "Worktree common directory ('%s') does not exist ", + wt->commondir_path); + err = -3; + goto out; + } + +out: + git_buf_free(&buf); + + return err; +} + +int git_worktree_add_init_options(git_worktree_add_options *opts, + unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, + git_worktree_add_options, GIT_WORKTREE_ADD_OPTIONS_INIT); + return 0; +} + +int git_worktree_add(git_worktree **out, git_repository *repo, + const char *name, const char *worktree, + const git_worktree_add_options *opts) +{ + git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT; + git_reference *ref = NULL, *head = NULL; + git_commit *commit = NULL; + git_repository *wt = NULL; + git_checkout_options coopts = GIT_CHECKOUT_OPTIONS_INIT; + git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT; + int err; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_ADD_OPTIONS_VERSION, "git_worktree_add_options"); + + if (opts) + memcpy(&wtopts, opts, sizeof(wtopts)); + + assert(out && repo && name && worktree); + + *out = NULL; + + /* Create gitdir directory ".git/worktrees/<name>" */ + if ((err = git_buf_joinpath(&gitdir, repo->commondir, "worktrees")) < 0) + goto out; + if (!git_path_exists(gitdir.ptr)) + if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_buf_joinpath(&gitdir, gitdir.ptr, name)) < 0) + goto out; + if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0) + goto out; + + /* Create worktree work dir */ + if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0) + goto out; + + if (wtopts.lock) { + int fd; + + if ((err = git_buf_joinpath(&buf, gitdir.ptr, "locked")) < 0) + goto out; + + if ((fd = p_creat(buf.ptr, 0644)) < 0) { + err = fd; + goto out; + } + + p_close(fd); + git_buf_clear(&buf); + } + + /* Create worktree .git file */ + if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0) + goto out; + if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0) + goto out; + + /* Create gitdir files */ + if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0) + || (err = git_buf_putc(&buf, '\n')) < 0 + || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0) + goto out; + if ((err = git_buf_joinpath(&buf, wddir.ptr, ".git")) < 0 + || (err = git_buf_putc(&buf, '\n')) < 0 + || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0) + goto out; + + /* Create new branch */ + if ((err = git_repository_head(&head, repo)) < 0) + goto out; + if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0) + goto out; + if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0) + goto out; + + /* Set worktree's HEAD */ + if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0) + goto out; + if ((err = git_repository_open(&wt, wddir.ptr)) < 0) + goto out; + + /* Checkout worktree's HEAD */ + coopts.checkout_strategy = GIT_CHECKOUT_FORCE; + if ((err = git_checkout_head(wt, &coopts)) < 0) + goto out; + + /* Load result */ + if ((err = git_worktree_lookup(out, repo, name)) < 0) + goto out; + +out: + git_buf_free(&gitdir); + git_buf_free(&wddir); + git_buf_free(&buf); + git_reference_free(ref); + git_reference_free(head); + git_commit_free(commit); + git_repository_free(wt); + + return err; +} + +int git_worktree_lock(git_worktree *wt, char *creason) +{ + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + int err; + + assert(wt); + + if ((err = git_worktree_is_locked(NULL, wt)) < 0) + goto out; + + if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + goto out; + + if (creason) + git_buf_attach_notowned(&buf, creason, strlen(creason)); + + if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) + goto out; + + wt->locked = 1; + +out: + git_buf_free(&path); + + return err; +} + +int git_worktree_unlock(git_worktree *wt) +{ + git_buf path = GIT_BUF_INIT; + + assert(wt); + + if (!git_worktree_is_locked(NULL, wt)) + return 0; + + if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0) + return -1; + + if (p_unlink(path.ptr) != 0) { + git_buf_free(&path); + return -1; + } + + wt->locked = 0; + + git_buf_free(&path); + + return 0; +} + +int git_worktree_is_locked(git_buf *reason, const git_worktree *wt) +{ + git_buf path = GIT_BUF_INIT; + int ret; + + assert(wt); + + if (reason) + git_buf_clear(reason); + + if ((ret = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + goto out; + if ((ret = git_path_exists(path.ptr)) && reason) + git_futils_readbuffer(reason, path.ptr); + +out: + git_buf_free(&path); + + return ret; +} + +int git_worktree_prune_init_options( + git_worktree_prune_options *opts, + unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, + git_worktree_prune_options, GIT_WORKTREE_PRUNE_OPTIONS_INIT); + return 0; +} + +int git_worktree_is_prunable(git_worktree *wt, + git_worktree_prune_options *opts) +{ + git_buf reason = GIT_BUF_INIT; + git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); + + if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 && + git_worktree_is_locked(&reason, wt)) + { + if (!reason.size) + git_buf_attach_notowned(&reason, "no reason given", 15); + giterr_set(GITERR_WORKTREE, "Not pruning locked working tree: '%s'", reason.ptr); + git_buf_free(&reason); + + return 0; + } + + if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 && + git_worktree_validate(wt) == 0) + { + giterr_set(GITERR_WORKTREE, "Not pruning valid working tree"); + return 0; + } + + return 1; +} + +int git_worktree_prune(git_worktree *wt, + git_worktree_prune_options *opts) +{ + git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_buf path = GIT_BUF_INIT; + char *wtpath; + int err; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); + + if (!git_worktree_is_prunable(wt, &popts)) { + err = -1; + goto out; + } + + /* Delete gitdir in parent repository */ + if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0) + goto out; + if (!git_path_exists(path.ptr)) + { + giterr_set(GITERR_WORKTREE, "Worktree gitdir '%s' does not exist", path.ptr); + err = -1; + goto out; + } + if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0) + goto out; + + /* Skip deletion of the actual working tree if it does + * not exist or deletion was not requested */ + if ((popts.flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 || + !git_path_exists(wt->gitlink_path)) + { + goto out; + } + + if ((wtpath = git_path_dirname(wt->gitlink_path)) == NULL) + goto out; + git_buf_attach(&path, wtpath, 0); + if (!git_path_exists(path.ptr)) + { + giterr_set(GITERR_WORKTREE, "Working tree '%s' does not exist", path.ptr); + err = -1; + goto out; + } + if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0) + goto out; + +out: + git_buf_free(&path); + + return err; +} diff --git a/src/worktree.h b/src/worktree.h new file mode 100644 index 000000000..52d13cced --- /dev/null +++ b/src/worktree.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_worktree_h__ +#define INCLUDE_worktree_h__ + +#include "common.h" + +#include "git2/common.h" +#include "git2/worktree.h" + +struct git_worktree { + /* Name of the working tree. This is the name of the + * containing directory in the `$PARENT/.git/worktrees/` + * directory. */ + char *name; + + /* Path to the .git file in the working tree's repository */ + char *gitlink_path; + /* Path to the .git directory inside the parent's + * worktrees directory */ + char *gitdir_path; + /* Path to the common directory contained in the parent + * repository */ + char *commondir_path; + /* Path to the parent's working directory */ + char *parent_path; + + int locked:1; +}; + +char *git_worktree__read_link(const char *base, const char *file); + +#endif diff --git a/src/zstream.c b/src/zstream.c index d9ad4ca89..4895bdb16 100644 --- a/src/zstream.c +++ b/src/zstream.c @@ -5,9 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "zstream.h" + #include <zlib.h> -#include "zstream.h" #include "buffer.h" #define ZSTREAM_BUFFER_SIZE (1024 * 1024) @@ -21,9 +22,9 @@ static int zstream_seterr(git_zstream *zs) if (zs->zerr == Z_MEM_ERROR) giterr_set_oom(); else if (zs->z.msg) - giterr_set(GITERR_ZLIB, zs->z.msg); + giterr_set_str(GITERR_ZLIB, zs->z.msg); else - giterr_set(GITERR_ZLIB, "Unknown compression error"); + giterr_set(GITERR_ZLIB, "unknown compression error"); return -1; } diff --git a/src/zstream.h b/src/zstream.h index f0006d32e..fcc4a3148 100644 --- a/src/zstream.h +++ b/src/zstream.h @@ -7,9 +7,10 @@ #ifndef INCLUDE_zstream_h__ #define INCLUDE_zstream_h__ +#include "common.h" + #include <zlib.h> -#include "common.h" #include "buffer.h" typedef enum { |
