diff options
Diffstat (limited to 'subversion/libsvn_subr')
-rw-r--r-- | subversion/libsvn_subr/cache-membuffer.c | 57 | ||||
-rw-r--r-- | subversion/libsvn_subr/config.c | 5 | ||||
-rw-r--r-- | subversion/libsvn_subr/dso.c | 23 | ||||
-rw-r--r-- | subversion/libsvn_subr/error.c | 4 | ||||
-rw-r--r-- | subversion/libsvn_subr/gpg_agent.c | 302 | ||||
-rw-r--r-- | subversion/libsvn_subr/internal_statements.h | 2 | ||||
-rw-r--r-- | subversion/libsvn_subr/io.c | 19 | ||||
-rw-r--r-- | subversion/libsvn_subr/mergeinfo.c | 67 | ||||
-rw-r--r-- | subversion/libsvn_subr/sqlite3wrapper.c | 2 | ||||
-rw-r--r-- | subversion/libsvn_subr/string.c | 4 | ||||
-rw-r--r-- | subversion/libsvn_subr/version.c | 2 |
11 files changed, 388 insertions, 99 deletions
diff --git a/subversion/libsvn_subr/cache-membuffer.c b/subversion/libsvn_subr/cache-membuffer.c index 131d914..7aa90cd 100644 --- a/subversion/libsvn_subr/cache-membuffer.c +++ b/subversion/libsvn_subr/cache-membuffer.c @@ -101,6 +101,21 @@ * on their hash key. */ +/* APR's read-write lock implementation on Windows is horribly inefficient. + * Even with very low contention a runtime overhead of 35% percent has been + * measured for 'svn-bench null-export' over ra_serf. + * + * Use a simple mutex on Windows. Because there is one mutex per segment, + * large machines should (and usually can) be configured with large caches + * such that read contention is kept low. This is basically the situation + * we head before 1.8. + */ +#ifdef WIN32 +# define USE_SIMPLE_MUTEX 1 +#else +# define USE_SIMPLE_MUTEX 0 +#endif + /* A 16-way associative cache seems to be a good compromise between * performance (worst-case lookups) and efficiency-loss due to collisions. * @@ -465,11 +480,15 @@ struct svn_membuffer_t * the cache's creator doesn't feel the cache needs to be * thread-safe. */ +# if USE_SIMPLE_MUTEX + svn_mutex__t *lock; +# else apr_thread_rwlock_t *lock; +# endif /* If set, write access will wait until they get exclusive access. * Otherwise, they will become no-ops if the segment is currently - * read-locked. + * read-locked. Only used when LOCK is an r/w lock. */ svn_boolean_t allow_blocking_writes; #endif @@ -489,12 +508,16 @@ static svn_error_t * read_lock_cache(svn_membuffer_t *cache) { #if APR_HAS_THREADS +# if USE_SIMPLE_MUTEX + return svn_mutex__lock(cache->lock); +# else if (cache->lock) { apr_status_t status = apr_thread_rwlock_rdlock(cache->lock); if (status) return svn_error_wrap_apr(status, _("Can't lock cache mutex")); } +# endif #endif return SVN_NO_ERROR; } @@ -505,6 +528,12 @@ static svn_error_t * write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success) { #if APR_HAS_THREADS +# if USE_SIMPLE_MUTEX + + return svn_mutex__lock(cache->lock); + +# else + if (cache->lock) { apr_status_t status; @@ -526,6 +555,8 @@ write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success) return svn_error_wrap_apr(status, _("Can't write-lock cache mutex")); } + +# endif #endif return SVN_NO_ERROR; } @@ -537,10 +568,18 @@ static svn_error_t * force_write_lock_cache(svn_membuffer_t *cache) { #if APR_HAS_THREADS +# if USE_SIMPLE_MUTEX + + return svn_mutex__lock(cache->lock); + +# else + apr_status_t status = apr_thread_rwlock_wrlock(cache->lock); if (status) return svn_error_wrap_apr(status, _("Can't write-lock cache mutex")); + +# endif #endif return SVN_NO_ERROR; } @@ -552,6 +591,12 @@ static svn_error_t * unlock_cache(svn_membuffer_t *cache, svn_error_t *err) { #if APR_HAS_THREADS +# if USE_SIMPLE_MUTEX + + return svn_mutex__unlock(cache->lock, err); + +# else + if (cache->lock) { apr_status_t status = apr_thread_rwlock_unlock(cache->lock); @@ -561,6 +606,8 @@ unlock_cache(svn_membuffer_t *cache, svn_error_t *err) if (status) return svn_error_wrap_apr(status, _("Can't unlock cache mutex")); } + +# endif #endif return err; } @@ -1290,6 +1337,12 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, * the cache's creator doesn't feel the cache needs to be * thread-safe. */ +# if USE_SIMPLE_MUTEX + + SVN_ERR(svn_mutex__init(&c[seg].lock, thread_safe, pool)); + +# else + c[seg].lock = NULL; if (thread_safe) { @@ -1299,6 +1352,8 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, return svn_error_wrap_apr(status, _("Can't create cache mutex")); } +# endif + /* Select the behavior of write operations. */ c[seg].allow_blocking_writes = allow_blocking_writes; diff --git a/subversion/libsvn_subr/config.c b/subversion/libsvn_subr/config.c index 94aecd3..cf97f0d 100644 --- a/subversion/libsvn_subr/config.c +++ b/subversion/libsvn_subr/config.c @@ -487,14 +487,15 @@ make_string_from_option(const char **valuep, svn_config_t *cfg, expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool); opt->expanded = TRUE; - if (!x_pool) + if (x_pool != cfg->x_pool) { /* Grab the fully expanded value from tmp_pool before its disappearing act. */ if (opt->x_value) opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value, strlen(opt->x_value)); - svn_pool_destroy(tmp_pool); + if (!x_pool) + svn_pool_destroy(tmp_pool); } } else diff --git a/subversion/libsvn_subr/dso.c b/subversion/libsvn_subr/dso.c index 3fa2517..7cce2fd 100644 --- a/subversion/libsvn_subr/dso.c +++ b/subversion/libsvn_subr/dso.c @@ -28,6 +28,7 @@ #include "svn_private_config.h" #include "private/svn_mutex.h" +#include "private/svn_atomic.h" /* A mutex to protect our global pool and cache. */ static svn_mutex__t *dso_mutex = NULL; @@ -41,18 +42,18 @@ static apr_hash_t *dso_cache; /* Just an arbitrary location in memory... */ static int not_there_sentinel; +static volatile svn_atomic_t atomic_init_status = 0; + /* A specific value we store in the dso_cache to indicate that the library wasn't found. This keeps us from allocating extra memory from dso_pool when trying to find libraries we already know aren't there. */ #define NOT_THERE ((void *) ¬_there_sentinel) -svn_error_t * -svn_dso_initialize2(void) +static svn_error_t * +atomic_init_func(void *baton, + apr_pool_t *pool) { - if (dso_pool) - return SVN_NO_ERROR; - dso_pool = svn_pool_create(NULL); SVN_ERR(svn_mutex__init(&dso_mutex, TRUE, dso_pool)); @@ -61,6 +62,15 @@ svn_dso_initialize2(void) return SVN_NO_ERROR; } +svn_error_t * +svn_dso_initialize2(void) +{ + SVN_ERR(svn_atomic__init_once(&atomic_init_status, atomic_init_func, + NULL, NULL)); + + return SVN_NO_ERROR; +} + #if APR_HAS_DSO static svn_error_t * svn_dso_load_internal(apr_dso_handle_t **dso, const char *fname) @@ -107,8 +117,7 @@ svn_dso_load_internal(apr_dso_handle_t **dso, const char *fname) svn_error_t * svn_dso_load(apr_dso_handle_t **dso, const char *fname) { - if (! dso_pool) - SVN_ERR(svn_dso_initialize2()); + SVN_ERR(svn_dso_initialize2()); SVN_MUTEX__WITH_LOCK(dso_mutex, svn_dso_load_internal(dso, fname)); diff --git a/subversion/libsvn_subr/error.c b/subversion/libsvn_subr/error.c index 2f04320..48ac906 100644 --- a/subversion/libsvn_subr/error.c +++ b/subversion/libsvn_subr/error.c @@ -289,6 +289,8 @@ svn_error_compose(svn_error_t *chain, svn_error_t *new_err) *chain = *new_err; if (chain->message) chain->message = apr_pstrdup(pool, new_err->message); + if (chain->file) + chain->file = apr_pstrdup(pool, new_err->file); chain->pool = pool; #if defined(SVN_DEBUG) if (! new_err->child) @@ -358,6 +360,8 @@ svn_error_dup(svn_error_t *err) tmp_err->pool = pool; if (tmp_err->message) tmp_err->message = apr_pstrdup(pool, tmp_err->message); + if (tmp_err->file) + tmp_err->file = apr_pstrdup(pool, tmp_err->file); } #if defined(SVN_DEBUG) diff --git a/subversion/libsvn_subr/gpg_agent.c b/subversion/libsvn_subr/gpg_agent.c index e233390..4dbf118 100644 --- a/subversion/libsvn_subr/gpg_agent.c +++ b/subversion/libsvn_subr/gpg_agent.c @@ -72,6 +72,9 @@ #include "svn_cmdline.h" #include "svn_checksum.h" #include "svn_string.h" +#include "svn_hash.h" +#include "svn_user.h" +#include "svn_dirent_uri.h" #include "private/svn_auth_private.h" @@ -80,6 +83,7 @@ #ifdef SVN_HAVE_GPG_AGENT #define BUFFER_SIZE 1024 +#define ATTEMPT_PARAMETER "svn.simple.gpg_agent.attempt" /* Modify STR in-place such that blanks are escaped as required by the * gpg-agent protocol. Return a pointer to STR. */ @@ -98,6 +102,24 @@ escape_blanks(char *str) return str; } +/* Generate the string CACHE_ID_P based on the REALMSTRING allocated in + * RESULT_POOL using SCRATCH_POOL for temporary allocations. This is similar + * to other password caching mechanisms. */ +static svn_error_t * +get_cache_id(const char **cache_id_p, const char *realmstring, + apr_pool_t *scratch_pool, apr_pool_t *result_pool) +{ + const char *cache_id = NULL; + svn_checksum_t *digest = NULL; + + SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring, + strlen(realmstring), scratch_pool)); + cache_id = svn_checksum_to_cstring(digest, result_pool); + *cache_id_p = cache_id; + + return SVN_NO_ERROR; +} + /* Attempt to read a gpg-agent response message from the socket SD into * buffer BUF. Buf is assumed to be N bytes large. Return TRUE if a response * message could be read that fits into the buffer. Else return FALSE. @@ -156,6 +178,17 @@ send_option(int sd, char *buf, size_t n, const char *option, const char *value, return (strncmp(buf, "OK", 2) == 0); } +/* Send the BYE command and disconnect from the gpg-agent. Doing this avoids + * gpg-agent emitting a "Connection reset by peer" log message with some + * versions of gpg-agent. */ +static void +bye_gpg_agent(int sd) +{ + /* don't bother to check the result of the write, it either worked or it + * didn't, but either way we're closing. */ + write(sd, "BYE\n", 4); + close(sd); +} /* Locate a running GPG Agent, and return an open file descriptor * for communication with the agent in *NEW_SD. If no running agent @@ -173,17 +206,34 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) *new_sd = -1; + /* This implements the method of finding the socket as described in + * the gpg-agent man page under the --use-standard-socket option. + * The manage page misleadingly says the standard socket is + * "named 'S.gpg-agent' located in the home directory." The standard + * socket path is actually in the .gnupg directory in the home directory, + * i.e. ~/.gnupg/S.gpg-agent */ gpg_agent_info = getenv("GPG_AGENT_INFO"); if (gpg_agent_info != NULL) { apr_array_header_t *socket_details; + /* For reference GPG_AGENT_INFO consists of 3 : separated fields. + * The path to the socket, the pid of the gpg-agent process and + * finally the version of the protocol the agent talks. */ socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE, pool); socket_name = APR_ARRAY_IDX(socket_details, 0, const char *); } else - return SVN_NO_ERROR; + { + const char *homedir = svn_user_get_homedir(pool); + + if (!homedir) + return SVN_NO_ERROR; + + socket_name = svn_dirent_join_many(pool, homedir, ".gnupg", + "S.gpg-agent", NULL); + } if (socket_name != NULL) { @@ -210,13 +260,13 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) buffer = apr_palloc(pool, BUFFER_SIZE); if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE)) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } if (strncmp(buffer, "OK", 2) != 0) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } @@ -226,19 +276,19 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) request = "GETINFO socket_name\n"; if (write(sd, request, strlen(request)) == -1) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE)) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } if (strncmp(buffer, "D", 1) == 0) p = &buffer[2]; if (!p) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } ep = strchr(p, '\n'); @@ -246,18 +296,18 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) *ep = '\0'; if (strcmp(socket_name, p) != 0) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } /* The agent will terminate its response with "OK". */ if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE)) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } if (strncmp(buffer, "OK", 2) != 0) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } @@ -265,60 +315,28 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) return SVN_NO_ERROR; } -/* Implementation of svn_auth__password_get_t that retrieves the password - from gpg-agent */ -static svn_error_t * -password_get_gpg_agent(svn_boolean_t *done, - const char **password, - apr_hash_t *creds, - const char *realmstring, - const char *username, - apr_hash_t *parameters, - svn_boolean_t non_interactive, - apr_pool_t *pool) +static svn_boolean_t +send_options(int sd, char *buf, size_t n, apr_pool_t *scratch_pool) { - int sd; - const char *p = NULL; - char *ep = NULL; - char *buffer; - const char *request = NULL; - const char *cache_id = NULL; const char *tty_name; const char *tty_type; const char *lc_ctype; const char *display; - svn_checksum_t *digest = NULL; - char *password_prompt; - char *realm_prompt; - - *done = FALSE; - - SVN_ERR(find_running_gpg_agent(&sd, pool)); - if (sd == -1) - return SVN_NO_ERROR; - - buffer = apr_palloc(pool, BUFFER_SIZE); /* Send TTY_NAME to the gpg-agent daemon. */ tty_name = getenv("GPG_TTY"); if (tty_name != NULL) { - if (!send_option(sd, buffer, BUFFER_SIZE, "ttyname", tty_name, pool)) - { - close(sd); - return SVN_NO_ERROR; - } + if (!send_option(sd, buf, n, "ttyname", tty_name, scratch_pool)) + return FALSE; } /* Send TTY_TYPE to the gpg-agent daemon. */ tty_type = getenv("TERM"); if (tty_type != NULL) { - if (!send_option(sd, buffer, BUFFER_SIZE, "ttytype", tty_type, pool)) - { - close(sd); - return SVN_NO_ERROR; - } + if (!send_option(sd, buf, n, "ttytype", tty_type, scratch_pool)) + return FALSE; } /* Compute LC_CTYPE. */ @@ -331,53 +349,92 @@ password_get_gpg_agent(svn_boolean_t *done, /* Send LC_CTYPE to the gpg-agent daemon. */ if (lc_ctype != NULL) { - if (!send_option(sd, buffer, BUFFER_SIZE, "lc-ctype", lc_ctype, pool)) - { - close(sd); - return SVN_NO_ERROR; - } + if (!send_option(sd, buf, n, "lc-ctype", lc_ctype, scratch_pool)) + return FALSE; } /* Send DISPLAY to the gpg-agent daemon. */ display = getenv("DISPLAY"); if (display != NULL) { - if (!send_option(sd, buffer, BUFFER_SIZE, "display", display, pool)) - { - close(sd); - return SVN_NO_ERROR; - } + if (!send_option(sd, buf, n, "display", display, scratch_pool)) + return FALSE; } - /* Create the CACHE_ID which will be generated based on REALMSTRING similar - to other password caching mechanisms. */ - SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring, - strlen(realmstring), pool)); - cache_id = svn_checksum_to_cstring(digest, pool); + return TRUE; +} + +/* Implementation of svn_auth__password_get_t that retrieves the password + from gpg-agent */ +static svn_error_t * +password_get_gpg_agent(svn_boolean_t *done, + const char **password, + apr_hash_t *creds, + const char *realmstring, + const char *username, + apr_hash_t *parameters, + svn_boolean_t non_interactive, + apr_pool_t *pool) +{ + int sd; + const char *p = NULL; + char *ep = NULL; + char *buffer; + const char *request = NULL; + const char *cache_id = NULL; + char *password_prompt; + char *realm_prompt; + char *error_prompt; + int *attempt; + + *done = FALSE; + + attempt = svn_hash_gets(parameters, ATTEMPT_PARAMETER); + + SVN_ERR(find_running_gpg_agent(&sd, pool)); + if (sd == -1) + return SVN_NO_ERROR; + + buffer = apr_palloc(pool, BUFFER_SIZE); + + if (!send_options(sd, buffer, BUFFER_SIZE, pool)) + { + bye_gpg_agent(sd); + return SVN_NO_ERROR; + } + + SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool)); password_prompt = apr_psprintf(pool, _("Password for '%s': "), username); realm_prompt = apr_psprintf(pool, _("Enter your Subversion password for %s"), realmstring); + if (*attempt == 1) + /* X means no error to the gpg-agent protocol */ + error_prompt = apr_pstrdup(pool, "X"); + else + error_prompt = apr_pstrdup(pool, _("Authentication failed")); + request = apr_psprintf(pool, - "GET_PASSPHRASE --data %s--repeat=1 " - "%s X %s %s\n", + "GET_PASSPHRASE --data %s" + "%s %s %s %s\n", non_interactive ? "--no-ask " : "", cache_id, + escape_blanks(error_prompt), escape_blanks(password_prompt), escape_blanks(realm_prompt)); if (write(sd, request, strlen(request)) == -1) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE)) { - close(sd); + bye_gpg_agent(sd); return SVN_NO_ERROR; } - close(sd); + bye_gpg_agent(sd); if (strncmp(buffer, "ERR", 3) == 0) return SVN_NO_ERROR; @@ -424,7 +481,7 @@ password_set_gpg_agent(svn_boolean_t *done, if (sd == -1) return SVN_NO_ERROR; - close(sd); + bye_gpg_agent(sd); *done = TRUE; return SVN_NO_ERROR; @@ -440,11 +497,108 @@ simple_gpg_agent_first_creds(void **credentials, const char *realmstring, apr_pool_t *pool) { - return svn_auth__simple_creds_cache_get(credentials, iter_baton, - provider_baton, parameters, - realmstring, password_get_gpg_agent, - SVN_AUTH__GPG_AGENT_PASSWORD_TYPE, - pool); + svn_error_t *err; + int *attempt = apr_palloc(pool, sizeof(*attempt)); + + *attempt = 1; + svn_hash_sets(parameters, ATTEMPT_PARAMETER, attempt); + err = svn_auth__simple_creds_cache_get(credentials, iter_baton, + provider_baton, parameters, + realmstring, password_get_gpg_agent, + SVN_AUTH__GPG_AGENT_PASSWORD_TYPE, + pool); + *iter_baton = attempt; + + return err; +} + +/* An implementation of svn_auth_provider_t::next_credentials() */ +static svn_error_t * +simple_gpg_agent_next_creds(void **credentials, + void *iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + int *attempt = (int *)iter_baton; + int sd; + char *buffer; + const char *cache_id = NULL; + const char *request = NULL; + + *credentials = NULL; + + /* The users previous credentials failed so first remove the cached entry, + * before trying to retrieve them again. Because gpg-agent stores cached + * credentials immediately upon retrieving them, this gives us the + * opportunity to remove the invalid credentials and prompt the + * user again. While it's possible that server side issues could trigger + * this, this cache is ephemeral so at worst we're just speeding up + * when the user would need to re-enter their password. */ + + if (svn_hash_gets(parameters, SVN_AUTH_PARAM_NON_INTERACTIVE)) + { + /* In this case since we're running non-interactively we do not + * want to clear the cache since the user was never prompted by + * gpg-agent to set a password. */ + return SVN_NO_ERROR; + } + + *attempt = *attempt + 1; + + SVN_ERR(find_running_gpg_agent(&sd, pool)); + if (sd == -1) + return SVN_NO_ERROR; + + buffer = apr_palloc(pool, BUFFER_SIZE); + + if (!send_options(sd, buffer, BUFFER_SIZE, pool)) + { + bye_gpg_agent(sd); + return SVN_NO_ERROR; + } + + SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool)); + + request = apr_psprintf(pool, "CLEAR_PASSPHRASE %s\n", cache_id); + + if (write(sd, request, strlen(request)) == -1) + { + bye_gpg_agent(sd); + return SVN_NO_ERROR; + } + + if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE)) + { + bye_gpg_agent(sd); + return SVN_NO_ERROR; + } + + if (strncmp(buffer, "OK\n", 3) != 0) + { + bye_gpg_agent(sd); + return SVN_NO_ERROR; + } + + /* TODO: This attempt limit hard codes it at 3 attempts (or 2 retries) + * which matches svn command line client's retry_limit as set in + * svn_cmdline_create_auth_baton(). It would be nice to have that + * limit reflected here but that violates the boundry between the + * prompt provider and the cache provider. gpg-agent is acting as + * both here due to the peculiarties of their design so we'll have to + * live with this for now. Note that when these failures get exceeded + * it'll eventually fall back on the retry limits of whatever prompt + * provider is in effect, so this effectively doubles the limit. */ + if (*attempt < 4) + return svn_auth__simple_creds_cache_get(credentials, &iter_baton, + provider_baton, parameters, + realmstring, + password_get_gpg_agent, + SVN_AUTH__GPG_AGENT_PASSWORD_TYPE, + pool); + + return SVN_NO_ERROR; } @@ -468,7 +622,7 @@ simple_gpg_agent_save_creds(svn_boolean_t *saved, static const svn_auth_provider_t gpg_agent_simple_provider = { SVN_AUTH_CRED_SIMPLE, simple_gpg_agent_first_creds, - NULL, + simple_gpg_agent_next_creds, simple_gpg_agent_save_creds }; diff --git a/subversion/libsvn_subr/internal_statements.h b/subversion/libsvn_subr/internal_statements.h index 58616f4..c606de4 100644 --- a/subversion/libsvn_subr/internal_statements.h +++ b/subversion/libsvn_subr/internal_statements.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from internal_statements.sql and .dist_sandbox/subversion-1.8.10/subversion/libsvn_subr/token-map.h. +/* This file is automatically generated from internal_statements.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_subr/token-map.h. * Do not edit this file -- edit the source and rerun gen-make.py */ #define STMT_INTERNAL_SAVEPOINT_SVN 0 diff --git a/subversion/libsvn_subr/io.c b/subversion/libsvn_subr/io.c index 08f2e32..e27411e 100644 --- a/subversion/libsvn_subr/io.c +++ b/subversion/libsvn_subr/io.c @@ -4675,8 +4675,25 @@ svn_io_open_unique_file3(apr_file_t **file, * case, but only if the umask allows it. */ if (!using_system_temp_dir) { + svn_error_t *err; + SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool)); - SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool)); + err = file_perms_set2(tempfile, perms, scratch_pool); + if (err) + { + if (APR_STATUS_IS_INCOMPLETE(err->apr_err) || + APR_STATUS_IS_ENOTIMPL(err->apr_err)) + svn_error_clear(err); + else + { + const char *message; + message = apr_psprintf(scratch_pool, + _("Can't set permissions on '%s'"), + svn_dirent_local_style(tempname, + scratch_pool)); + return svn_error_quick_wrap(err, message); + } + } } #endif diff --git a/subversion/libsvn_subr/mergeinfo.c b/subversion/libsvn_subr/mergeinfo.c index 63496ae..131fe59 100644 --- a/subversion/libsvn_subr/mergeinfo.c +++ b/subversion/libsvn_subr/mergeinfo.c @@ -611,6 +611,43 @@ svn_rangelist__parse(svn_rangelist_t **rangelist, return SVN_NO_ERROR; } +/* Return TRUE, if all ranges in RANGELIST are in ascending order and do + * not overlap and are not adjacent. + * + * ### Can yield false negatives: ranges of differing inheritance are + * allowed to be adjacent. + * + * If this returns FALSE, you probaly want to qsort() the + * ranges and then call svn_rangelist__combine_adjacent_ranges(). + */ +static svn_boolean_t +is_rangelist_normalized(svn_rangelist_t *rangelist) +{ + int i; + svn_merge_range_t **ranges = (svn_merge_range_t **)rangelist->elts; + + for (i = 0; i < rangelist->nelts-1; ++i) + if (ranges[i]->end >= ranges[i+1]->start) + return FALSE; + + return TRUE; +} + +svn_error_t * +svn_rangelist__canonicalize(svn_rangelist_t *rangelist, + apr_pool_t *scratch_pool) +{ + if (! is_rangelist_normalized(rangelist)) + { + qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, + svn_sort_compare_ranges); + + SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool)); + } + + return SVN_NO_ERROR; +} + svn_error_t * svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist, apr_pool_t *scratch_pool) @@ -692,15 +729,11 @@ parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash, if (*input != end) *input = *input + 1; - /* Sort the rangelist, combine adjacent ranges into single ranges, - and make sure there are no overlapping ranges. */ - if (rangelist->nelts > 1) - { - qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, - svn_sort_compare_ranges); - - SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool)); - } + /* Sort the rangelist, combine adjacent ranges into single ranges, and + make sure there are no overlapping ranges. Luckily, most data in + svn:mergeinfo will already be in normalized form and this will be quick. + */ + SVN_ERR(svn_rangelist__canonicalize(rangelist, scratch_pool)); /* Handle any funky mergeinfo with relative merge source paths that might exist due to issue #3547. It's possible that this issue allowed @@ -1973,6 +2006,22 @@ svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool) return SVN_NO_ERROR; } +svn_error_t * +svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo, + apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, mergeinfo); hi; hi = apr_hash_next(hi)) + { + apr_array_header_t *rl = svn__apr_hash_index_val(hi); + + SVN_ERR(svn_rangelist__canonicalize(rl, scratch_pool)); + } + + return SVN_NO_ERROR; +} + svn_mergeinfo_catalog_t svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog, apr_pool_t *pool) diff --git a/subversion/libsvn_subr/sqlite3wrapper.c b/subversion/libsvn_subr/sqlite3wrapper.c index c35642b..4bbb619 100644 --- a/subversion/libsvn_subr/sqlite3wrapper.c +++ b/subversion/libsvn_subr/sqlite3wrapper.c @@ -24,7 +24,7 @@ /* Include sqlite3 inline, making all symbols private. */ #ifdef SVN_SQLITE_INLINE -# define SQLITE_OMIT_DEPRECATED +# define SQLITE_OMIT_DEPRECATED 1 # define SQLITE_API static # if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6 || __APPLE_CC__)) # if !__APPLE_CC__ || __GNUC_MINOR__ >= 6 diff --git a/subversion/libsvn_subr/string.c b/subversion/libsvn_subr/string.c index 20b0f24..c3d7fec 100644 --- a/subversion/libsvn_subr/string.c +++ b/subversion/libsvn_subr/string.c @@ -619,7 +619,7 @@ svn_stringbuf_insert(svn_stringbuf_t *str, if (bytes + count > str->data && bytes < str->data + str->blocksize) { /* special case: BYTES overlaps with this string -> copy the source */ - const char *temp = apr_pstrndup(str->pool, bytes, count); + const char *temp = apr_pmemdup(str->pool, bytes, count); svn_stringbuf_insert(str, pos, temp, count); } else @@ -659,7 +659,7 @@ svn_stringbuf_replace(svn_stringbuf_t *str, if (bytes + new_count > str->data && bytes < str->data + str->blocksize) { /* special case: BYTES overlaps with this string -> copy the source */ - const char *temp = apr_pstrndup(str->pool, bytes, new_count); + const char *temp = apr_pmemdup(str->pool, bytes, new_count); svn_stringbuf_replace(str, pos, old_count, temp, new_count); } else diff --git a/subversion/libsvn_subr/version.c b/subversion/libsvn_subr/version.c index 0859489..85b5a4e 100644 --- a/subversion/libsvn_subr/version.c +++ b/subversion/libsvn_subr/version.c @@ -136,7 +136,7 @@ svn_version_extended(svn_boolean_t verbose, info->build_time = __TIME__; info->build_host = SVN_BUILD_HOST; info->copyright = apr_pstrdup - (pool, _("Copyright (C) 2014 The Apache Software Foundation.\n" + (pool, _("Copyright (C) 2015 The Apache Software Foundation.\n" "This software consists of contributions made by many people;\n" "see the NOTICE file for more information.\n" "Subversion is open source software, see " |