Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/rev_hunt.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/rev_hunt.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_repos/rev_hunt.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/rev_hunt.c Fri Sep 11 15:51:30 2015 @@ -710,23 +710,6 @@ svn_repos_trace_node_locations(svn_fs_t if (! prev_path) break; - if (authz_read_func) - { - svn_boolean_t readable; - svn_fs_root_t *tmp_root; - - SVN_ERR(svn_fs_revision_root(&tmp_root, fs, revision, currpool)); - SVN_ERR(authz_read_func(&readable, tmp_root, path, - authz_read_baton, currpool)); - if (! readable) - { - svn_pool_destroy(lastpool); - svn_pool_destroy(currpool); - - return SVN_NO_ERROR; - } - } - /* Assign the current path to all younger revisions until we reach the copy target rev. */ while ((revision_ptr < revision_ptr_end) @@ -749,6 +732,20 @@ svn_repos_trace_node_locations(svn_fs_t path = prev_path; revision = prev_rev; + if (authz_read_func) + { + svn_boolean_t readable; + SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool)); + SVN_ERR(authz_read_func(&readable, root, path, + authz_read_baton, currpool)); + if (!readable) + { + svn_pool_destroy(lastpool); + svn_pool_destroy(currpool); + return SVN_NO_ERROR; + } + } + /* Clear last pool and switch. */ svn_pool_clear(lastpool); tmppool = lastpool; @@ -1336,6 +1333,7 @@ struct send_baton apr_hash_t *last_props; const char *last_path; svn_fs_root_t *last_root; + svn_boolean_t include_merged_revisions; }; /* Send PATH_REV to HANDLER and HANDLER_BATON, using information provided by @@ -1373,15 +1371,32 @@ send_path_revision(struct path_revision SVN_ERR(svn_prop_diffs(&prop_diffs, props, sb->last_props, sb->iterpool)); - /* Check if the contents *may* have changed. (Allow false positives, - for now, as the blame implementation currently depends on them.) */ - /* Special case: In the first revision, we always provide a delta. */ - if (sb->last_root) - SVN_ERR(svn_fs_contents_different(&contents_changed, sb->last_root, - sb->last_path, root, path_rev->path, - sb->iterpool)); + /* Check if the contents *may* have changed. */ + if (! sb->last_root) + { + /* Special case: In the first revision, we always provide a delta. */ + contents_changed = TRUE; + } + else if (sb->include_merged_revisions + && strcmp(sb->last_path, path_rev->path)) + { + /* ### This is a HACK!!! + * Blame -g, in older clients anyways, relies on getting a notification + * whenever the path changes - even if there was no content change. + * + * TODO: A future release should take an extra parameter and depending + * on that either always send a text delta or only send it if there + * is a difference. */ + contents_changed = TRUE; + } else - contents_changed = TRUE; + { + /* Did the file contents actually change? + * It could e.g. be a property-only change. */ + SVN_ERR(svn_fs_contents_different(&contents_changed, sb->last_root, + sb->last_path, root, path_rev->path, + sb->iterpool)); + } /* We have all we need, give to the handler. */ SVN_ERR(handler(handler_baton, path_rev->path, path_rev->revnum, @@ -1450,6 +1465,7 @@ get_file_revs_backwards(svn_repos_t *rep last_pool = svn_pool_create(scratch_pool); sb.iterpool = svn_pool_create(scratch_pool); sb.last_pool = svn_pool_create(scratch_pool); + sb.include_merged_revisions = FALSE; /* We want the first txdelta to be against the empty file. */ sb.last_root = NULL; @@ -1605,6 +1621,9 @@ svn_repos_get_file_revs2(svn_repos_t *re /* Create an empty hash table for the first property diff. */ sb.last_props = apr_hash_make(sb.last_pool); + /* Inform send_path_revision() whether workarounds / special behavior + * may be needed. */ + sb.include_merged_revisions = include_merged_revisions; /* Get the revisions we are interested in. */ duplicate_path_revs = apr_hash_make(scratch_pool);
Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/atomic.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/atomic.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/atomic.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/atomic.c Fri Sep 11 15:51:30 2015 @@ -24,6 +24,7 @@ #include <apr_time.h> #include "private/svn_atomic.h" + /* Magic values for atomic initialization */ #define SVN_ATOMIC_UNINITIALIZED 0 #define SVN_ATOMIC_START_INIT 1 @@ -31,19 +32,20 @@ #define SVN_ATOMIC_INITIALIZED 3 +/* Baton used by init_funct_t and init_once(). */ +typedef struct init_baton_t init_baton_t; + +/* Initialization function wrapper. Hides API details from init_once(). + The implementation must return FALSE on failure. */ +typedef svn_boolean_t (*init_func_t)(init_baton_t *init_baton); + /* - * This is the actual atomic initialization driver. The caller must - * provide either ERR_INIT_FUNC and ERR_P, or STR_INIT_FUNC and - * ERRSTR_P, but never both. + * This is the actual atomic initialization driver. + * Returns FALSE on failure. */ -static void -init_once(svn_atomic__err_init_func_t err_init_func, - svn_error_t **err_p, - svn_atomic__str_init_func_t str_init_func, - const char **errstr_p, - volatile svn_atomic_t *global_status, - void *baton, - apr_pool_t* pool) +static svn_boolean_t +init_once(volatile svn_atomic_t *global_status, + init_func_t init_func, init_baton_t *init_baton) { /* !! Don't use localizable strings in this function, because these !! might cause deadlocks. This function can be used to initialize @@ -53,44 +55,25 @@ init_once(svn_atomic__err_init_func_t er doesn't have statically-initialized mutexes, we implement a poor man's spinlock using svn_atomic_cas. */ - svn_error_t *err = SVN_NO_ERROR; - const char *errstr = NULL; svn_atomic_t status = svn_atomic_cas(global_status, SVN_ATOMIC_START_INIT, SVN_ATOMIC_UNINITIALIZED); -#ifdef SVN_DEBUG - /* Check that the parameters are valid. */ - assert(!err_init_func != !str_init_func); - assert(!err_init_func == !err_p); - assert(!str_init_func == !errstr_p); -#endif /* SVN_DEBUG */ - for (;;) { switch (status) { case SVN_ATOMIC_UNINITIALIZED: - if (err_init_func) - err = err_init_func(baton, pool); - else - errstr = str_init_func(baton); - if (err || errstr) - { - status = svn_atomic_cas(global_status, - SVN_ATOMIC_INIT_FAILED, - SVN_ATOMIC_START_INIT); - } - else - { - status = svn_atomic_cas(global_status, - SVN_ATOMIC_INITIALIZED, - SVN_ATOMIC_START_INIT); - } - - /* Take another spin through the switch to report either - failure or success. */ - continue; + { + const svn_boolean_t result = init_func(init_baton); + const svn_atomic_t init_state = (result + ? SVN_ATOMIC_INITIALIZED + : SVN_ATOMIC_INIT_FAILED); + + svn_atomic_cas(global_status, init_state, + SVN_ATOMIC_START_INIT); + return result; + } case SVN_ATOMIC_START_INIT: /* Wait for the init function to complete. */ @@ -101,19 +84,10 @@ init_once(svn_atomic__err_init_func_t er continue; case SVN_ATOMIC_INIT_FAILED: - if (err_init_func) - *err_p = svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, err, - "Couldn't perform atomic initialization"); - else - *errstr_p = errstr; - return; + return FALSE; case SVN_ATOMIC_INITIALIZED: - if (err_init_func) - *err_p = SVN_NO_ERROR; - else - *errstr_p = NULL; - return; + return TRUE; default: /* Something went seriously wrong with the atomic operations. */ @@ -123,15 +97,61 @@ init_once(svn_atomic__err_init_func_t er } +/* This baton structure is used by the two flavours of init-once APIs + to hide their differences from the init_once() driver. Each private + API uses only selected parts of the baton. + + No part of this structure changes unless a wrapped init function is + actually invoked by init_once(). +*/ +struct init_baton_t +{ + /* Used only by svn_atomic__init_once()/err_init_func_wrapper() */ + svn_atomic__err_init_func_t err_init_func; + svn_error_t *err; + apr_pool_t *pool; + + /* Used only by svn_atomic__init_no_error()/str_init_func_wrapper() */ + svn_atomic__str_init_func_t str_init_func; + const char *errstr; + + /* Used by both pairs of functions */ + void *baton; +}; + +/* Wrapper for the svn_atomic__init_once init function. */ +static svn_boolean_t err_init_func_wrapper(init_baton_t *init_baton) +{ + init_baton->err = init_baton->err_init_func(init_baton->baton, + init_baton->pool); + return (init_baton->err == SVN_NO_ERROR); +} + svn_error_t * svn_atomic__init_once(volatile svn_atomic_t *global_status, svn_atomic__err_init_func_t err_init_func, void *baton, apr_pool_t* pool) { - svn_error_t *err; - init_once(err_init_func, &err, NULL, NULL, global_status, baton, pool); - return err; + init_baton_t init_baton; + init_baton.err_init_func = err_init_func; + init_baton.err = NULL; + init_baton.pool = pool; + init_baton.baton = baton; + + if (init_once(global_status, err_init_func_wrapper, &init_baton)) + return SVN_NO_ERROR; + + return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, init_baton.err, + "Couldn't perform atomic initialization"); +} + + +/* Wrapper for the svn_atomic__init_no_error init function. */ +static svn_boolean_t str_init_func_wrapper(init_baton_t *init_baton) +{ + init_baton->errstr = init_baton->str_init_func(init_baton->baton); + return (init_baton->errstr == NULL); } const char * @@ -139,7 +159,18 @@ svn_atomic__init_once_no_error(volatile svn_atomic__str_init_func_t str_init_func, void *baton) { - const char *errstr; - init_once(NULL, NULL, str_init_func, &errstr, global_status, baton, NULL); - return errstr; + init_baton_t init_baton; + init_baton.str_init_func = str_init_func; + init_baton.errstr = NULL; + init_baton.baton = baton; + + if (init_once(global_status, str_init_func_wrapper, &init_baton)) + return NULL; + + /* Our init function wrapper may not have been called; make sure + that we return generic error message in that case. */ + if (!init_baton.errstr) + return "Couldn't perform atomic initialization"; + else + return init_baton.errstr; } Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.c Fri Sep 11 15:51:30 2015 @@ -880,3 +880,33 @@ svn_auth__make_session_auth(svn_auth_bat return SVN_NO_ERROR; } + + +static svn_error_t * +dummy_first_creds(void **credentials, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + *credentials = NULL; + *iter_baton = NULL; + return SVN_NO_ERROR; +} + +void +svn_auth__get_dummmy_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + static const svn_auth_provider_t vtable = { + SVN_AUTH_CRED_SIMPLE, + dummy_first_creds, + NULL, NULL + }; + + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + + po->vtable = &vtable; + *provider = po; +} Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.h URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.h?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.h (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/auth.h Fri Sep 11 15:51:30 2015 @@ -157,6 +157,15 @@ svn_auth__get_gpg_agent_simple_provider apr_pool_t *pool); #endif /* !defined(WIN32) || defined(DOXYGEN) */ +/** + * Set @a *provider to a dummy provider of type @c + * svn_auth_cred_simple_t that never returns or stores any + * credentials. + */ +void +svn_auth__get_dummmy_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool); + #ifdef __cplusplus } #endif /* __cplusplus */ Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/cache-membuffer.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/cache-membuffer.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/cache-membuffer.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/cache-membuffer.c Fri Sep 11 15:51:30 2015 @@ -28,12 +28,14 @@ #include "svn_pools.h" #include "svn_checksum.h" #include "svn_private_config.h" +#include "svn_hash.h" #include "svn_string.h" #include "svn_sorts.h" /* get the MIN macro */ #include "private/svn_atomic.h" #include "private/svn_dep_compat.h" #include "private/svn_mutex.h" +#include "private/svn_subr_private.h" #include "private/svn_string_private.h" #include "cache.h" @@ -117,6 +119,12 @@ * key length stored in the entry acts as an additional offset to find the * actual item. * + * Most keys are 16 bytes or less. We use the prefix indexes returned by + * a prefix_pool_t instance to uniquely identify the prefix in that case. + * Then the combination of prefix index and key stored in the fingerprint + * is then unique, too, and can never conflict. No full key construction, + * storage and comparison is needed in that case. + * * All access to the cached data needs to be serialized. Because we want * to scale well despite that bottleneck, we simply segment the cache into * a number of independent caches (segments). Items will be multiplexed based @@ -189,6 +197,10 @@ * entries with the same entry key. However unlikely, though, two different * full keys (see full_key_t) may have the same entry key. That is a * collision and at most one of them can be stored in the cache at any time. + * + * If the prefix is shared, which implies that the variable key part is no + * longer than 16 bytes, then there is a 1:1 mapping between full key and + * entry key. */ typedef struct entry_key_t { @@ -196,24 +208,184 @@ typedef struct entry_key_t apr_uint64_t fingerprint[2]; /* Length of the full key. This value is aligned to ITEM_ALIGNMENT to - * make sure the subsequent item content is properly aligned. */ + * make sure the subsequent item content is properly aligned. If 0, + * PREFIX_KEY is implied to be != NO_INDEX. */ apr_size_t key_len; + + /* Unique index of the shared key prefix, i.e. it's index within the + * prefix pool (see prefix_pool_t). NO_INDEX if the key prefix is not + * shared, otherwise KEY_LEN==0 is implied. */ + apr_uint32_t prefix_idx; } entry_key_t; /* A full key, i.e. the combination of the cache's key prefix with some * dynamic part appended to it. It also contains its ENTRY_KEY. + * + * If the ENTRY_KEY has a 1:1 mapping to the FULL_KEY, then the latter + * will be empty and remains unused. */ typedef struct full_key_t { /* Reduced form identifying the cache entry (if such an entry exists). */ entry_key_t entry_key; - /* This contains the full combination. Note that the SIZE element may - * be larger than ENTRY_KEY.KEY_LEN, but only the latter determines the - * valid key size. */ + /* If ENTRY_KEY is not a 1:1 mapping of the prefix + dynamic key + * combination, then this contains the full combination. Note that the + * SIZE element may be larger than ENTRY_KEY.KEY_LEN, but only the latter + * determines the valid key size. */ svn_membuf_t full_key; } full_key_t; +/* A limited capacity, thread-safe pool of unique C strings. Operations on + * this data structure are defined by prefix_pool_* functions. The only + * "public" member is VALUES (r/o access only). + */ +typedef struct prefix_pool_t +{ + /* Map C string to a pointer into VALUES with the same contents. */ + apr_hash_t *map; + + /* Pointer to an array of strings. These are the contents of this pool + * and each one of them is referenced by MAP. Valid indexes are 0 to + * VALUES_USED - 1. May be NULL if VALUES_MAX is 0. */ + const char **values; + + /* Number of used entries that VALUES may have. */ + apr_uint32_t values_max; + + /* Number of used entries in VALUES. Never exceeds VALUES_MAX. */ + apr_uint32_t values_used; + + /* Maximum number of bytes to allocate. */ + apr_size_t bytes_max; + + /* Number of bytes currently allocated. Should not exceed BYTES_MAX but + * the implementation may . */ + apr_size_t bytes_used; + + /* The serialization object. */ + svn_mutex__t *mutex; +} prefix_pool_t; + +/* Set *PREFIX_POOL to a new instance that tries to limit allocation to + * BYTES_MAX bytes. If MUTEX_REQUIRED is set and multi-threading is + * supported, serialize all access to the new instance. Allocate the + * object from *RESULT_POOL. */ +static svn_error_t * +prefix_pool_create(prefix_pool_t **prefix_pool, + apr_size_t bytes_max, + svn_boolean_t mutex_required, + apr_pool_t *result_pool) +{ + enum + { + /* With 56 byes of overhead under 64 bits, we will probably never get + * substantially below this. If we accidentally do, we will simply + * run out of entries in the VALUES array before running out of + * allocated memory. */ + ESTIMATED_BYTES_PER_ENTRY = 120, + }; + + /* Number of entries we are going to support. */ + apr_size_t capacity = MIN(APR_UINT32_MAX, + bytes_max / ESTIMATED_BYTES_PER_ENTRY); + + /* Construct the result struct. */ + prefix_pool_t *result = apr_pcalloc(result_pool, sizeof(*result)); + result->map = svn_hash__make(result_pool); + + result->values = capacity + ? apr_pcalloc(result_pool, capacity * sizeof(const char *)) + : NULL; + result->values_max = (apr_uint32_t)capacity; + result->values_used = 0; + + result->bytes_max = bytes_max; + result->bytes_used = capacity * sizeof(svn_membuf_t); + + SVN_ERR(svn_mutex__init(&result->mutex, mutex_required, result_pool)); + + /* Done. */ + *prefix_pool = result; + return SVN_NO_ERROR; +} + +/* Set *PREFIX_IDX to the offset in PREFIX_POOL->VALUES that contains the + * value PREFIX. If none exists, auto-insert it. If we can't due to + * capacity exhaustion, set *PREFIX_IDX to NO_INDEX. + * To be called by prefix_pool_get() only. */ +static svn_error_t * +prefix_pool_get_internal(apr_uint32_t *prefix_idx, + prefix_pool_t *prefix_pool, + const char *prefix) +{ + enum + { + /* Size of an hash entry plus (max.) APR alignment loss. + * + * This may be slightly off if e.g. APR changes its internal data + * structures but that will translate in just a few percent (~10%) + * over-allocation. Memory consumption will still be capped. + */ + OVERHEAD = 40 + 8 + }; + + const char **value; + apr_size_t prefix_len = strlen(prefix); + apr_size_t bytes_needed; + apr_pool_t *pool; + + /* Lookup. If we already know that prefix, return its index. */ + value = apr_hash_get(prefix_pool->map, prefix, prefix_len); + if (value != NULL) + { + const apr_size_t index = value - prefix_pool->values; + SVN_ERR_ASSERT(index < prefix_pool->values_used); + *prefix_idx = (apr_uint32_t) index; + return SVN_NO_ERROR; + } + + /* Capacity checks. */ + if (prefix_pool->values_used == prefix_pool->values_max) + { + *prefix_idx = NO_INDEX; + return SVN_NO_ERROR; + } + + bytes_needed = prefix_len + 1 + OVERHEAD; + if (prefix_pool->bytes_used + bytes_needed > prefix_pool->values_max) + { + *prefix_idx = NO_INDEX; + return SVN_NO_ERROR; + } + + /* Add new entry. */ + pool = apr_hash_pool_get(prefix_pool->map); + + value = &prefix_pool->values[prefix_pool->values_used]; + *value = apr_pstrndup(pool, prefix, prefix_len + 1); + apr_hash_set(prefix_pool->map, *value, prefix_len, value); + + *prefix_idx = prefix_pool->values_used; + ++prefix_pool->values_used; + prefix_pool->bytes_used += bytes_needed; + + return SVN_NO_ERROR; +} + +/* Thread-safe wrapper around prefix_pool_get_internal. */ +static svn_error_t * +prefix_pool_get(apr_uint32_t *prefix_idx, + prefix_pool_t *prefix_pool, + const char *prefix) +{ + SVN_MUTEX__WITH_LOCK(prefix_pool->mutex, + prefix_pool_get_internal(prefix_idx, prefix_pool, + prefix)); + + return SVN_NO_ERROR; +} + /* Debugging / corruption detection support. * If you define this macro, the getter functions will performed expensive * checks on the item data, requested keys and entry types. If there is @@ -263,13 +435,12 @@ typedef struct entry_tag_t /* Initialize all members of TAG except for the content hash. */ static svn_error_t *store_key_part(entry_tag_t *tag, - const full_key_t *prefix_key, + const char *prefix, const void *key, apr_size_t key_len, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { svn_checksum_t *checksum; - const char *prefix = prefix_key->full_key.data; apr_size_t prefix_len = strlen(prefix); if (prefix_len > sizeof(tag->prefix_tail)) @@ -280,12 +451,16 @@ static svn_error_t *store_key_part(entry SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, + prefix, + strlen(prefix), + scratch_pool)); + memcpy(tag->prefix_hash, checksum->digest, sizeof(tag->prefix_hash)); + + SVN_ERR(svn_checksum(&checksum, + svn_checksum_md5, key, key_len, - pool)); - - memcpy(tag->prefix_hash, prefix_key->entry_key.fingerprint, - sizeof(tag->prefix_hash)); + scratch_pool)); memcpy(tag->key_hash, checksum->digest, sizeof(tag->key_hash)); memset(tag->prefix_tail, 0, sizeof(tag->key_hash)); @@ -346,7 +521,7 @@ static svn_error_t* assert_equal_tags(co entry_tag_t *tag = &_tag; \ if (key) \ SVN_ERR(store_key_part(tag, \ - &cache->prefix, \ + get_prefix_key(cache), \ key, \ cache->key_len == APR_HASH_KEY_STRING \ ? strlen((const char *) key) \ @@ -521,6 +696,12 @@ struct svn_membuffer_t and that all segments must / will report the same values here. */ apr_uint32_t segment_count; + /* Collection of prefixes shared among all instances accessing the + * same membuffer cache backend. If a prefix is contained in this + * pool then all cache instances using an equal prefix must actually + * use the one stored in this pool. */ + prefix_pool_t *prefix_pool; + /* The dictionary, GROUP_SIZE * (group_count + spare_group_count) * entries long. Never NULL. */ @@ -802,8 +983,8 @@ initialize_group(svn_membuffer_t *cache, apr_uint32_t first_index = (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY; apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY; - if (last_index > cache->group_count) - last_index = cache->group_count; + if (last_index > cache->group_count + cache->spare_group_count) + last_index = cache->group_count + cache->spare_group_count; for (i = first_index; i < last_index; ++i) { @@ -1186,6 +1367,7 @@ entry_keys_match(const entry_key_t *lhs, { return (lhs->fingerprint[0] == rhs->fingerprint[0]) && (lhs->fingerprint[1] == rhs->fingerprint[1]) + && (lhs->prefix_idx == rhs->prefix_idx) && (lhs->key_len == rhs->key_len); } @@ -1248,7 +1430,8 @@ find_entry(svn_membuffer_t *cache, /* If we want to preserve it, check that it is actual a match. */ if (!find_empty) { - /* If there is no full key to compare, we are done. */ + /* If the full key is fully defined in prefix_id & mangeled + * key, we are done. */ if (!entry->key.key_len) return entry; @@ -1347,7 +1530,7 @@ find_entry(svn_membuffer_t *cache, */ for (i = 0; i < GROUP_SIZE; ++i) if (entry != &to_shrink->entries[i]) - let_entry_age(cache, entry); + let_entry_age(cache, &to_shrink->entries[i]); drop_entry(cache, entry); } @@ -1675,6 +1858,7 @@ svn_cache__membuffer_cache_create(svn_me apr_pool_t *pool) { svn_membuffer_t *c; + prefix_pool_t *prefix_pool; apr_uint32_t seg; apr_uint32_t group_count; @@ -1684,6 +1868,12 @@ svn_cache__membuffer_cache_create(svn_me apr_uint64_t data_size; apr_uint64_t max_entry_size; + /* Allocate 1% of the cache capacity to the prefix string pool. + */ + SVN_ERR(prefix_pool_create(&prefix_pool, total_size / 100, thread_safe, + pool)); + total_size -= total_size / 100; + /* Limit the total size (only relevant if we can address > 4GB) */ #if APR_SIZEOF_VOIDP > 4 @@ -1794,14 +1984,18 @@ svn_cache__membuffer_cache_create(svn_me /* allocate buffers and initialize cache members */ c[seg].segment_count = (apr_uint32_t)segment_count; + c[seg].prefix_pool = prefix_pool; c[seg].group_count = main_group_count; c[seg].spare_group_count = spare_group_count; c[seg].first_spare_group = NO_INDEX; c[seg].max_spare_used = 0; - c[seg].directory = apr_pcalloc(pool, - group_count * sizeof(entry_group_t)); + /* Allocate but don't clear / zero the directory because it would add + significantly to the server start-up time if the caches are large. + Group initialization will take care of that in stead. */ + c[seg].directory = apr_palloc(pool, + group_count * sizeof(entry_group_t)); /* Allocate and initialize directory entries as "not initialized", hence "unused" */ @@ -2233,7 +2427,7 @@ membuffer_cache_get_internal(svn_membuff /* Look for the *ITEM identified by KEY. If no item has been stored * for KEY, *ITEM will be NULL. Otherwise, the DESERIALIZER is called - * re-construct the proper object from the serialized data. + * to re-construct the proper object from the serialized data. * Allocations will be done in POOL. */ static svn_error_t * @@ -2581,11 +2775,11 @@ typedef struct svn_membuffer_cache_t */ svn_cache__deserialize_func_t deserializer; - /* Prepend this byte sequence to any key passed to us. + /* Prepend this to any key passed to us. * This makes our keys different from all keys used by svn_membuffer_cache_t * instances that we don't want to share cached data with. */ - full_key_t prefix; + entry_key_t prefix; /* length of the keys that will be passed to us through the * svn_cache_t interface. May be APR_HASH_KEY_STRING. @@ -2604,6 +2798,15 @@ typedef struct svn_membuffer_cache_t svn_mutex__t *mutex; } svn_membuffer_cache_t; +/* Return the prefix key used by CACHE. */ +static const char * +get_prefix_key(const svn_membuffer_cache_t *cache) +{ + return (cache->prefix.prefix_idx == NO_INDEX + ? cache->combined_key.full_key.data + : cache->membuffer->prefix_pool->values[cache->prefix.prefix_idx]); +} + /* Basically calculate a hash value for KEY of length KEY_LEN, combine it * with the CACHE->PREFIX and write the result in CACHE->COMBINED_KEY. * This could replace combine_key() entirely but we actually use it only @@ -2616,7 +2819,7 @@ combine_long_key(svn_membuffer_cache_t * { apr_uint32_t *digest_buffer; char *key_copy; - apr_size_t prefix_len = cache->prefix.entry_key.key_len; + apr_size_t prefix_len = cache->prefix.key_len; apr_size_t aligned_key_len; /* handle variable-length keys */ @@ -2640,9 +2843,9 @@ combine_long_key(svn_membuffer_cache_t * /* Combine with prefix. */ cache->combined_key.entry_key.fingerprint[0] - ^= cache->prefix.entry_key.fingerprint[0]; + ^= cache->prefix.fingerprint[0]; cache->combined_key.entry_key.fingerprint[1] - ^= cache->prefix.entry_key.fingerprint[1]; + ^= cache->prefix.fingerprint[1]; } /* Basically calculate a hash value for KEY of length KEY_LEN, combine it @@ -2653,47 +2856,55 @@ combine_key(svn_membuffer_cache_t *cache const void *key, apr_ssize_t key_len) { - /* short, fixed-size keys are the most common case */ - if (key_len != APR_HASH_KEY_STRING && key_len <= 16) - { - const apr_size_t prefix_len = cache->prefix.entry_key.key_len; + /* copy of *key, padded with 0 */ + apr_uint64_t data[2]; - /* Copy of *key, padded with 0. - * We put it just behind the prefix already copied into the COMBINED_KEY. - * The buffer space has been allocated when the cache was created. */ - apr_uint64_t *data = (void *)((char *)cache->combined_key.full_key.data + - prefix_len); - assert(prefix_len <= cache->combined_key.full_key.size - 16); - cache->combined_key.entry_key.key_len = prefix_len + 16; + /* Do we have to compare full keys? */ + if (cache->prefix.prefix_idx == NO_INDEX) + { + combine_long_key(cache, key, key_len); + return; + } - data[0] = 0; + /* short, fixed-size keys are the most common case */ + if (key_len == 16) + { + memcpy(data, key, 16); + } + else if (key_len == 8) + { + memcpy(data, key, 8); data[1] = 0; - memcpy(data, key, key_len); - - /* Scramble key DATA to spread the key space more evenly across the - * cache segments and entry buckets. All of this shall be reversible - * to prevent key collisions. So, we limit ourselves to xor and - * permutations. - * - * As long as we compare the full combined key, the additional - * fingerprint collisions introduced by a non-reversible scramble - * would simply reduce the cache effectiveness. - */ - data[1] = (data[1] << 27) | (data[1] >> 37); - data[1] ^= data[0] & 0xffff; - data[0] ^= data[1] & APR_UINT64_C(0xffffffffffff0000); - - /* combine with this cache's namespace */ - cache->combined_key.entry_key.fingerprint[0] - = data[0] ^ cache->prefix.entry_key.fingerprint[0]; - cache->combined_key.entry_key.fingerprint[1] - = data[1] ^ cache->prefix.entry_key.fingerprint[1]; } else { - /* longer or variably sized keys */ - combine_long_key(cache, key, key_len); + assert(key_len != APR_HASH_KEY_STRING && key_len < 16); + data[0] = 0; + data[1] = 0; + memcpy(data, key, key_len); } + + /* Scramble key DATA to spread the key space more evenly across the + * cache segments and entry buckets. All of this shall be reversible + * to prevent key collisions. So, we limit ourselves to xor and + * permutations. + * + * Since the entry key must preserve the full key (prefix and KEY), + * the scramble must not introduce KEY collisions. + */ + data[1] = (data[1] << 27) | (data[1] >> 37); + data[1] ^= data[0] & 0xffff; + data[0] ^= data[1] & APR_UINT64_C(0xffffffffffff0000); + + /* Combine with this cache's prefix. This is reversible because the + * prefix is known through to the respective entry_key element. So, + * knowing entry_key.prefix_id, we can still reconstruct KEY (and the + * prefix key). + */ + cache->combined_key.entry_key.fingerprint[0] + = data[0] ^ cache->prefix.fingerprint[0]; + cache->combined_key.entry_key.fingerprint[1] + = data[1] ^ cache->prefix.fingerprint[1]; } /* Implement svn_cache__vtable_t.get (not thread-safe) @@ -2940,7 +3151,7 @@ svn_membuffer_cache_get_info(void *cache /* cache front-end specific data */ - info->id = apr_pstrdup(result_pool, cache->prefix.full_key.data); + info->id = apr_pstrdup(result_pool, get_prefix_key(cache)); /* collect info from shared cache back-end */ @@ -3129,6 +3340,7 @@ svn_cache__create_membuffer_cache(svn_ca const char *prefix, apr_uint32_t priority, svn_boolean_t thread_safe, + svn_boolean_t short_lived, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -3159,28 +3371,50 @@ svn_cache__create_membuffer_cache(svn_ca prefix_orig_len = strlen(prefix) + 1; prefix_len = ALIGN_VALUE(prefix_orig_len); - svn_membuf__create(&cache->prefix.full_key, prefix_len, result_pool); - memcpy((char *)cache->prefix.full_key.data, prefix, prefix_orig_len); - memset((char *)cache->prefix.full_key.data + prefix_orig_len, 0, - prefix_len - prefix_orig_len); - /* Construct the folded prefix key. */ SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, prefix, strlen(prefix), scratch_pool)); - memcpy(cache->prefix.entry_key.fingerprint, checksum->digest, - sizeof(cache->prefix.entry_key.fingerprint)); - cache->prefix.entry_key.key_len = prefix_len; - - /* Initialize the combined key. Pre-allocate some extra room in the full - * key such that we probably don't need to re-alloc. */ - cache->combined_key.entry_key = cache->prefix.entry_key; - svn_membuf__create(&cache->combined_key.full_key, prefix_len + 200, - result_pool); - memcpy(cache->combined_key.full_key.data, cache->prefix.full_key.data, - prefix_len); + memcpy(cache->prefix.fingerprint, checksum->digest, + sizeof(cache->prefix.fingerprint)); + cache->prefix.key_len = prefix_len; + + /* Fix-length keys of up to 16 bytes may be handled without storing the + * full key separately for each item. */ + if ( (klen != APR_HASH_KEY_STRING) + && (klen <= sizeof(cache->combined_key.entry_key.fingerprint)) + && !short_lived) + SVN_ERR(prefix_pool_get(&cache->prefix.prefix_idx, + membuffer->prefix_pool, + prefix)); + else + cache->prefix.prefix_idx = NO_INDEX; + + /* If key combining is not guaranteed to produce unique results, we have + * to handle full keys. Otherwise, leave it NULL. */ + if (cache->prefix.prefix_idx == NO_INDEX) + { + /* Initialize the combined key. Pre-allocate some extra room in the + * full key such that we probably don't need to re-alloc. */ + cache->combined_key.entry_key = cache->prefix; + svn_membuf__create(&cache->combined_key.full_key, prefix_len + 200, + result_pool); + memcpy((char *)cache->combined_key.full_key.data, prefix, + prefix_orig_len); + memset((char *)cache->combined_key.full_key.data + prefix_orig_len, 0, + prefix_len - prefix_orig_len); + } + else + { + /* Initialize the combined key. We will never have the full combined + * key, so leave it NULL and set its length to 0 to prevent access to + * it. Keep the fingerprint 0 as well b/c it will always be set anew + * by combine_key(). */ + cache->combined_key.entry_key.prefix_idx = cache->prefix.prefix_idx; + cache->combined_key.entry_key.key_len = 0; + } /* initialize the generic cache wrapper */ Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/compress.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/compress.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/compress.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/compress.c Fri Sep 11 15:51:30 2015 @@ -31,6 +31,21 @@ #include "svn_private_config.h" +const char * +svn_zlib__compiled_version(void) +{ + static const char zlib_version_str[] = ZLIB_VERSION; + + return zlib_version_str; +} + +const char * +svn_zlib__runtime_version(void) +{ + return zlibVersion(); +} + + /* The zlib compressBound function was not exported until 1.2.0. */ #if ZLIB_VERNUM >= 0x1200 #define svnCompressBound(LEN) compressBound(LEN) Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/config.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/config.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/config.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/config.c Fri Sep 11 15:51:30 2015 @@ -55,6 +55,27 @@ struct cfg_section_t }; +/* States that a config option value can assume. */ +typedef enum option_state_t +{ + /* Value still needs to be expanded. + This is the initial state for *all* values. */ + option_state_needs_expanding, + + /* Value is currently being expanded. + This transitional state allows for detecting cyclic dependencies. */ + option_state_expanding, + + /* Expanded value is available. + Values that never needed expanding directly go into that state + skipping option_state_expanding. */ + option_state_expanded, + + /* The value expansion is cyclic which results in "undefined" behavior. + This is to return a defined value ("") in that case. */ + option_state_cyclic +} option_state_t; + /* Option table entries. */ typedef struct cfg_option_t cfg_option_t; struct cfg_option_t @@ -71,10 +92,10 @@ struct cfg_option_t /* The expanded option value. */ const char *x_value; - /* Expansion flag. If this is TRUE, this value has already been expanded. - In this case, if x_value is NULL, no expansions were necessary, - and value should be used directly. */ - svn_boolean_t expanded; + /* Expansion state. If this is option_state_expanded, VALUE has already + been expanded. In this case, if x_value is NULL, no expansions were + necessary, and value should be used directly. */ + option_state_t state; }; @@ -396,12 +417,13 @@ svn_config_merge(svn_config_t *cfg, cons static svn_boolean_t rmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option) { - /* Only clear the `expanded' flag if the value actually contains + /* Only reset the expansion state if the value actually contains variable expansions. */ - if (option->expanded && option->x_value != NULL) + if ( (option->state == option_state_expanded && option->x_value != NULL) + || option->state == option_state_cyclic) { option->x_value = NULL; - option->expanded = FALSE; + option->state = option_state_needs_expanding; } return FALSE; @@ -482,7 +504,7 @@ find_option(svn_config_t *cfg, const cha /* Has a bi-directional dependency with make_string_from_option(). */ -static void +static svn_boolean_t expand_option_value(svn_config_t *cfg, cfg_section_t *section, const char *opt_value, const char **opt_x_valuep, apr_pool_t *x_pool); @@ -496,7 +518,20 @@ make_string_from_option(const char **val apr_pool_t* x_pool) { /* Expand the option value if necessary. */ - if (!opt->expanded) + if ( opt->state == option_state_expanding + || opt->state == option_state_cyclic) + { + /* Recursion is not supported. Since we can't produce an error + * nor should we abort the process, the next best thing is to + * report the recursive part as an empty string. */ + *valuep = ""; + + /* Go into "value undefined" state. */ + opt->state = option_state_cyclic; + + return; + } + else if (opt->state == option_state_needs_expanding) { /* before attempting to expand an option, check for the placeholder. * If none is there, there is no point in calling expand_option_value. @@ -511,9 +546,16 @@ make_string_from_option(const char **val tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool)); - expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool); - opt->expanded = TRUE; + /* Expand the value. During that process, have the option marked + * as "expanding" to detect cycles. */ + opt->state = option_state_expanding; + if (expand_option_value(cfg, section, opt->value, &opt->x_value, + tmp_pool)) + opt->state = option_state_expanded; + else + opt->state = option_state_cyclic; + /* Ensure the expanded value is allocated in a permanent pool. */ if (x_pool != cfg->x_pool) { /* Grab the fully expanded value from tmp_pool before its @@ -527,7 +569,7 @@ make_string_from_option(const char **val } else { - opt->expanded = TRUE; + opt->state = option_state_expanded; } } @@ -549,8 +591,9 @@ make_string_from_option(const char **val /* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP. If no variable replacements are done, set *OPT_X_VALUEP to - NULL. Allocate from X_POOL. */ -static void + NULL. Return TRUE if the expanded value is defined and FALSE + for recursive definitions. Allocate from X_POOL. */ +static svn_boolean_t expand_option_value(svn_config_t *cfg, cfg_section_t *section, const char *opt_value, const char **opt_x_valuep, apr_pool_t *x_pool) @@ -587,6 +630,18 @@ expand_option_value(svn_config_t *cfg, c should terminate. */ make_string_from_option(&cstring, cfg, section, x_opt, x_pool); + /* Values depending on cyclic values must also be marked as + * "undefined" because they might themselves form cycles with + * the one cycle we just detected. Due to the early abort of + * the recursion, we won't follow and thus detect dependent + * cycles anymore. + */ + if (x_opt->state == option_state_cyclic) + { + *opt_x_valuep = ""; + return FALSE; + } + /* Append the plain text preceding the expansion. */ len = name_start - FMT_START_LEN - copy_from; if (buf == NULL) @@ -625,6 +680,9 @@ expand_option_value(svn_config_t *cfg, c } else *opt_x_valuep = NULL; + + /* Expansion has a well-defined answer. */ + return TRUE; } static cfg_section_t * @@ -664,7 +722,7 @@ svn_config_create_option(cfg_option_t ** o->value = apr_pstrdup(pool, value); o->x_value = NULL; - o->expanded = FALSE; + o->state = option_state_needs_expanding; *opt = o; } @@ -685,7 +743,8 @@ svn_config__is_expanded(svn_config_t *cf return FALSE; /* already expanded? */ - if (opt->expanded) + if ( opt->state == option_state_expanded + || opt->state == option_state_cyclic) return TRUE; /* needs expansion? */ @@ -719,8 +778,14 @@ svn_config_get(svn_config_t *cfg, const { apr_pool_t *tmp_pool = svn_pool_create(cfg->pool); const char *x_default; - expand_option_value(cfg, sec, default_value, &x_default, tmp_pool); - if (x_default) + if (!expand_option_value(cfg, sec, default_value, &x_default, + tmp_pool)) + { + /* Recursive definitions are not supported. + Normalize the answer in that case. */ + *valuep = ""; + } + else if (x_default) { svn_stringbuf_set(cfg->tmp_value, x_default); *valuep = cfg->tmp_value->data; @@ -758,7 +823,7 @@ svn_config_set(svn_config_t *cfg, { /* Replace the option's value. */ opt->value = apr_pstrdup(cfg->pool, value); - opt->expanded = FALSE; + opt->state = option_state_needs_expanding; return; } @@ -1171,7 +1236,7 @@ svn_config_dup(svn_config_t **cfgp, destopt->value = apr_pstrdup(pool, srcopt->value); destopt->x_value = apr_pstrdup(pool, srcopt->x_value); - destopt->expanded = srcopt->expanded; + destopt->state = srcopt->state; apr_hash_set(destsec->options, apr_pstrdup(pool, (const char*)optkey), optkeyLength, destopt); Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_auth.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_auth.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_auth.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_auth.c Fri Sep 11 15:51:30 2015 @@ -144,7 +144,7 @@ svn_config_write_auth_data(apr_hash_t *h apr_psprintf(pool, _("Error writing hash to '%s'"), svn_dirent_local_style(auth_path, pool))); SVN_ERR(svn_stream_close(stream)); - SVN_ERR(svn_io_file_rename(tmp_path, auth_path, pool)); + SVN_ERR(svn_io_file_rename2(tmp_path, auth_path, FALSE, pool)); /* To be nice, remove the realmstring from the hash again, just in case the caller wants their hash unchanged. Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_file.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_file.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_file.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/config_file.c Fri Sep 11 15:51:30 2015 @@ -1407,7 +1407,7 @@ svn_config_get_user_config_path(const ch if (! homedir) return SVN_NO_ERROR; *path = svn_dirent_join_many(pool, - svn_dirent_canonicalize(homedir, pool), + homedir, SVN_CONFIG__USR_DIRECTORY, fname, SVN_VA_NULL); } #endif /* WIN32 */ Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/deprecated.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/deprecated.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/deprecated.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/deprecated.c Fri Sep 11 15:51:30 2015 @@ -889,6 +889,14 @@ svn_io_stat_dirent(const svn_io_dirent2_ scratch_pool)); } +svn_error_t * +svn_io_file_rename(const char *from_path, const char *to_path, + apr_pool_t *pool) +{ + return svn_error_trace(svn_io_file_rename2(from_path, to_path, + FALSE, pool)); +} + /*** From constructors.c ***/ svn_log_changed_path_t * svn_log_changed_path_dup(const svn_log_changed_path_t *changed_path, @@ -1501,7 +1509,11 @@ void svn_auth_get_gpg_agent_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool) { +#ifdef SVN_HAVE_GPG_AGENT svn_auth__get_gpg_agent_simple_provider(provider, pool); +#else + svn_auth__get_dummmy_simple_provider(provider, pool); +#endif /* SVN_HAVE_GPG_AGENT */ } #endif /* !WIN32 */ Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/dirent_uri.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/dirent_uri.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/dirent_uri.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/dirent_uri.c Fri Sep 11 15:51:30 2015 @@ -495,14 +495,20 @@ canonicalize(path_type_t type, const cha #ifdef SVN_USE_DOS_PATHS /* If this is the first path segment of a file:// URI and it contains a windows drive letter, convert the drive letter to upper case. */ - else if (url && canon_segments == 1 && seglen == 2 && + else if (url && canon_segments == 1 && seglen >= 2 && (strncmp(canon, "file:", 5) == 0) && src[0] >= 'a' && src[0] <= 'z' && src[1] == ':') { *(dst++) = canonicalize_to_upper(src[0]); *(dst++) = ':'; - if (*next) - *(dst++) = *next; + if (seglen > 2) /* drive relative path */ + { + memcpy(dst, src + 2, seglen - 2); + dst += seglen - 2; + } + + if (slash_len) + *(dst++) = '/'; canon_segments++; } #endif /* SVN_USE_DOS_PATHS */ @@ -2398,8 +2404,11 @@ svn_uri_get_dirent_from_file_url(const c if (dup_path[1] == '|') dup_path[1] = ':'; - if (dup_path[2] == '/' || dup_path[2] == '\0') + if (dup_path[2] == '/' || dup_path[2] == '\\' || dup_path[2] == '\0') { + /* Dirents have upper case drive letters in their canonical form */ + dup_path[0] = canonicalize_to_upper(dup_path[0]); + if (dup_path[2] == '\0') { /* A valid dirent for the driveroot must be like "C:/" instead of @@ -2412,6 +2421,8 @@ svn_uri_get_dirent_from_file_url(const c new_path[3] = '\0'; dup_path = new_path; } + else + dup_path[2] = '/'; /* Ensure not relative for '\' after drive! */ } } if (hostname) Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/io.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/io.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/io.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/io.c Fri Sep 11 15:51:30 2015 @@ -142,6 +142,23 @@ #endif #ifdef WIN32 + +#if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */ +typedef struct _FILE_RENAME_INFO { + BOOL ReplaceIfExists; + HANDLE RootDirectory; + DWORD FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFO, *PFILE_RENAME_INFO; + +typedef struct _FILE_DISPOSITION_INFO { + BOOL DeleteFile; +} FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO; + +#define FileRenameInfo 3 +#define FileDispositionInfo 4 +#endif /* WIN32 < Vista */ + /* One-time initialization of the late bound Windows API functions. */ static volatile svn_atomic_t win_dynamic_imports_state = 0; @@ -152,7 +169,13 @@ typedef DWORD (WINAPI *GETFINALPATHNAMEB DWORD cchFilePath, DWORD dwFlags); +typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, + int FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize); + static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL; +static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL; /* Forward declaration. */ static svn_error_t * io_win_read_link(svn_string_t **dest, @@ -555,10 +578,8 @@ svn_io_open_uniquely_named(apr_file_t ** continue; #ifdef WIN32 - apr_err_2 = APR_TO_OS_ERROR(apr_err); - - if (apr_err_2 == ERROR_ACCESS_DENIED || - apr_err_2 == ERROR_SHARING_VIOLATION) + if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) || + apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) { /* The file is in use by another process or is hidden; create a new name, but don't do this 99999 times in @@ -750,7 +771,7 @@ svn_io_copy_link(const char *src, ".tmp", pool)); /* Move the tmp-link to link. */ - return svn_io_file_rename(dst_tmp, dst, pool); + return svn_io_file_rename2(dst_tmp, dst, FALSE, pool); #else return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, @@ -919,7 +940,7 @@ svn_io_copy_file(const char *src, if (copy_perms) SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool)); - return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool)); + return svn_error_trace(svn_io_file_rename2(dst_tmp, dst, FALSE, pool)); } #if !defined(WIN32) && !defined(__OS2__) @@ -1504,7 +1525,7 @@ reown_file(const char *path, SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name, svn_dirent_dirname(path, pool), svn_io_file_del_none, pool, pool)); - SVN_ERR(svn_io_file_rename(path, unique_name, pool)); + SVN_ERR(svn_io_file_rename2(path, unique_name, FALSE, pool)); SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool)); return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool)); } @@ -1861,11 +1882,18 @@ io_win_file_attrs_set(const char *fname, static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool) { - get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE) - GetProcAddress(GetModuleHandleA("kernel32.dll"), - "GetFinalPathNameByHandleW"); + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); - return SVN_NO_ERROR; + if (kernel32) + { + get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE) + GetProcAddress(kernel32, "GetFinalPathNameByHandleW"); + + set_file_information_by_handle_proc = (SetFileInformationByHandle_t) + GetProcAddress(kernel32, "SetFileInformationByHandle"); + } + + return SVN_NO_ERROR; } static svn_error_t * io_win_read_link(svn_string_t **dest, @@ -1937,6 +1965,130 @@ static svn_error_t * io_win_read_link(sv } } +/* Wrapper around Windows API function SetFileInformationByHandle() that + * returns APR status instead of boolean flag. */ +static apr_status_t +win32_set_file_information_by_handle(HANDLE hFile, + int FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize) +{ + svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state, + win_init_dynamic_imports, + NULL, NULL)); + + if (!set_file_information_by_handle_proc) + { + return SVN_ERR_UNSUPPORTED_FEATURE; + } + + if (!set_file_information_by_handle_proc(hFile, FileInformationClass, + lpFileInformation, + dwBufferSize)) + { + return apr_get_os_error(); + } + + return APR_SUCCESS; +} + +svn_error_t * +svn_io__win_delete_file_on_close(apr_file_t *file, + const char *path, + apr_pool_t *pool) +{ + FILE_DISPOSITION_INFO disposition_info; + HANDLE hFile; + apr_status_t status; + + apr_os_file_get(&hFile, file); + + disposition_info.DeleteFile = TRUE; + + status = win32_set_file_information_by_handle(hFile, FileDispositionInfo, + &disposition_info, + sizeof(disposition_info)); + + if (status) + { + return svn_error_wrap_apr(status, _("Can't remove file '%s'"), + svn_dirent_local_style(path, pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_io__win_rename_open_file(apr_file_t *file, + const char *from_path, + const char *to_path, + apr_pool_t *pool) +{ + WCHAR *w_final_abspath; + size_t path_len; + size_t rename_size; + FILE_RENAME_INFO *rename_info; + HANDLE hFile; + apr_status_t status; + + apr_os_file_get(&hFile, file); + + SVN_ERR(svn_io__utf8_to_unicode_longpath( + &w_final_abspath, svn_dirent_local_style(to_path,pool), + pool)); + + path_len = wcslen(w_final_abspath); + rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len; + + /* The rename info struct doesn't need hacks for long paths, + so no ugly escaping calls here */ + rename_info = apr_pcalloc(pool, rename_size); + rename_info->ReplaceIfExists = TRUE; + rename_info->FileNameLength = path_len; + memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR)); + + status = win32_set_file_information_by_handle(hFile, FileRenameInfo, + rename_info, + rename_size); + + if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) + { + /* Set the destination file writable because Windows will not allow + us to rename when final_abspath is read-only. */ + SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); + + status = win32_set_file_information_by_handle(hFile, + FileRenameInfo, + rename_info, + rename_size); + } + + /* Windows returns Vista+ client accessing network share stored on Windows + Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+ + client access Windows Server 2008 with disabled SMBv2 protocol. + + So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when + SetFileInformationByHandle() is not available and let caller to + handle it. + + See "Access denied error on checkout-commit after updating to 1.9.X" + discussion on [email protected]: + http://svn.haxx.se/dev/archive-2015-09/0054.shtml */ + if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)) + { + status = SVN_ERR_UNSUPPORTED_FEATURE; + } + + if (status) + { + return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"), + svn_dirent_local_style(from_path, pool), + svn_dirent_local_style(to_path, pool)); + } + + return SVN_NO_ERROR; +} + #endif /* WIN32 */ svn_error_t * @@ -2120,7 +2272,6 @@ svn_io_is_file_executable(svn_boolean_t /*** File locking. ***/ -#if !defined(WIN32) && !defined(__OS2__) /* Clear all outstanding locks on ARG, an open apr_file_t *. */ static apr_status_t file_clear_locks(void *arg) @@ -2135,7 +2286,6 @@ file_clear_locks(void *arg) return 0; } -#endif svn_error_t * svn_io_lock_open_file(apr_file_t *lockfile_handle, @@ -2193,13 +2343,14 @@ svn_io_lock_open_file(apr_file_t *lockfi } } -/* On Windows and OS/2 file locks are automatically released when - the file handle closes */ -#if !defined(WIN32) && !defined(__OS2__) + /* On Windows, a process may not release file locks before closing the + handle, and in this case the outstanding locks are unlocked by the OS. + However, this is not recommended, because the actual unlocking may be + postponed depending on available system resources. We explicitly unlock + the file as a part of the pool cleanup handler. */ apr_pool_cleanup_register(pool, lockfile_handle, file_clear_locks, apr_pool_cleanup_null); -#endif return SVN_NO_ERROR; } @@ -2223,11 +2374,7 @@ svn_io_unlock_open_file(apr_file_t *lock return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"), try_utf8_from_internal_style(fname, pool)); -/* On Windows and OS/2 file locks are automatically released when - the file handle closes */ -#if !defined(WIN32) && !defined(__OS2__) apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks); -#endif return SVN_NO_ERROR; } @@ -2301,6 +2448,14 @@ svn_error_t *svn_io_file_flush_to_disk(a apr_pool_t *pool) { apr_os_file_t filehand; + const char *fname; + apr_status_t apr_err; + + /* We need this only in case of an error but this is cheap to get - + * so we do it here for clarity. */ + apr_err = apr_file_name_get(&fname, file); + if (apr_err) + return svn_error_wrap_apr(apr_err, _("Can't get file name")); /* ### In apr 1.4+ we could delegate most of this function to apr_file_sync(). The only major difference is that this doesn't @@ -2318,7 +2473,8 @@ svn_error_t *svn_io_file_flush_to_disk(a if (! FlushFileBuffers(filehand)) return svn_error_wrap_apr(apr_get_os_error(), - _("Can't flush file to disk")); + _("Can't flush file '%s' to disk"), + try_utf8_from_internal_style(fname, pool)); #else int rv; @@ -2339,7 +2495,8 @@ svn_error_t *svn_io_file_flush_to_disk(a if (rv == -1) return svn_error_wrap_apr(apr_get_os_error(), - _("Can't flush file to disk")); + _("Can't flush file '%s' to disk"), + try_utf8_from_internal_style(fname, pool)); #endif } @@ -3864,7 +4021,7 @@ svn_io_write_atomic(const char *final_pa err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool); if (!err) - err = svn_io_file_rename(tmp_path, final_path, scratch_pool); + err = svn_io_file_rename2(tmp_path, final_path, TRUE, scratch_pool); if (err) { @@ -3878,21 +4035,6 @@ svn_io_write_atomic(const char *final_pa scratch_pool)); } -#if SVN_ON_POSIX - { - /* On POSIX, the file name is stored in the file's directory entry. - Hence, we need to fsync() that directory as well. - On other operating systems, we'd only be asking for trouble - by trying to open and fsync a directory. */ - apr_file_t *file; - - SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, - scratch_pool)); - SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); - SVN_ERR(svn_io_file_close(file, scratch_pool)); - } -#endif - return SVN_NO_ERROR; } @@ -4014,20 +4156,53 @@ svn_io_stat(apr_finfo_t *finfo, const ch return SVN_NO_ERROR; } +#if defined(WIN32) +/* Platform specific implementation of apr_file_rename() to workaround + APR problems on Windows. */ +static apr_status_t +win32_file_rename(const WCHAR *from_path_w, + const WCHAR *to_path_w, + svn_boolean_t flush_to_disk) +{ + /* APR calls MoveFileExW() with MOVEFILE_COPY_ALLOWED, while we rely + * that rename is atomic operation. Call MoveFileEx directly on Windows + * without MOVEFILE_COPY_ALLOWED flag to workaround it. + */ + + DWORD flags = MOVEFILE_REPLACE_EXISTING; + + if (flush_to_disk) + { + /* Do not return until the file has actually been moved on the disk. */ + flags |= MOVEFILE_WRITE_THROUGH; + } + + if (!MoveFileExW(from_path_w, to_path_w, flags)) + return apr_get_os_error(); + + return APR_SUCCESS; +} +#endif svn_error_t * -svn_io_file_rename(const char *from_path, const char *to_path, - apr_pool_t *pool) +svn_io_file_rename2(const char *from_path, const char *to_path, + svn_boolean_t flush_to_disk, apr_pool_t *pool) { apr_status_t status = APR_SUCCESS; const char *from_path_apr, *to_path_apr; +#if defined(WIN32) + WCHAR *from_path_w; + WCHAR *to_path_w; +#endif SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool)); SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool)); - status = apr_file_rename(from_path_apr, to_path_apr, pool); +#if defined(WIN32) + SVN_ERR(svn_io__utf8_to_unicode_longpath(&from_path_w, from_path_apr, pool)); + SVN_ERR(svn_io__utf8_to_unicode_longpath(&to_path_w, to_path_apr, pool)); + status = win32_file_rename(from_path_w, to_path_w, flush_to_disk); -#if defined(WIN32) || defined(__OS2__) /* If the target file is read only NTFS reports EACCESS and FAT/FAT32 reports EEXIST */ if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) @@ -4037,9 +4212,25 @@ svn_io_file_rename(const char *from_path allow renaming when from_path is read only. */ SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); + status = win32_file_rename(from_path_w, to_path_w, flush_to_disk); + } + WIN32_RETRY_LOOP(status, win32_file_rename(from_path_w, to_path_w, + flush_to_disk)); +#elif defined(__OS2__) + status = apr_file_rename(from_path_apr, to_path_apr, pool); + /* If the target file is read only NTFS reports EACCESS and + FAT/FAT32 reports EEXIST */ + if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) + { + /* Set the destination file writable because OS/2 will not + allow us to rename when to_path is read-only, but will + allow renaming when from_path is read only. */ + SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); + status = apr_file_rename(from_path_apr, to_path_apr, pool); } - WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool)); +#else + status = apr_file_rename(from_path_apr, to_path_apr, pool); #endif /* WIN32 || __OS2__ */ if (status) @@ -4047,6 +4238,34 @@ svn_io_file_rename(const char *from_path svn_dirent_local_style(from_path, pool), svn_dirent_local_style(to_path, pool)); +#if defined(SVN_ON_POSIX) + if (flush_to_disk) + { + /* On POSIX, the file name is stored in the file's directory entry. + Hence, we need to fsync() that directory as well. + On other operating systems, we'd only be asking for trouble + by trying to open and fsync a directory. */ + const char *dirname; + apr_file_t *file; + + dirname = svn_dirent_dirname(to_path, pool); + SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, + pool)); + SVN_ERR(svn_io_file_flush_to_disk(file, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + } +#elif !defined(WIN32) + /* Flush the target of the rename to disk. */ + if (flush_to_disk) + { + apr_file_t *file; + SVN_ERR(svn_io_file_open(&file, to_path, APR_WRITE, + APR_OS_DEFAULT, pool)); + SVN_ERR(svn_io_file_flush_to_disk(file, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + } +#endif + return SVN_NO_ERROR; } @@ -4055,8 +4274,8 @@ svn_error_t * svn_io_file_move(const char *from_path, const char *to_path, apr_pool_t *pool) { - svn_error_t *err = svn_error_trace(svn_io_file_rename(from_path, to_path, - pool)); + svn_error_t *err = svn_error_trace(svn_io_file_rename2(from_path, to_path, + FALSE, pool)); if (err && APR_STATUS_IS_EXDEV(err->apr_err)) { @@ -4216,8 +4435,8 @@ svn_io_dir_remove_nonrecursive(const cha { svn_boolean_t retry = TRUE; - if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY) - { + if (status == APR_FROM_OS_ERROR(ERROR_DIR_NOT_EMPTY)) + { apr_status_t empty_status = dir_is_empty(dirname_apr, pool); if (APR_STATUS_IS_ENOTEMPTY(empty_status)) @@ -4496,7 +4715,7 @@ svn_io_write_version_file(const char *pa #endif /* WIN32 || __OS2__ */ /* rename the temp file as the real destination */ - SVN_ERR(svn_io_file_rename(path_tmp, path, pool)); + SVN_ERR(svn_io_file_rename2(path_tmp, path, FALSE, pool)); /* And finally remove the perms to make it read only */ return svn_io_set_file_read_only(path, FALSE, pool); @@ -4924,10 +5143,8 @@ temp_file_create(apr_file_t **new_file, if (!apr_err_2 && finfo.filetype == APR_DIR) continue; - apr_err_2 = APR_TO_OS_ERROR(apr_err); - - if (apr_err_2 == ERROR_ACCESS_DENIED || - apr_err_2 == ERROR_SHARING_VIOLATION) + if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) || + apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) { /* The file is in use by another process or is hidden; create a new name, but don't do this 99999 times in Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/mergeinfo.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/mergeinfo.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/mergeinfo.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/mergeinfo.c Fri Sep 11 15:51:30 2015 @@ -2359,7 +2359,7 @@ svn_mergeinfo__catalog_to_formatted_stri svn_stringbuf_appendcstr(output_buf, "\n"); } } -#if SVN_DEBUG +#ifdef SVN_DEBUG else if (!catalog) { output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); Modified: subversion/branches/reuse-ra-session/subversion/libsvn_subr/mutex.c URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_subr/mutex.c?rev=1702504&r1=1702503&r2=1702504&view=diff ============================================================================== --- subversion/branches/reuse-ra-session/subversion/libsvn_subr/mutex.c (original) +++ subversion/branches/reuse-ra-session/subversion/libsvn_subr/mutex.c Fri Sep 11 15:51:30 2015 @@ -105,3 +105,13 @@ svn_mutex__unlock(svn_mutex__t *mutex, return err; } + +#if APR_HAS_THREADS + +apr_thread_mutex_t * +svn_mutex__get(svn_mutex__t *mutex) +{ + return mutex->mutex; +} + +#endif
