summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <carlosmn@github.com>2016-02-19 13:06:51 +0100
committerCarlos Martín Nieto <carlosmn@github.com>2016-02-19 13:06:51 +0100
commit78e16c3442b57f967c31e65cfb4792dcf184c6e8 (patch)
tree36ea735e4c45ea360fae6e9aa944f2c02d48a256
parentb643501dbab3ba6a3c8343349986bc541e9197b8 (diff)
parent82abd40d80a8912043dd072a09962f06df193e36 (diff)
downloadlibgit2-78e16c3442b57f967c31e65cfb4792dcf184c6e8.tar.gz
Merge pull request #3597 from ethomson/filter_registration
Filter registration
-rw-r--r--include/git2/sys/filter.h59
-rw-r--r--src/filter.c279
-rw-r--r--src/filter.h2
-rw-r--r--src/global.c210
-rw-r--r--src/openssl_stream.c120
-rw-r--r--src/openssl_stream.h2
-rw-r--r--src/thread-utils.h2
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(&reg->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(&reg->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(
- &reg->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(&reg->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()