Commit 78e16c34 by Carlos Martín Nieto

Merge pull request #3597 from ethomson/filter_registration

Filter registration
parents b643501d 82abd40d
...@@ -127,17 +127,6 @@ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *sr ...@@ -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); 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 * Initialize callback on filter
* *
...@@ -233,31 +222,51 @@ typedef void (*git_filter_cleanup_fn)( ...@@ -233,31 +222,51 @@ typedef void (*git_filter_cleanup_fn)(
* To associate extra data with a filter, allocate extra data and put the * 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 * `git_filter` struct at the start of your data buffer, then cast the
* `self` pointer to your larger structure when your callback is invoked. * `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 { struct git_filter {
/** The `version` field should be set to `GIT_FILTER_VERSION`. */
unsigned int 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; const char *attributes;
/** Called when the filter is first used for any file. */
git_filter_init_fn initialize; git_filter_init_fn initialize;
/** Called when the filter is removed or unregistered from the system. */
git_filter_shutdown_fn shutdown; 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; 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; 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; git_filter_stream_fn stream;
/** Called when the system is done filtering for a file. */
git_filter_cleanup_fn cleanup; git_filter_cleanup_fn cleanup;
}; };
......
...@@ -56,80 +56,15 @@ static int filter_def_priority_cmp(const void *a, const void *b) ...@@ -56,80 +56,15 @@ static int filter_def_priority_cmp(const void *a, const void *b)
return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
} }
struct filter_registry { struct git_filter_registry {
git_rwlock lock;
git_vector filters; git_vector filters;
}; };
static struct filter_registry *git__filter_registry = NULL; static struct git_filter_registry filter_registry;
static void filter_registry_shutdown(void) static void git_filter_global_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;
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( static int filter_def_scan_attrs(
git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) 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) ...@@ -210,40 +145,14 @@ static int filter_def_filter_key_check(const void *key, const void *fdef)
return (key == filter) ? 0 : -1; return (key == filter) ? 0 : -1;
} }
static int filter_registry_find(size_t *pos, const char *name) /* Note: callers must lock the registry before calling this function */
{ static int filter_registry_insert(
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(
const char *name, git_filter *filter, int priority) const char *name, git_filter *filter, int priority)
{ {
git_filter_def *fdef; git_filter_def *fdef;
size_t nattr = 0, nmatch = 0, alloc_len; size_t nattr = 0, nmatch = 0, alloc_len;
git_buf attrs = GIT_BUF_INIT; 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) if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
return -1; return -1;
...@@ -265,21 +174,123 @@ int git_filter_register( ...@@ -265,21 +174,123 @@ int git_filter_register(
filter_def_set_attrs(fdef); 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->filter_name);
git__free(fdef->attrdata); git__free(fdef->attrdata);
git__free(fdef); git__free(fdef);
return -1; return -1;
} }
git_vector_sort(&git__filter_registry->filters); git_vector_sort(&filter_registry.filters);
return 0; 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) int git_filter_unregister(const char *name)
{ {
size_t pos; size_t pos;
git_filter_def *fdef; git_filter_def *fdef;
int error = 0;
assert(name); assert(name);
...@@ -289,12 +300,18 @@ int git_filter_unregister(const char *name) ...@@ -289,12 +300,18 @@ int git_filter_unregister(const char *name)
return -1; 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) { 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);
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) { if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
fdef->filter->shutdown(fdef->filter); fdef->filter->shutdown(fdef->filter);
...@@ -305,21 +322,18 @@ int git_filter_unregister(const char *name) ...@@ -305,21 +322,18 @@ int git_filter_unregister(const char *name)
git__free(fdef->attrdata); git__free(fdef->attrdata);
git__free(fdef); git__free(fdef);
return 0; done:
git_rwlock_wrunlock(&filter_registry.lock);
return error;
} }
static int filter_initialize(git_filter_def *fdef) static int filter_initialize(git_filter_def *fdef)
{ {
int error = 0; int error = 0;
if (!fdef->initialized && if (!fdef->initialized && fdef->filter && fdef->filter->initialize) {
fdef->filter && if ((error = fdef->filter->initialize(fdef->filter)) < 0)
fdef->filter->initialize && return error;
(error = fdef->filter->initialize(fdef->filter)) < 0)
{
/* auto-unregister if initialize fails */
git_filter_unregister(fdef->filter_name);
return error;
} }
fdef->initialized = true; fdef->initialized = true;
...@@ -330,17 +344,22 @@ git_filter *git_filter_lookup(const char *name) ...@@ -330,17 +344,22 @@ git_filter *git_filter_lookup(const char *name)
{ {
size_t pos; size_t pos;
git_filter_def *fdef; 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; return NULL;
}
if ((fdef = filter_registry_lookup(&pos, name)) == NULL) if ((fdef = filter_registry_lookup(&pos, name)) == NULL ||
return NULL; (!fdef->initialized && filter_initialize(fdef) < 0))
goto done;
if (!fdef->initialized && filter_initialize(fdef) < 0) filter = fdef->filter;
return NULL;
return fdef->filter; done:
git_rwlock_rdunlock(&filter_registry.lock);
return filter;
} }
void git_filter_free(git_filter *filter) void git_filter_free(git_filter *filter)
...@@ -478,8 +497,10 @@ int git_filter_list__load_ext( ...@@ -478,8 +497,10 @@ int git_filter_list__load_ext(
size_t idx; size_t idx;
git_filter_def *fdef; 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; return -1;
}
src.repo = repo; src.repo = repo;
src.path = path; src.path = path;
...@@ -489,7 +510,7 @@ int git_filter_list__load_ext( ...@@ -489,7 +510,7 @@ int git_filter_list__load_ext(
if (blob) if (blob)
git_oid_cpy(&src.oid, git_blob_id(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; const char **values = NULL;
void *payload = NULL; void *payload = NULL;
...@@ -523,7 +544,7 @@ int git_filter_list__load_ext( ...@@ -523,7 +544,7 @@ int git_filter_list__load_ext(
else { else {
if (!fl) { if (!fl) {
if ((error = filter_list_new(&fl, &src)) < 0) if ((error = filter_list_new(&fl, &src)) < 0)
return error; break;
fl->temp_buf = filter_opts->temp_buf; fl->temp_buf = filter_opts->temp_buf;
} }
...@@ -537,6 +558,8 @@ int git_filter_list__load_ext( ...@@ -537,6 +558,8 @@ int git_filter_list__load_ext(
} }
} }
git_rwlock_rdunlock(&filter_registry.lock);
if (error && fl != NULL) { if (error && fl != NULL) {
git_array_clear(fl->filters); git_array_clear(fl->filters);
git__free(fl); git__free(fl);
...@@ -604,20 +627,28 @@ int git_filter_list_push( ...@@ -604,20 +627,28 @@ int git_filter_list_push(
{ {
int error = 0; int error = 0;
size_t pos; size_t pos;
git_filter_def *fdef; git_filter_def *fdef = NULL;
git_filter_entry *fe; git_filter_entry *fe;
assert(fl && filter); 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( if (git_vector_search2(
&pos, &git__filter_registry->filters, &pos, &filter_registry.filters,
filter_def_filter_key_check, filter) < 0) { 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"); giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
return -1; return -1;
} }
fdef = git_vector_get(&git__filter_registry->filters, pos);
if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
return error; return error;
......
...@@ -32,6 +32,8 @@ typedef struct { ...@@ -32,6 +32,8 @@ typedef struct {
#define GIT_FILTER_OPTIONS_INIT {0} #define GIT_FILTER_OPTIONS_INIT {0}
extern int git_filter_global_init(void);
extern void git_filter_free(git_filter *filter); extern void git_filter_free(git_filter *filter);
extern int git_filter_list__load_ext( extern int git_filter_list__load_ext(
......
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#include "global.h" #include "global.h"
#include "hash.h" #include "hash.h"
#include "sysdir.h" #include "sysdir.h"
#include "git2/global.h" #include "filter.h"
#include "git2/sys/openssl.h" #include "openssl_stream.h"
#include "thread-utils.h" #include "thread-utils.h"
#include "git2/global.h"
#if defined(GIT_MSVC_CRTDBG) #if defined(GIT_MSVC_CRTDBG)
#include "win32/w32_stack.h" #include "win32/w32_stack.h"
#include "win32/w32_crtdbg_stacktrace.h" #include "win32/w32_crtdbg_stacktrace.h"
...@@ -49,118 +51,48 @@ static void git__global_state_cleanup(git_global_st *st) ...@@ -49,118 +51,48 @@ static void git__global_state_cleanup(git_global_st *st)
st->error_t.message = NULL; st->error_t.message = NULL;
} }
static void git__shutdown(void) static int init_common(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)
{ {
int lock; int ret;
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) /* Initialize the CRT debug allocator first, before our first malloc */
git_mutex_free(openssl_locks); #if defined(GIT_MSVC_CRTDBG)
git__free(openssl_locks); git_win32__crtdbg_stacktrace_init();
} git_win32__stack_init();
#endif #endif
static void init_ssl(void) /* Initialize any other subsystems that have global state */
{ if ((ret = git_hash_global_init()) == 0 &&
#ifdef GIT_OPENSSL (ret = git_sysdir_global_init()) == 0 &&
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; (ret = git_filter_global_init()) == 0)
ret = git_openssl_stream_global_init();
/* Older OpenSSL and MacOS OpenSSL doesn't have this */ GIT_MEMORY_BARRIER;
#ifdef SSL_OP_NO_COMPRESSION
ssl_opts |= SSL_OP_NO_COMPRESSION;
#endif
SSL_load_error_strings(); return ret;
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
} }
/** static void shutdown_common(void)
* This function aims to clean-up the SSL context which
* we allocated.
*/
static void uninit_ssl(void)
{ {
#ifdef GIT_OPENSSL int pos;
if (git__ssl_ctx) {
SSL_CTX_free(git__ssl_ctx);
git__ssl_ctx = NULL;
}
#endif
}
int git_openssl_set_locking(void) /* Shutdown subsystems that have registered */
{ for (pos = git_atomic_get(&git__n_shutdown_callbacks);
#ifdef GIT_OPENSSL pos > 0;
# ifdef GIT_THREADS pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
int num_locks, i;
num_locks = CRYPTO_num_locks(); git_global_shutdown_fn cb = git__swap(
openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); git__shutdown_callbacks[pos - 1], NULL);
GITERR_CHECK_ALLOC(openssl_locks);
for (i = 0; i < num_locks; i++) { if (cb != NULL)
if (git_mutex_init(&openssl_locks[i]) != 0) { cb();
giterr_set(GITERR_SSL, "failed to initialize openssl locks");
return -1;
}
} }
CRYPTO_set_locking_callback(openssl_locking_function); git__free(git__user_agent);
git__on_shutdown(shutdown_ssl_locking);
return 0; #if defined(GIT_MSVC_CRTDBG)
# else git_win32__crtdbg_stacktrace_cleanup();
giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); git_win32__stack_cleanup();
return -1;
# endif
#else
giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
return -1;
#endif #endif
} }
...@@ -208,14 +140,13 @@ static int synchronized_threads_init(void) ...@@ -208,14 +140,13 @@ static int synchronized_threads_init(void)
int error; int error;
_tls_index = TlsAlloc(); _tls_index = TlsAlloc();
win32_pthread_initialize();
if (git_mutex_init(&git__mwindow_mutex)) if (git_mutex_init(&git__mwindow_mutex))
return -1; return -1;
/* Initialize any other subsystems that have global state */ error = init_common();
if ((error = git_hash_global_init()) >= 0)
error = git_sysdir_global_init();
win32_pthread_initialize();
return error; return error;
} }
...@@ -229,11 +160,6 @@ int git_libgit2_init(void) ...@@ -229,11 +160,6 @@ int git_libgit2_init(void)
/* Only do work on a 0 -> 1 transition of the refcount */ /* Only do work on a 0 -> 1 transition of the refcount */
if ((ret = git_atomic_inc(&git__n_inits)) == 1) { 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) if (synchronized_threads_init() < 0)
ret = -1; ret = -1;
} }
...@@ -244,17 +170,6 @@ int git_libgit2_init(void) ...@@ -244,17 +170,6 @@ int git_libgit2_init(void)
return ret; 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 git_libgit2_shutdown(void)
{ {
int ret; int ret;
...@@ -264,14 +179,12 @@ int git_libgit2_shutdown(void) ...@@ -264,14 +179,12 @@ int git_libgit2_shutdown(void)
/* Only do work on a 1 -> 0 transition of the refcount */ /* Only do work on a 1 -> 0 transition of the refcount */
if ((ret = git_atomic_dec(&git__n_inits)) == 0) { if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
synchronized_threads_shutdown(); shutdown_common();
#if defined(GIT_MSVC_CRTDBG) git__free_tls_data();
git_win32__crtdbg_stacktrace_cleanup();
git_win32__stack_cleanup();
#endif
git__free(git__user_agent); TlsFree(_tls_index);
git_mutex_free(&git__mwindow_mutex);
} }
/* Exit the lock */ /* Exit the lock */
...@@ -331,17 +244,10 @@ static void init_once(void) ...@@ -331,17 +244,10 @@ static void init_once(void)
{ {
if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
return; 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 */ pthread_key_create(&_tls_key, &cb__free_status);
init_ssl();
GIT_MEMORY_BARRIER; init_error = init_common();
} }
int git_libgit2_init(void) int git_libgit2_init(void)
...@@ -364,15 +270,13 @@ int git_libgit2_shutdown(void) ...@@ -364,15 +270,13 @@ int git_libgit2_shutdown(void)
return ret; return ret;
/* Shut down any subsystems that have global state */ /* Shut down any subsystems that have global state */
git__shutdown(); shutdown_common();
uninit_ssl();
ptr = pthread_getspecific(_tls_key); ptr = pthread_getspecific(_tls_key);
pthread_setspecific(_tls_key, NULL); pthread_setspecific(_tls_key, NULL);
git__global_state_cleanup(ptr); git__global_state_cleanup(ptr);
git__free(ptr); git__free(ptr);
git__free(git__user_agent);
pthread_key_delete(_tls_key); pthread_key_delete(_tls_key);
git_mutex_free(&git__mwindow_mutex); git_mutex_free(&git__mwindow_mutex);
...@@ -405,15 +309,16 @@ static git_global_st __state; ...@@ -405,15 +309,16 @@ static git_global_st __state;
int git_libgit2_init(void) int git_libgit2_init(void)
{ {
static int ssl_inited = 0; int ret;
if (!ssl_inited) { /* Only init SSL the first time */
init_ssl(); if ((ret = git_atomic_inc(&git__n_inits)) != 1)
ssl_inited = 1; return ret;
}
if ((ret = init_common()) < 0)
return ret;
git_buf_init(&__state.error_buf, 0); return 1;
return git_atomic_inc(&git__n_inits);
} }
int git_libgit2_shutdown(void) int git_libgit2_shutdown(void)
...@@ -421,15 +326,12 @@ int git_libgit2_shutdown(void) ...@@ -421,15 +326,12 @@ int git_libgit2_shutdown(void)
int ret; int ret;
/* Shut down any subsystems that have global state */ /* Shut down any subsystems that have global state */
if ((ret = git_atomic_dec(&git__n_inits)) != 0) if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
return ret; shutdown_common();
git__global_state_cleanup(&__state);
git__shutdown(); }
git__global_state_cleanup(&__state);
uninit_ssl();
git__free(git__user_agent);
return 0; return ret;
} }
git_global_st *git__global_state(void) git_global_st *git__global_state(void)
......
...@@ -31,6 +31,115 @@ ...@@ -31,6 +31,115 @@
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
#include <openssl/bio.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) static int bio_create(BIO *b)
{ {
b->init = 1; b->init = 1;
...@@ -472,6 +581,17 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) ...@@ -472,6 +581,17 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
#include "stream.h" #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) int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
{ {
GIT_UNUSED(out); GIT_UNUSED(out);
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "git2/sys/stream.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); extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
#endif #endif
...@@ -275,7 +275,7 @@ GIT_INLINE(int) git_atomic_get(git_atomic *a) ...@@ -275,7 +275,7 @@ GIT_INLINE(int) git_atomic_get(git_atomic *a)
extern int git_online_cpus(void); 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() # define GIT_MEMORY_BARRIER MemoryBarrier()
#elif defined(GIT_THREADS) #elif defined(GIT_THREADS)
# define GIT_MEMORY_BARRIER __sync_synchronize() # define GIT_MEMORY_BARRIER __sync_synchronize()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment