diff options
author | Carlos Martín Nieto <carlosmn@github.com> | 2016-02-19 13:06:51 +0100 |
---|---|---|
committer | Carlos Martín Nieto <carlosmn@github.com> | 2016-02-19 13:06:51 +0100 |
commit | 78e16c3442b57f967c31e65cfb4792dcf184c6e8 (patch) | |
tree | 36ea735e4c45ea360fae6e9aa944f2c02d48a256 | |
parent | b643501dbab3ba6a3c8343349986bc541e9197b8 (diff) | |
parent | 82abd40d80a8912043dd072a09962f06df193e36 (diff) | |
download | libgit2-78e16c3442b57f967c31e65cfb4792dcf184c6e8.tar.gz |
Merge pull request #3597 from ethomson/filter_registration
Filter registration
-rw-r--r-- | include/git2/sys/filter.h | 59 | ||||
-rw-r--r-- | src/filter.c | 279 | ||||
-rw-r--r-- | src/filter.h | 2 | ||||
-rw-r--r-- | src/global.c | 210 | ||||
-rw-r--r-- | src/openssl_stream.c | 120 | ||||
-rw-r--r-- | src/openssl_stream.h | 2 | ||||
-rw-r--r-- | src/thread-utils.h | 2 |
7 files changed, 370 insertions, 304 deletions
diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index baf1515d6..d0e5d4d6f 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -127,17 +127,6 @@ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *sr */ GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src); -/* - * struct git_filter - * - * The filter lifecycle: - * - initialize - first use of filter - * - shutdown - filter removed/unregistered from system - * - check - considering filter for file - * - apply - apply filter to file contents - * - cleanup - done with file - */ - /** * Initialize callback on filter * @@ -233,31 +222,51 @@ typedef void (*git_filter_cleanup_fn)( * To associate extra data with a filter, allocate extra data and put the * `git_filter` struct at the start of your data buffer, then cast the * `self` pointer to your larger structure when your callback is invoked. - * - * `version` should be set to GIT_FILTER_VERSION - * - * `attributes` is a whitespace-separated list of attribute names to check - * for this filter (e.g. "eol crlf text"). If the attribute name is bare, - * it will be simply loaded and passed to the `check` callback. If it has - * a value (i.e. "name=value"), the attribute must match that value for - * the filter to be applied. The value may be a wildcard (eg, "name=*"), - * in which case the filter will be invoked for any value for the given - * attribute name. See the attribute parameter of the `check` callback - * for the attribute value that was specified. - * - * The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks - * are all documented above with the respective function pointer typedefs. */ struct git_filter { + /** The `version` field should be set to `GIT_FILTER_VERSION`. */ unsigned int version; + /** + * A whitespace-separated list of attribute names to check for this + * filter (e.g. "eol crlf text"). If the attribute name is bare, it + * will be simply loaded and passed to the `check` callback. If it + * has a value (i.e. "name=value"), the attribute must match that + * value for the filter to be applied. The value may be a wildcard + * (eg, "name=*"), in which case the filter will be invoked for any + * value for the given attribute name. See the attribute parameter + * of the `check` callback for the attribute value that was specified. + */ const char *attributes; + /** Called when the filter is first used for any file. */ git_filter_init_fn initialize; + + /** Called when the filter is removed or unregistered from the system. */ git_filter_shutdown_fn shutdown; + + /** + * Called to determine whether the filter should be invoked for a + * given file. If this function returns `GIT_PASSTHROUGH` then the + * `apply` function will not be invoked and the contents will be passed + * through unmodified. + */ git_filter_check_fn check; + + /** + * Called to actually apply the filter to file contents. If this + * function returns `GIT_PASSTHROUGH` then the contents will be passed + * through unmodified. + */ git_filter_apply_fn apply; + + /** + * Called to apply the filter in a streaming manner. If this is not + * specified then the system will call `apply` with the whole buffer. + */ git_filter_stream_fn stream; + + /** Called when the system is done filtering for a file. */ git_filter_cleanup_fn cleanup; }; diff --git a/src/filter.c b/src/filter.c index 60473e4e1..a0628d779 100644 --- a/src/filter.c +++ b/src/filter.c @@ -56,80 +56,15 @@ static int filter_def_priority_cmp(const void *a, const void *b) return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; } -struct filter_registry { +struct git_filter_registry { + git_rwlock lock; git_vector filters; }; -static struct filter_registry *git__filter_registry = NULL; +static struct git_filter_registry filter_registry; -static void filter_registry_shutdown(void) -{ - struct filter_registry *reg = NULL; - size_t pos; - git_filter_def *fdef; - - if ((reg = git__swap(git__filter_registry, NULL)) == NULL) - return; - - git_vector_foreach(®->filters, pos, fdef) { - if (fdef->filter && fdef->filter->shutdown) { - fdef->filter->shutdown(fdef->filter); - fdef->initialized = false; - } - - git__free(fdef->filter_name); - git__free(fdef->attrdata); - git__free(fdef); - } - - git_vector_free(®->filters); - git__free(reg); -} - -static int filter_registry_initialize(void) -{ - int error = 0; - struct filter_registry *reg; - - if (git__filter_registry) - return 0; - - reg = git__calloc(1, sizeof(struct filter_registry)); - GITERR_CHECK_ALLOC(reg); - - if ((error = git_vector_init( - ®->filters, 2, filter_def_priority_cmp)) < 0) - goto cleanup; +static void git_filter_global_shutdown(void); - reg = git__compare_and_swap(&git__filter_registry, NULL, reg); - if (reg != NULL) - goto cleanup; - - git__on_shutdown(filter_registry_shutdown); - - /* try to register both default filters */ - { - git_filter *crlf = git_crlf_filter_new(); - git_filter *ident = git_ident_filter_new(); - - if (crlf && git_filter_register( - GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0) - crlf = NULL; - if (ident && git_filter_register( - GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) - ident = NULL; - - if (!crlf || !ident) - return -1; - } - - return 0; - -cleanup: - git_vector_free(®->filters); - git__free(reg); - return error; -} static int filter_def_scan_attrs( git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) @@ -210,40 +145,14 @@ static int filter_def_filter_key_check(const void *key, const void *fdef) return (key == filter) ? 0 : -1; } -static int filter_registry_find(size_t *pos, const char *name) -{ - return git_vector_search2( - pos, &git__filter_registry->filters, filter_def_name_key_check, name); -} - -static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) -{ - git_filter_def *fdef = NULL; - - if (!filter_registry_find(pos, name)) - fdef = git_vector_get(&git__filter_registry->filters, *pos); - - return fdef; -} - -int git_filter_register( +/* Note: callers must lock the registry before calling this function */ +static int filter_registry_insert( const char *name, git_filter *filter, int priority) { git_filter_def *fdef; size_t nattr = 0, nmatch = 0, alloc_len; git_buf attrs = GIT_BUF_INIT; - assert(name && filter); - - if (filter_registry_initialize() < 0) - return -1; - - if (!filter_registry_find(NULL, name)) { - giterr_set( - GITERR_FILTER, "Attempt to reregister existing filter '%s'", name); - return GIT_EEXISTS; - } - if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) return -1; @@ -265,21 +174,123 @@ int git_filter_register( filter_def_set_attrs(fdef); - if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) { + if (git_vector_insert(&filter_registry.filters, fdef) < 0) { git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); return -1; } - git_vector_sort(&git__filter_registry->filters); + git_vector_sort(&filter_registry.filters); return 0; } +int git_filter_global_init(void) +{ + git_filter *crlf = NULL, *ident = NULL; + int error = 0; + + if (git_rwlock_init(&filter_registry.lock) < 0) + return -1; + + if ((error = git_vector_init(&filter_registry.filters, 2, + filter_def_priority_cmp)) < 0) + goto done; + + if ((crlf = git_crlf_filter_new()) == NULL || + filter_registry_insert( + GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 || + (ident = git_ident_filter_new()) == NULL || + filter_registry_insert( + GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) + error = -1; + + git__on_shutdown(git_filter_global_shutdown); + +done: + if (error) { + git_filter_free(crlf); + git_filter_free(ident); + } + + return error; +} + +static void git_filter_global_shutdown(void) +{ + size_t pos; + git_filter_def *fdef; + + if (git_rwlock_wrlock(&filter_registry.lock) < 0) + return; + + git_vector_foreach(&filter_registry.filters, pos, fdef) { + if (fdef->filter && fdef->filter->shutdown) { + fdef->filter->shutdown(fdef->filter); + fdef->initialized = false; + } + + git__free(fdef->filter_name); + git__free(fdef->attrdata); + git__free(fdef); + } + + git_vector_free(&filter_registry.filters); + + git_rwlock_wrunlock(&filter_registry.lock); + git_rwlock_free(&filter_registry.lock); +} + +/* Note: callers must lock the registry before calling this function */ +static int filter_registry_find(size_t *pos, const char *name) +{ + return git_vector_search2( + pos, &filter_registry.filters, filter_def_name_key_check, name); +} + +/* Note: callers must lock the registry before calling this function */ +static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) +{ + git_filter_def *fdef = NULL; + + if (!filter_registry_find(pos, name)) + fdef = git_vector_get(&filter_registry.filters, *pos); + + return fdef; +} + + +int git_filter_register( + const char *name, git_filter *filter, int priority) +{ + int error; + + assert(name && filter); + + if (git_rwlock_wrlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + + if (!filter_registry_find(NULL, name)) { + giterr_set( + GITERR_FILTER, "attempt to reregister existing filter '%s'", name); + error = GIT_EEXISTS; + goto done; + } + + error = filter_registry_insert(name, filter, priority); + +done: + git_rwlock_wrunlock(&filter_registry.lock); + return error; +} + int git_filter_unregister(const char *name) { size_t pos; git_filter_def *fdef; + int error = 0; assert(name); @@ -289,12 +300,18 @@ int git_filter_unregister(const char *name) return -1; } + if (git_rwlock_wrlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); - return GIT_ENOTFOUND; + error = GIT_ENOTFOUND; + goto done; } - (void)git_vector_remove(&git__filter_registry->filters, pos); + git_vector_remove(&filter_registry.filters, pos); if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); @@ -305,21 +322,18 @@ int git_filter_unregister(const char *name) git__free(fdef->attrdata); git__free(fdef); - return 0; +done: + git_rwlock_wrunlock(&filter_registry.lock); + return error; } static int filter_initialize(git_filter_def *fdef) { int error = 0; - if (!fdef->initialized && - fdef->filter && - fdef->filter->initialize && - (error = fdef->filter->initialize(fdef->filter)) < 0) - { - /* auto-unregister if initialize fails */ - git_filter_unregister(fdef->filter_name); - return error; + if (!fdef->initialized && fdef->filter && fdef->filter->initialize) { + if ((error = fdef->filter->initialize(fdef->filter)) < 0) + return error; } fdef->initialized = true; @@ -330,17 +344,22 @@ git_filter *git_filter_lookup(const char *name) { size_t pos; git_filter_def *fdef; + git_filter *filter = NULL; - if (filter_registry_initialize() < 0) + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return NULL; + } - if ((fdef = filter_registry_lookup(&pos, name)) == NULL) - return NULL; + if ((fdef = filter_registry_lookup(&pos, name)) == NULL || + (!fdef->initialized && filter_initialize(fdef) < 0)) + goto done; - if (!fdef->initialized && filter_initialize(fdef) < 0) - return NULL; + filter = fdef->filter; - return fdef->filter; +done: + git_rwlock_rdunlock(&filter_registry.lock); + return filter; } void git_filter_free(git_filter *filter) @@ -478,8 +497,10 @@ int git_filter_list__load_ext( size_t idx; git_filter_def *fdef; - if (filter_registry_initialize() < 0) + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return -1; + } src.repo = repo; src.path = path; @@ -489,7 +510,7 @@ int git_filter_list__load_ext( if (blob) git_oid_cpy(&src.oid, git_blob_id(blob)); - git_vector_foreach(&git__filter_registry->filters, idx, fdef) { + git_vector_foreach(&filter_registry.filters, idx, fdef) { const char **values = NULL; void *payload = NULL; @@ -523,7 +544,7 @@ int git_filter_list__load_ext( else { if (!fl) { if ((error = filter_list_new(&fl, &src)) < 0) - return error; + break; fl->temp_buf = filter_opts->temp_buf; } @@ -537,6 +558,8 @@ int git_filter_list__load_ext( } } + git_rwlock_rdunlock(&filter_registry.lock); + if (error && fl != NULL) { git_array_clear(fl->filters); git__free(fl); @@ -604,20 +627,28 @@ int git_filter_list_push( { int error = 0; size_t pos; - git_filter_def *fdef; + git_filter_def *fdef = NULL; git_filter_entry *fe; assert(fl && filter); + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + if (git_vector_search2( - &pos, &git__filter_registry->filters, - filter_def_filter_key_check, filter) < 0) { + &pos, &filter_registry.filters, + filter_def_filter_key_check, filter) == 0) + fdef = git_vector_get(&filter_registry.filters, pos); + + git_rwlock_rdunlock(&filter_registry.lock); + + if (fdef == NULL) { giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); return -1; } - fdef = git_vector_get(&git__filter_registry->filters, pos); - if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) return error; diff --git a/src/filter.h b/src/filter.h index 5062afba5..9bd835f94 100644 --- a/src/filter.h +++ b/src/filter.h @@ -32,6 +32,8 @@ typedef struct { #define GIT_FILTER_OPTIONS_INIT {0} +extern int git_filter_global_init(void); + extern void git_filter_free(git_filter *filter); extern int git_filter_list__load_ext( diff --git a/src/global.c b/src/global.c index 0eab8d552..4a61a062c 100644 --- a/src/global.c +++ b/src/global.c @@ -8,9 +8,11 @@ #include "global.h" #include "hash.h" #include "sysdir.h" -#include "git2/global.h" -#include "git2/sys/openssl.h" +#include "filter.h" +#include "openssl_stream.h" #include "thread-utils.h" +#include "git2/global.h" + #if defined(GIT_MSVC_CRTDBG) #include "win32/w32_stack.h" #include "win32/w32_crtdbg_stacktrace.h" @@ -49,118 +51,48 @@ static void git__global_state_cleanup(git_global_st *st) st->error_t.message = NULL; } -static void git__shutdown(void) -{ - int pos; - - /* Shutdown subsystems that have registered */ - for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) { - git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL); - if (cb != NULL) - cb(); - } -} - -#if defined(GIT_THREADS) && defined(GIT_OPENSSL) -void openssl_locking_function(int mode, int n, const char *file, int line) +static int init_common(void) { - int lock; - - GIT_UNUSED(file); - GIT_UNUSED(line); - - lock = mode & CRYPTO_LOCK; - - if (lock) { - git_mutex_lock(&openssl_locks[n]); - } else { - git_mutex_unlock(&openssl_locks[n]); - } -} - -static void shutdown_ssl_locking(void) -{ - int num_locks, i; - - num_locks = CRYPTO_num_locks(); - CRYPTO_set_locking_callback(NULL); + int ret; - for (i = 0; i < num_locks; ++i) - git_mutex_free(openssl_locks); - git__free(openssl_locks); -} + /* Initialize the CRT debug allocator first, before our first malloc */ +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_init(); + git_win32__stack_init(); #endif -static void init_ssl(void) -{ -#ifdef GIT_OPENSSL - long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + /* Initialize any other subsystems that have global state */ + if ((ret = git_hash_global_init()) == 0 && + (ret = git_sysdir_global_init()) == 0 && + (ret = git_filter_global_init()) == 0) + ret = git_openssl_stream_global_init(); - /* Older OpenSSL and MacOS OpenSSL doesn't have this */ -#ifdef SSL_OP_NO_COMPRESSION - ssl_opts |= SSL_OP_NO_COMPRESSION; -#endif + GIT_MEMORY_BARRIER; - SSL_load_error_strings(); - OpenSSL_add_ssl_algorithms(); - /* - * Load SSLv{2,3} and TLSv1 so that we can talk with servers - * which use the SSL hellos, which are often used for - * compatibility. We then disable SSL so we only allow OpenSSL - * to speak TLSv1 to perform the encryption itself. - */ - git__ssl_ctx = SSL_CTX_new(SSLv23_method()); - SSL_CTX_set_options(git__ssl_ctx, ssl_opts); - SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); - if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; - } -#endif + return ret; } -/** - * This function aims to clean-up the SSL context which - * we allocated. - */ -static void uninit_ssl(void) +static void shutdown_common(void) { -#ifdef GIT_OPENSSL - if (git__ssl_ctx) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; - } -#endif -} + int pos; -int git_openssl_set_locking(void) -{ -#ifdef GIT_OPENSSL -# ifdef GIT_THREADS - int num_locks, i; + /* Shutdown subsystems that have registered */ + for (pos = git_atomic_get(&git__n_shutdown_callbacks); + pos > 0; + pos = git_atomic_dec(&git__n_shutdown_callbacks)) { - num_locks = CRYPTO_num_locks(); - openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); - GITERR_CHECK_ALLOC(openssl_locks); + git_global_shutdown_fn cb = git__swap( + git__shutdown_callbacks[pos - 1], NULL); - for (i = 0; i < num_locks; i++) { - if (git_mutex_init(&openssl_locks[i]) != 0) { - giterr_set(GITERR_SSL, "failed to initialize openssl locks"); - return -1; - } + if (cb != NULL) + cb(); } - CRYPTO_set_locking_callback(openssl_locking_function); - git__on_shutdown(shutdown_ssl_locking); - return 0; -# else - giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); - return -1; -# endif -#else - giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); - return -1; + git__free(git__user_agent); + +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_cleanup(); + git_win32__stack_cleanup(); #endif } @@ -208,14 +140,13 @@ static int synchronized_threads_init(void) int error; _tls_index = TlsAlloc(); + + win32_pthread_initialize(); + if (git_mutex_init(&git__mwindow_mutex)) return -1; - /* Initialize any other subsystems that have global state */ - if ((error = git_hash_global_init()) >= 0) - error = git_sysdir_global_init(); - - win32_pthread_initialize(); + error = init_common(); return error; } @@ -229,11 +160,6 @@ int git_libgit2_init(void) /* Only do work on a 0 -> 1 transition of the refcount */ if ((ret = git_atomic_inc(&git__n_inits)) == 1) { -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_init(); - git_win32__stack_init(); -#endif - if (synchronized_threads_init() < 0) ret = -1; } @@ -244,17 +170,6 @@ int git_libgit2_init(void) return ret; } -static void synchronized_threads_shutdown(void) -{ - /* Shut down any subsystems that have global state */ - git__shutdown(); - - git__free_tls_data(); - - TlsFree(_tls_index); - git_mutex_free(&git__mwindow_mutex); -} - int git_libgit2_shutdown(void) { int ret; @@ -264,14 +179,12 @@ int git_libgit2_shutdown(void) /* Only do work on a 1 -> 0 transition of the refcount */ if ((ret = git_atomic_dec(&git__n_inits)) == 0) { - synchronized_threads_shutdown(); + shutdown_common(); -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_cleanup(); - git_win32__stack_cleanup(); -#endif + git__free_tls_data(); - git__free(git__user_agent); + TlsFree(_tls_index); + git_mutex_free(&git__mwindow_mutex); } /* Exit the lock */ @@ -331,17 +244,10 @@ static void init_once(void) { if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) return; - pthread_key_create(&_tls_key, &cb__free_status); - - - /* Initialize any other subsystems that have global state */ - if ((init_error = git_hash_global_init()) >= 0) - init_error = git_sysdir_global_init(); - /* OpenSSL needs to be initialized from the main thread */ - init_ssl(); + pthread_key_create(&_tls_key, &cb__free_status); - GIT_MEMORY_BARRIER; + init_error = init_common(); } int git_libgit2_init(void) @@ -364,15 +270,13 @@ int git_libgit2_shutdown(void) return ret; /* Shut down any subsystems that have global state */ - git__shutdown(); - uninit_ssl(); + shutdown_common(); ptr = pthread_getspecific(_tls_key); pthread_setspecific(_tls_key, NULL); git__global_state_cleanup(ptr); git__free(ptr); - git__free(git__user_agent); pthread_key_delete(_tls_key); git_mutex_free(&git__mwindow_mutex); @@ -405,15 +309,16 @@ static git_global_st __state; int git_libgit2_init(void) { - static int ssl_inited = 0; + int ret; - if (!ssl_inited) { - init_ssl(); - ssl_inited = 1; - } + /* Only init SSL the first time */ + if ((ret = git_atomic_inc(&git__n_inits)) != 1) + return ret; + + if ((ret = init_common()) < 0) + return ret; - git_buf_init(&__state.error_buf, 0); - return git_atomic_inc(&git__n_inits); + return 1; } int git_libgit2_shutdown(void) @@ -421,15 +326,12 @@ int git_libgit2_shutdown(void) int ret; /* Shut down any subsystems that have global state */ - if ((ret = git_atomic_dec(&git__n_inits)) != 0) - return ret; - - git__shutdown(); - git__global_state_cleanup(&__state); - uninit_ssl(); - git__free(git__user_agent); + if ((ret = git_atomic_dec(&git__n_inits)) == 0) { + shutdown_common(); + git__global_state_cleanup(&__state); + } - return 0; + return ret; } git_global_st *git__global_state(void) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 54dd761ca..0705d90e7 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -31,6 +31,115 @@ #include <openssl/x509v3.h> #include <openssl/bio.h> +SSL_CTX *git__ssl_ctx; + +#ifdef GIT_THREADS + +static git_mutex *openssl_locks; + +static void openssl_locking_function( + int mode, int n, const char *file, int line) +{ + int lock; + + GIT_UNUSED(file); + GIT_UNUSED(line); + + lock = mode & CRYPTO_LOCK; + + if (lock) { + git_mutex_lock(&openssl_locks[n]); + } else { + git_mutex_unlock(&openssl_locks[n]); + } +} + +static void shutdown_ssl_locking(void) +{ + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < num_locks; ++i) + git_mutex_free(openssl_locks); + git__free(openssl_locks); +} + +#endif /* GIT_THREADS */ + +/** + * This function aims to clean-up the SSL context which + * we allocated. + */ +static void shutdown_ssl(void) +{ + if (git__ssl_ctx) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + } +} + +int git_openssl_stream_global_init(void) +{ +#ifdef GIT_OPENSSL + long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + /* Older OpenSSL and MacOS OpenSSL doesn't have this */ +#ifdef SSL_OP_NO_COMPRESSION + ssl_opts |= SSL_OP_NO_COMPRESSION; +#endif + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + /* + * Load SSLv{2,3} and TLSv1 so that we can talk with servers + * which use the SSL hellos, which are often used for + * compatibility. We then disable SSL so we only allow OpenSSL + * to speak TLSv1 to perform the encryption itself. + */ + git__ssl_ctx = SSL_CTX_new(SSLv23_method()); + SSL_CTX_set_options(git__ssl_ctx, ssl_opts); + SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); + if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + return -1; + } +#endif + + git__on_shutdown(shutdown_ssl); + + return 0; +} + +int git_openssl_set_locking(void) +{ +#ifdef GIT_THREADS + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); + GITERR_CHECK_ALLOC(openssl_locks); + + for (i = 0; i < num_locks; i++) { + if (git_mutex_init(&openssl_locks[i]) != 0) { + giterr_set(GITERR_SSL, "failed to initialize openssl locks"); + return -1; + } + } + + CRYPTO_set_locking_callback(openssl_locking_function); + git__on_shutdown(shutdown_ssl_locking); + return 0; +#else + giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); + return -1; +#endif +} + + static int bio_create(BIO *b) { b->init = 1; @@ -472,6 +581,17 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) #include "stream.h" +int git_openssl_stream_global_init(void) +{ + return 0; +} + +int git_openssl_set_locking(void) +{ + giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); + return -1; +} + int git_openssl_stream_new(git_stream **out, const char *host, const char *port) { GIT_UNUSED(out); diff --git a/src/openssl_stream.h b/src/openssl_stream.h index 9ca06489e..82b5110c4 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -9,6 +9,8 @@ #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); #endif diff --git a/src/thread-utils.h b/src/thread-utils.h index dd1136caf..14c8a41ff 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -275,7 +275,7 @@ GIT_INLINE(int) git_atomic_get(git_atomic *a) extern int git_online_cpus(void); -#if defined(GIT_THREADS) && defined(GIT_WIN32) +#if defined(GIT_THREADS) && defined(_MSC_VER) # define GIT_MEMORY_BARRIER MemoryBarrier() #elif defined(GIT_THREADS) # define GIT_MEMORY_BARRIER __sync_synchronize() |