Modified: subversion/branches/ra-git/subversion/libsvn_repos/replay.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/replay.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_repos/replay.c (original) +++ subversion/branches/ra-git/subversion/libsvn_repos/replay.c Tue Oct 11 09:11:50 2016 @@ -198,7 +198,7 @@ add_subdir(svn_fs_root_t *source_root, for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent = apr_hash_this_val(hi); const char *copyfrom_path = NULL; @@ -412,7 +412,7 @@ fill_copyfrom(svn_fs_root_t **copyfrom_r svn_revnum_t *copyfrom_rev, svn_boolean_t *src_readable, svn_fs_root_t *root, - svn_fs_path_change2_t *change, + svn_fs_path_change3_t *change, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, const char *path, @@ -463,7 +463,7 @@ path_driver_cb_func(void **dir_baton, const svn_delta_editor_t *editor = cb->editor; void *edit_baton = cb->edit_baton; svn_fs_root_t *root = cb->root; - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t do_add = FALSE, do_delete = FALSE; void *file_baton = NULL; svn_revnum_t copyfrom_rev; @@ -843,6 +843,80 @@ fetch_props_func(apr_hash_t **props, +/* Retrieve the path changes under ROOT, filter them with AUTHZ_READ_FUNC + and AUTHZ_READ_BATON and return those that intersect with BASE_RELPATH. + + The svn_fs_path_change3_t* will be returned in *CHANGED_PATHS, keyed by + their path. The paths themselves are additionally returned in *PATHS. + + Allocate the returned data in RESULT_POOL and use SCRATCH_POOL for + temporary allocations. + */ +static svn_error_t * +get_relevant_changes(apr_hash_t **changed_paths, + apr_array_header_t **paths, + svn_fs_root_t *root, + const char *base_relpath, + svn_repos_authz_func_t authz_read_func, + void *authz_read_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Fetch the paths changed under ROOT. */ + SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + + /* Make an array from the keys of our CHANGED_PATHS hash, and copy + the values into a new hash whose keys have no leading slashes. */ + *paths = apr_array_make(result_pool, 16, sizeof(const char *)); + *changed_paths = apr_hash_make(result_pool); + while (change) + { + const char *path = change->path.data; + apr_ssize_t keylen = change->path.len; + svn_boolean_t allowed = TRUE; + + svn_pool_clear(iterpool); + if (authz_read_func) + SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, + iterpool)); + + if (allowed) + { + if (path[0] == '/') + { + path++; + keylen--; + } + + /* If the base_path doesn't match the top directory of this path + we don't want anything to do with it... + ...unless this was a change to one of the parent directories of + base_path. */ + if ( svn_relpath_skip_ancestor(base_relpath, path) + || svn_relpath_skip_ancestor(path, base_relpath)) + { + change = svn_fs_path_change3_dup(change, result_pool); + path = change->path.data; + if (path[0] == '/') + path++; + + APR_ARRAY_PUSH(*paths, const char *) = path; + apr_hash_set(*changed_paths, path, keylen, change); + } + } + + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + svn_error_t * svn_repos_replay2(svn_fs_root_t *root, const char *base_path, @@ -855,9 +929,7 @@ svn_repos_replay2(svn_fs_root_t *root, apr_pool_t *pool) { #ifndef USE_EV2_IMPL - apr_hash_t *fs_changes; apr_hash_t *changed_paths; - apr_hash_index_t *hi; apr_array_header_t *paths; struct path_driver_cb_baton cb_baton; @@ -869,54 +941,15 @@ svn_repos_replay2(svn_fs_root_t *root, return SVN_NO_ERROR; } - /* Fetch the paths changed under ROOT. */ - SVN_ERR(svn_fs_paths_changed2(&fs_changes, root, pool)); - if (! base_path) base_path = ""; else if (base_path[0] == '/') ++base_path; - /* Make an array from the keys of our CHANGED_PATHS hash, and copy - the values into a new hash whose keys have no leading slashes. */ - paths = apr_array_make(pool, apr_hash_count(fs_changes), - sizeof(const char *)); - changed_paths = apr_hash_make(pool); - for (hi = apr_hash_first(pool, fs_changes); hi; hi = apr_hash_next(hi)) - { - const char *path = apr_hash_this_key(hi); - apr_ssize_t keylen = apr_hash_this_key_len(hi); - svn_fs_path_change2_t *change = apr_hash_this_val(hi); - svn_boolean_t allowed = TRUE; - - if (authz_read_func) - SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, - pool)); - - if (allowed) - { - if (path[0] == '/') - { - path++; - keylen--; - } - - /* If the base_path doesn't match the top directory of this path - we don't want anything to do with it... */ - if (svn_relpath_skip_ancestor(base_path, path) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - /* ...unless this was a change to one of the parent directories of - base_path. */ - else if (svn_relpath_skip_ancestor(path, base_path) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - } - } + /* Fetch the paths changed under ROOT. */ + SVN_ERR(get_relevant_changes(&changed_paths, &paths, root, base_path, + authz_read_func, authz_read_baton, + pool, pool)); /* If we were not given a low water mark, assume that everything is there, all the way back to revision 0. */ @@ -1062,7 +1095,7 @@ add_subdir_ev2(svn_fs_root_t *source_roo for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent = apr_hash_this_val(hi); const char *copyfrom_path = NULL; @@ -1190,7 +1223,7 @@ replay_node(svn_fs_root_t *root, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t do_add = FALSE; svn_boolean_t do_delete = FALSE; svn_revnum_t copyfrom_rev; @@ -1485,9 +1518,7 @@ svn_repos__replay_ev2(svn_fs_root_t *roo void *authz_read_baton, apr_pool_t *scratch_pool) { - apr_hash_t *fs_changes; apr_hash_t *changed_paths; - apr_hash_index_t *hi; apr_array_header_t *paths; apr_array_header_t *copies; apr_pool_t *iterpool; @@ -1505,49 +1536,10 @@ svn_repos__replay_ev2(svn_fs_root_t *roo } /* Fetch the paths changed under ROOT. */ - SVN_ERR(svn_fs_paths_changed2(&fs_changes, root, scratch_pool)); - - /* Make an array from the keys of our CHANGED_PATHS hash, and copy - the values into a new hash whose keys have no leading slashes. */ - paths = apr_array_make(scratch_pool, apr_hash_count(fs_changes), - sizeof(const char *)); - changed_paths = apr_hash_make(scratch_pool); - for (hi = apr_hash_first(scratch_pool, fs_changes); hi; - hi = apr_hash_next(hi)) - { - const char *path = apr_hash_this_key(hi); - apr_ssize_t keylen = apr_hash_this_key_len(hi); - svn_fs_path_change2_t *change = apr_hash_this_val(hi); - svn_boolean_t allowed = TRUE; - - if (authz_read_func) - SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, - scratch_pool)); - - if (allowed) - { - if (path[0] == '/') - { - path++; - keylen--; - } - - /* If the base_path doesn't match the top directory of this path - we don't want anything to do with it... */ - if (svn_relpath_skip_ancestor(base_repos_relpath, path) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - /* ...unless this was a change to one of the parent directories of - base_path. */ - else if (svn_relpath_skip_ancestor(path, base_repos_relpath) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - } - } + SVN_ERR(get_relevant_changes(&changed_paths, &paths, root, + base_repos_relpath, + authz_read_func, authz_read_baton, + scratch_pool, scratch_pool)); /* If we were not given a low water mark, assume that everything is there, all the way back to revision 0. */
Modified: subversion/branches/ra-git/subversion/libsvn_repos/reporter.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/reporter.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_repos/reporter.c (original) +++ subversion/branches/ra-git/subversion/libsvn_repos/reporter.c Tue Oct 11 09:11:50 2016 @@ -522,19 +522,13 @@ delta_proplists(report_baton_t *b, svn_r { svn_fs_root_t *s_root; apr_hash_t *s_props = NULL, *t_props; - apr_array_header_t *prop_diffs; - int i; svn_revnum_t crev; - revision_info_t *revision_info; - svn_boolean_t changed; - const svn_prop_t *pc; - svn_lock_t *lock; - apr_hash_index_t *hi; /* Fetch the created-rev and send entry props. */ SVN_ERR(svn_fs_node_created_rev(&crev, b->t_root, t_path, pool)); if (SVN_IS_VALID_REVNUM(crev)) { + revision_info_t *revision_info; /* convert committed-rev to string */ char buf[SVN_INT64_BUFFER_SIZE]; svn_string_t cr_str; @@ -565,6 +559,7 @@ delta_proplists(report_baton_t *b, svn_r /* Update lock properties. */ if (lock_token) { + svn_lock_t *lock; SVN_ERR(svn_fs_get_lock(&lock, b->repos->fs, t_path, pool)); /* Delete a defunct lock. */ @@ -575,6 +570,7 @@ delta_proplists(report_baton_t *b, svn_r if (s_path) { + svn_boolean_t changed; SVN_ERR(get_source_root(b, &s_root, s_rev)); /* Is this deltification worth our time? */ @@ -592,16 +588,20 @@ delta_proplists(report_baton_t *b, svn_r if (s_props && apr_hash_count(s_props)) { + apr_array_header_t *prop_diffs; + int i; + /* Now transmit the differences. */ SVN_ERR(svn_prop_diffs(&prop_diffs, t_props, s_props, pool)); for (i = 0; i < prop_diffs->nelts; i++) { - pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t); + const svn_prop_t *pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t); SVN_ERR(change_fn(b, object, pc->name, pc->value, pool)); } } else if (apr_hash_count(t_props)) { + apr_hash_index_t *hi; /* So source, i.e. all new. Transmit all target props. */ for (hi = apr_hash_first(pool, t_props); hi; hi = apr_hash_next(hi)) { @@ -671,7 +671,6 @@ delta_files(report_baton_t *b, void *fil const char *s_path, const char *t_path, const char *lock_token, apr_pool_t *pool) { - svn_boolean_t changed; svn_fs_root_t *s_root = NULL; svn_txdelta_stream_t *dstream = NULL; svn_checksum_t *s_checksum; @@ -685,14 +684,15 @@ delta_files(report_baton_t *b, void *fil if (s_path) { + svn_boolean_t changed; SVN_ERR(get_source_root(b, &s_root, s_rev)); /* We're not interested in the theoretical difference between "has contents which have not changed with respect to" and "has the same actual contents as" when sending text-deltas. If we know the delta is an empty one, we avoiding sending it in either case. */ - SVN_ERR(svn_repos__compare_files(&changed, b->t_root, t_path, - s_root, s_path, pool)); + SVN_ERR(svn_fs_contents_different(&changed, b->t_root, t_path, + s_root, s_path, pool)); if (!changed) return SVN_NO_ERROR; @@ -918,7 +918,7 @@ update_entry(report_baton_t *b, svn_revn const char *e_path, path_info_t *info, svn_depth_t wc_depth, svn_depth_t requested_depth, apr_pool_t *pool) { - svn_fs_root_t *s_root; + svn_fs_root_t *s_root = NULL; svn_boolean_t allowed, related; void *new_baton; svn_checksum_t *checksum; @@ -961,7 +961,26 @@ update_entry(report_baton_t *b, svn_revn if (s_entry && t_entry && s_entry->kind == t_entry->kind) { int distance = svn_fs_compare_ids(s_entry->id, t_entry->id); - if (distance == 0 && !any_path_info(b, e_path) + svn_boolean_t changed = TRUE; + + /* Check related files for content changes to avoid reporting + * unchanged copies of files to the client as an open_file() call + * and change_file_prop()/apply_textdelta() calls with no-op changes. + * The client will otherwise raise unnecessary tree conflicts. */ + if (!b->ignore_ancestry && t_entry->kind == svn_node_file && + distance == 1) + { + if (s_root == NULL) + SVN_ERR(get_source_root(b, &s_root, s_rev)); + + SVN_ERR(svn_fs_props_different(&changed, s_root, s_path, + b->t_root, t_path, pool)); + if (!changed) + SVN_ERR(svn_fs_contents_different(&changed, s_root, s_path, + b->t_root, t_path, pool)); + } + + if ((distance == 0 || !changed) && !any_path_info(b, e_path) && (requested_depth <= wc_depth || t_entry->kind == svn_node_file)) { if (!info) Modified: subversion/branches/ra-git/subversion/libsvn_repos/repos.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/repos.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_repos/repos.c (original) +++ subversion/branches/ra-git/subversion/libsvn_repos/repos.c Tue Oct 11 09:11:50 2016 @@ -712,7 +712,7 @@ create_hooks(svn_repos_t *repos, apr_poo "# Because the locks have already been created and cannot be undone," NL "# the exit code of the hook program is ignored. The hook program" NL "# can use the 'svnlook' utility to examine the paths in the repository" NL -"# but since the hook is invoked asyncronously the newly-created locks" NL +"# but since the hook is invoked asynchronously the newly-created locks" NL "# may no longer be present." NL; script = "REPOS=\"$1\"" NL @@ -848,11 +848,16 @@ create_conf(svn_repos_t *repos, apr_pool "### no path-based access control is done." NL "### Uncomment the line below to use the default authorization file." NL "# authz-db = " SVN_REPOS__CONF_AUTHZ NL -"### The groups-db option controls the location of the groups file." NL -"### Unless you specify a path starting with a /, the file's location is" NL -"### relative to the directory containing this file. The specified path" NL -"### may be a repository relative URL (^/) or an absolute file:// URL to a" NL -"### text file in a Subversion repository." NL +"### The groups-db option controls the location of the file with the" NL +"### group definitions and allows maintaining groups separately from the" NL +"### authorization rules. The groups-db file is of the same format as the" NL +"### authz-db file and should contain a single [groups] section with the" NL +"### group definitions. If the option is enabled, the authz-db file cannot" NL +"### contain a [groups] section. Unless you specify a path starting with" NL +"### a /, the file's location is relative to the directory containing this" NL +"### file. The specified path may be a repository relative URL (^/) or an" NL +"### absolute file:// URL to a text file in a Subversion repository." NL +"### This option is not being used by default." NL "# groups-db = " SVN_REPOS__CONF_GROUPS NL "### This option specifies the authentication realm of the repository." NL "### If two repositories have the same authentication realm, they should" NL Modified: subversion/branches/ra-git/subversion/libsvn_repos/repos.h URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/repos.h?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_repos/repos.h (original) +++ subversion/branches/ra-git/subversion/libsvn_repos/repos.h Tue Oct 11 09:11:50 2016 @@ -390,17 +390,6 @@ svn_repos__authz_validate(svn_authz_t *a /*** Utility Functions ***/ -/* Set *CHANGED_P to TRUE if ROOT1/PATH1 and ROOT2/PATH2 have - different contents, FALSE if they have the same contents. - Use POOL for temporary allocation. */ -svn_error_t * -svn_repos__compare_files(svn_boolean_t *changed_p, - svn_fs_root_t *root1, - const char *path1, - svn_fs_root_t *root2, - const char *path2, - apr_pool_t *pool); - /* Set *PREV_PATH and *PREV_REV to the path and revision which represent the location at which PATH in FS was located immediately prior to REVISION iff there was a copy operation (to PATH or one of Modified: subversion/branches/ra-git/subversion/libsvn_repos/rev_hunt.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/rev_hunt.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_repos/rev_hunt.c (original) +++ subversion/branches/ra-git/subversion/libsvn_repos/rev_hunt.c Tue Oct 11 09:11:50 2016 @@ -1012,26 +1012,39 @@ get_merged_mergeinfo(apr_hash_t **merged apr_hash_t *curr_mergeinfo, *prev_mergeinfo, *deleted, *changed; svn_error_t *err; svn_fs_root_t *root, *prev_root; - apr_hash_t *changed_paths; - const char *path = old_path_rev->path; + const char *start_path = old_path_rev->path; + const char *path = NULL; + + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; /* Getting/parsing/diffing svn:mergeinfo is expensive, so only do it if there is a property change. */ SVN_ERR(svn_fs_revision_root(&root, repos->fs, old_path_rev->revnum, scratch_pool)); - SVN_ERR(svn_fs_paths_changed2(&changed_paths, root, scratch_pool)); - while (1) + SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + + /* Find the changed PATH closest to START_PATH which may have a mergeinfo + * change. */ + while (change) { - svn_fs_path_change2_t *changed_path = svn_hash_gets(changed_paths, path); - if (changed_path && changed_path->prop_mod - && changed_path->mergeinfo_mod != svn_tristate_false) - break; - if (svn_fspath__is_root(path, strlen(path))) + if ( change->prop_mod + && change->mergeinfo_mod != svn_tristate_false + && svn_fspath__skip_ancestor(change->path.data, start_path)) { - *merged_mergeinfo = NULL; - return SVN_NO_ERROR; + if (!path || svn_fspath__skip_ancestor(path, change->path.data)) + path = apr_pstrmemdup(scratch_pool, change->path.data, + change->path.len); } - path = svn_fspath__dirname(path, scratch_pool); + + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + } + + if (path == NULL) + { + *merged_mergeinfo = NULL; + return SVN_NO_ERROR; } /* First, find the mergeinfo difference for old_path_rev->revnum, and Modified: subversion/branches/ra-git/subversion/libsvn_subr/cache-membuffer.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/cache-membuffer.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/cache-membuffer.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/cache-membuffer.c Tue Oct 11 09:11:50 2016 @@ -147,6 +147,10 @@ #endif /* For more efficient copy operations, let's align all data items properly. + * Since we can't portably align pointers, this is rather the item size + * granularity which ensures *relative* alignment within the cache - still + * giving us decent copy speeds on most machines. + * * Must be a power of 2. */ #define ITEM_ALIGNMENT 16 @@ -354,7 +358,7 @@ prefix_pool_get_internal(apr_uint32_t *p bytes_needed = prefix_len + 1 + OVERHEAD; assert(prefix_pool->bytes_max >= prefix_pool->bytes_used); - if (prefix_pool->bytes_max - prefix_pool->bytes_used > bytes_needed) + if (prefix_pool->bytes_max - prefix_pool->bytes_used < bytes_needed) { *prefix_idx = NO_INDEX; return SVN_NO_ERROR; @@ -814,10 +818,6 @@ struct svn_membuffer_t */ #define ALIGN_VALUE(value) (((value) + ITEM_ALIGNMENT-1) & -ITEM_ALIGNMENT) -/* Align POINTER value to the next ITEM_ALIGNMENT boundary. - */ -#define ALIGN_POINTER(pointer) ((void*)ALIGN_VALUE((apr_size_t)(char*)(pointer))) - /* If locking is supported for CACHE, acquire a read lock for it. */ static svn_error_t * @@ -1827,28 +1827,6 @@ ensure_data_insertable_l1(svn_membuffer_ * right answer. */ } -/* Mimic apr_pcalloc in APR_POOL_DEBUG mode, i.e. handle failed allocations - * (e.g. OOM) properly: Allocate at least SIZE bytes from POOL and zero - * the content of the allocated memory if ZERO has been set. Return NULL - * upon failed allocations. - * - * Also, satisfy our buffer alignment needs for performance reasons. - */ -static void* secure_aligned_alloc(apr_pool_t *pool, - apr_size_t size, - svn_boolean_t zero) -{ - void* memory = apr_palloc(pool, size + ITEM_ALIGNMENT); - if (memory != NULL) - { - memory = ALIGN_POINTER(memory); - if (zero) - memset(memory, 0, size); - } - - return memory; -} - svn_error_t * svn_cache__membuffer_cache_create(svn_membuffer_t **cache, apr_size_t total_size, @@ -2017,10 +1995,11 @@ svn_cache__membuffer_cache_create(svn_me c[seg].l2.last = NO_INDEX; c[seg].l2.next = NO_INDEX; c[seg].l2.start_offset = c[seg].l1.size; - c[seg].l2.size = data_size - c[seg].l1.size; + c[seg].l2.size = ALIGN_VALUE(data_size) - c[seg].l1.size; c[seg].l2.current_data = c[seg].l2.start_offset; - c[seg].data = secure_aligned_alloc(pool, (apr_size_t)data_size, FALSE); + /* This cast is safe because DATA_SIZE <= MAX_SEGMENT_SIZE. */ + c[seg].data = apr_palloc(pool, (apr_size_t)ALIGN_VALUE(data_size)); c[seg].data_used = 0; c[seg].max_entry_size = max_entry_size; @@ -2219,18 +2198,13 @@ membuffer_cache_set_internal(svn_membuff /* first, look for a previous entry for the given key */ entry_t *entry = find_entry(cache, group_index, to_find, FALSE); - /* Quick size check to make sure arithmetics will work further down - * the road. */ - if ( cache->max_entry_size >= item_size - && cache->max_entry_size - item_size >= to_find->entry_key.key_len) - { - size = item_size + to_find->entry_key.key_len; - } - else - { - /* The combination of serialized ITEM and KEY does not fit, so the - * the insertion attempt will fail and simply remove any old entry - * if that exists. */ + /* Quick check make sure arithmetics will work further down the road. */ + size = item_size + to_find->entry_key.key_len; + if (size < item_size) + { + /* Arithmetic overflow, so combination of serialized ITEM and KEY + * cannot not fit into the cache. Setting BUFFER to NULL will cause + * the removal of any entry if that exists without writing new data. */ buffer = NULL; } @@ -2414,7 +2388,7 @@ membuffer_cache_get_internal(svn_membuff } size = ALIGN_VALUE(entry->size) - entry->key.key_len; - *buffer = ALIGN_POINTER(apr_palloc(result_pool, size + ITEM_ALIGNMENT-1)); + *buffer = apr_palloc(result_pool, size); memcpy(*buffer, cache->data + entry->offset + entry->key.key_len, size); #ifdef SVN_DEBUG_CACHE_MEMBUFFER Modified: subversion/branches/ra-git/subversion/libsvn_subr/checksum.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/checksum.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/checksum.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/checksum.c Tue Oct 11 09:11:50 2016 @@ -86,6 +86,7 @@ static const char *ckind_str[] = { "$sha1$", "$fnv1$", "$fnvm$", + /* ### svn_checksum_deserialize() assumes all these have the same strlen() */ }; /* Returns the digest size of it's argument. */ Modified: subversion/branches/ra-git/subversion/libsvn_subr/cmdline.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/cmdline.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/cmdline.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/cmdline.c Tue Oct 11 09:11:50 2016 @@ -42,6 +42,7 @@ #include <apr_general.h> /* for apr_initialize/apr_terminate */ #include <apr_strings.h> /* for apr_snprintf */ #include <apr_pools.h> +#include <apr_signal.h> #include "svn_cmdline.h" #include "svn_ctype.h" @@ -821,7 +822,7 @@ most_similar(const char *needle_cstr, apr_size_t haystack_len, apr_pool_t *scratch_pool) { - const char *max_similar; + const char *max_similar = NULL; apr_size_t max_score = 0; apr_size_t i; svn_membuf_t membuf; @@ -846,10 +847,7 @@ most_similar(const char *needle_cstr, } } - if (max_score) - return max_similar; - else - return NULL; + return max_similar; } /* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */ @@ -1209,11 +1207,7 @@ svn_cmdline__be_interactive(svn_boolean_ * If --force-interactive was passed, always be interactive. */ if (!force_interactive && !non_interactive) { -#ifdef WIN32 - return (_isatty(STDIN_FILENO) != 0); -#else - return (isatty(STDIN_FILENO) != 0); -#endif + return svn_cmdline__stdin_is_a_terminal(); } else if (force_interactive) return TRUE; @@ -1606,3 +1600,99 @@ svn_cmdline__parse_trust_options( return SVN_NO_ERROR; } + +/* Flags to see if we've been cancelled by the client or not. */ +static volatile sig_atomic_t cancelled = FALSE; +static volatile sig_atomic_t signum_cancelled = 0; + +/* The signals we handle. */ +static int signal_map [] = { + SIGINT +#ifdef SIGBREAK + /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ + , SIGBREAK +#endif +#ifdef SIGHUP + , SIGHUP +#endif +#ifdef SIGTERM + , SIGTERM +#endif +}; + +/* A signal handler to support cancellation. */ +static void +signal_handler(int signum) +{ + int i; + + apr_signal(signum, SIG_IGN); + cancelled = TRUE; + for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i) + if (signal_map[i] == signum) + { + signum_cancelled = i + 1; + break; + } +} + +/* An svn_cancel_func_t callback. */ +static svn_error_t * +check_cancel(void *baton) +{ + /* Cancel baton should be always NULL in command line client. */ + SVN_ERR_ASSERT(baton == NULL); + if (cancelled) + return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); + else + return SVN_NO_ERROR; +} + +svn_cancel_func_t +svn_cmdline__setup_cancellation_handler(void) +{ + int i; + + for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i) + apr_signal(signal_map[i], signal_handler); + +#ifdef SIGPIPE + /* Disable SIGPIPE generation for the platforms that have it. */ + apr_signal(SIGPIPE, SIG_IGN); +#endif + +#ifdef SIGXFSZ + /* Disable SIGXFSZ generation for the platforms that have it, otherwise + * working with large files when compiled against an APR that doesn't have + * large file support will crash the program, which is uncool. */ + apr_signal(SIGXFSZ, SIG_IGN); +#endif + + return check_cancel; +} + +void +svn_cmdline__disable_cancellation_handler(void) +{ + int i; + + for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i) + apr_signal(signal_map[i], SIG_DFL); +} + +void +svn_cmdline__cancellation_exit(void) +{ + int signum = 0; + + if (cancelled && signum_cancelled) + signum = signal_map[signum_cancelled - 1]; + if (signum) + { +#ifndef WIN32 + apr_signal(signum, SIG_DFL); + /* No APR support for getpid() so cannot use apr_proc_kill(). */ + kill(getpid(), signum); +#endif + } +} Modified: subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c Tue Oct 11 09:11:50 2016 @@ -1521,7 +1521,11 @@ void svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool) { +#ifdef SVN_HAVE_KEYCHAIN_SERVICES svn_auth__get_keychain_simple_provider(provider, pool); +#else + svn_auth__get_dummmy_simple_provider(provider, pool); +#endif } void @@ -1529,7 +1533,13 @@ svn_auth_get_keychain_ssl_client_cert_pw (svn_auth_provider_object_t **provider, apr_pool_t *pool) { +#ifdef SVN_HAVE_KEYCHAIN_SERVICES svn_auth__get_keychain_ssl_client_cert_pw_provider(provider, pool); +#else + /* Not really the right type of dummy provider, but doesn't throw NULL + errors as just returning NULL would */ + svn_auth__get_dummmy_simple_provider(provider, pool); +#endif } #endif /* DARWIN */ Modified: subversion/branches/ra-git/subversion/libsvn_subr/eol.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/eol.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/eol.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/eol.c Tue Oct 11 09:11:50 2016 @@ -33,43 +33,32 @@ char * svn_eol__find_eol_start(char *buf, apr_size_t len) { -#if !SVN_UNALIGNED_ACCESS_IS_OK - - /* On some systems, we need to make sure that BUF is properly aligned - * for chunky data access. This overhead is still justified because - * only lines tend to be tens of chars long. - */ - for (; (len > 0) && ((apr_uintptr_t)buf) & (sizeof(apr_uintptr_t)-1) - ; ++buf, --len) - { - if (*buf == '\n' || *buf == '\r') - return buf; - } - -#endif +#if SVN_UNALIGNED_ACCESS_IS_OK /* Scan the input one machine word at a time. */ for (; len > sizeof(apr_uintptr_t) ; buf += sizeof(apr_uintptr_t), len -= sizeof(apr_uintptr_t)) - { - /* This is a variant of the well-known strlen test: */ - apr_uintptr_t chunk = *(const apr_uintptr_t *)buf; - - /* A byte in SVN__R_TEST is \0, iff it was \r in *BUF. - * Similarly, SVN__N_TEST is an indicator for \n. */ - apr_uintptr_t r_test = chunk ^ SVN__R_MASK; - apr_uintptr_t n_test = chunk ^ SVN__N_MASK; - - /* A byte in SVN__R_TEST can only be < 0x80, iff it has been \0 before - * (i.e. \r in *BUF). Ditto for SVN__N_TEST. */ - r_test |= (r_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; - n_test |= (n_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; - - /* Check whether at least one of the words contains a byte <0x80 - * (if one is detected, there was a \r or \n in CHUNK). */ - if ((r_test & n_test & SVN__BIT_7_SET) != SVN__BIT_7_SET) - break; - } + { + /* This is a variant of the well-known strlen test: */ + apr_uintptr_t chunk = *(const apr_uintptr_t *)buf; + + /* A byte in SVN__R_TEST is \0, iff it was \r in *BUF. + * Similarly, SVN__N_TEST is an indicator for \n. */ + apr_uintptr_t r_test = chunk ^ SVN__R_MASK; + apr_uintptr_t n_test = chunk ^ SVN__N_MASK; + + /* A byte in SVN__R_TEST can only be < 0x80, iff it has been \0 before + * (i.e. \r in *BUF). Ditto for SVN__N_TEST. */ + r_test |= (r_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; + n_test |= (n_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; + + /* Check whether at least one of the words contains a byte <0x80 + * (if one is detected, there was a \r or \n in CHUNK). */ + if ((r_test & n_test & SVN__BIT_7_SET) != SVN__BIT_7_SET) + break; + } + +#endif /* The remaining odd bytes will be examined the naive way: */ for (; len > 0; ++buf, --len) Modified: subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c Tue Oct 11 09:11:50 2016 @@ -103,6 +103,40 @@ escape_blanks(char *str) return str; } +#define is_hex(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F')) +#define hex_to_int(c) ((c) < '9' ? (c) - '0' : (c) - 'A' + 10) + +/* Modify STR in-place. '%', CR and LF are always percent escaped, + other characters may be percent escaped, always using uppercase + hex, see https://www.gnupg.org/documentation/manuals/assuan.pdf */ +static char * +unescape_assuan(char *str) +{ + char *s = str; + + while (s[0]) + { + if (s[0] == '%' && is_hex(s[1]) && is_hex(s[2])) + { + char *s2 = s; + char val = hex_to_int(s[1]) * 16 + hex_to_int(s[2]); + + s2[0] = val; + ++s2; + + while (s2[2]) + { + s2[0] = s2[2]; + ++s2; + } + s2[0] = '\0'; + } + ++s; + } + + 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. */ @@ -378,7 +412,7 @@ password_get_gpg_agent(svn_boolean_t *do apr_pool_t *pool) { int sd; - const char *p = NULL; + char *p = NULL; char *ep = NULL; char *buffer; const char *request = NULL; @@ -451,7 +485,7 @@ password_get_gpg_agent(svn_boolean_t *do if (ep != NULL) *ep = '\0'; - *password = p; + *password = unescape_assuan(p); *done = TRUE; return SVN_NO_ERROR; Modified: subversion/branches/ra-git/subversion/libsvn_subr/io.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/io.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/io.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/io.c Tue Oct 11 09:11:50 2016 @@ -1478,18 +1478,14 @@ svn_io_file_checksum2(svn_checksum_t **c apr_pool_t *pool) { svn_stream_t *file_stream; - svn_stream_t *checksum_stream; apr_file_t* f; SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool)); file_stream = svn_stream_from_aprfile2(f, FALSE, pool); - checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind, - TRUE, pool); + SVN_ERR(svn_stream_contents_checksum(checksum, file_stream, kind, + pool, pool)); - /* Because the checksummed stream will force the reading (and - checksumming) of all the file's bytes, we can just close the stream - and let its magic work. */ - return svn_stream_close(checksum_stream); + return SVN_NO_ERROR; } @@ -1568,8 +1564,14 @@ get_default_file_perms(apr_fileperms_t * Using svn_io_open_uniquely_named() here because other tempfile creation functions tweak the permission bits of files they create. + + Note that APR pool structures are allocated as the first item + in their first memory page (with e.g. 4kB granularity), i.e. the + lower bits tend to be identical between pool instances. That is + particularly true for the MMAPed allocator. */ randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool + + (apr_uint32_t)((apr_uintptr_t)scratch_pool / 4096) + (apr_uint32_t)apr_time_now()); fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish); @@ -1888,7 +1890,7 @@ io_win_file_attrs_set(const char *fname, static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool) { - HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); if (kernel32) { @@ -2649,9 +2651,6 @@ svn_io_remove_file2(const char *path, allow us to delete when path is read-only */ SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool)); apr_err = apr_file_remove(path_apr, scratch_pool); - - if (!apr_err) - return SVN_NO_ERROR; } /* Check to make sure we aren't trying to delete a directory */ @@ -4065,6 +4064,26 @@ svn_io_write_atomic2(const char *final_p svn_error_t * svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool) { + /* Workaround for yet another APR issue with trunc. + + If the APR file internally is in read mode, the current buffer pointer + will not be clipped to the valid data range. get_file_offset may then + return an invalid position *after* new data was written to it. + + To prevent this, write 1 dummy byte just after the OFFSET at which we + will trunc it. That will force the APR file into write mode + internally and the flush() work-around below becomes affective. */ + apr_off_t position = 0; + + /* A frequent usage is OFFSET==0, in which case we don't need to preserve + any file content or file pointer. */ + if (offset) + { + SVN_ERR(svn_io_file_seek(file, APR_CUR, &position, pool)); + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); + } + SVN_ERR(svn_io_file_putc(0, file, pool)); + /* This is a work-around. APR would flush the write buffer _after_ truncating the file causing now invalid buffered data to be written behind OFFSET. */ @@ -4073,10 +4092,17 @@ svn_io_file_trunc(apr_file_t *file, apr_ N_("Can't flush stream"), pool)); - return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset), - N_("Can't truncate file '%s'"), - N_("Can't truncate stream"), - pool); + SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset), + N_("Can't truncate file '%s'"), + N_("Can't truncate stream"), + pool)); + + /* Restore original file pointer, if necessary. + It's currently at OFFSET. */ + if (position < offset) + SVN_ERR(svn_io_file_seek(file, APR_SET, &position, pool)); + + return SVN_NO_ERROR; } Modified: subversion/branches/ra-git/subversion/libsvn_subr/packed_data.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/packed_data.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/packed_data.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/packed_data.c Tue Oct 11 09:11:50 2016 @@ -676,6 +676,12 @@ svn_packed__byte_count(svn_packed__byte_ return stream->packed->len; } +apr_size_t +svn_packed__byte_block_count(svn_packed__byte_stream_t *stream) +{ + return svn_packed__int_count(stream->lengths_stream); +} + /* Read one 7b/8b encoded value from *P and return it in *RESULT. Returns * the first position after the parsed data. * Modified: subversion/branches/ra-git/subversion/libsvn_subr/path.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/path.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/path.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/path.c Tue Oct 11 09:11:50 2016 @@ -506,7 +506,7 @@ get_path_ancestor_length(const char *pat else if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/') return 1; - return last_dirsep; + return last_dirsep; } Modified: subversion/branches/ra-git/subversion/libsvn_subr/pool.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/pool.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/pool.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/pool.c Tue Oct 11 09:11:50 2016 @@ -54,6 +54,13 @@ abort_on_pool_failure(int retcode) /* Don't translate this string! It requires memory allocation to do so! And we don't have any of it... */ printf("libsvn: Out of memory - terminating application.\n"); + +#ifdef WIN32 + /* Provide a way to distinguish the out-of-memory error from abort(). */ + if (retcode == APR_ENOMEM) + RaiseException(STATUS_NO_MEMORY, EXCEPTION_NONCONTINUABLE, 0, NULL); +#endif + abort(); return 0; /* not reached */ } Modified: subversion/branches/ra-git/subversion/libsvn_subr/prefix_string.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/prefix_string.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/prefix_string.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/prefix_string.c Tue Oct 11 09:11:50 2016 @@ -49,7 +49,12 @@ struct svn_prefix_string__t /* mandatory prefix */ node_t *prefix; - /* 0 ..7 chars to add the the prefix. NUL-terminated. */ + /* 0 ..7 chars to add the the prefix. + * + * NUL-terminated, if this is indeed a tree leaf. We use the same struct + * within node_t for inner tree nodes, too. There, DATA[7] is not NUL, + * meaning DATA may or may not be NUL terminated. The actual length is + * provided by the node_t.length field (minus parent node length). */ char data[8]; }; @@ -94,6 +99,12 @@ struct svn_prefix_tree__t static svn_boolean_t is_leaf(node_t *node) { + /* If this NOT a leaf node and this node has ... + * ... 8 chars, data[7] will not be NUL because we only support + * NUL-*terminated* strings. + * ... less than 8 chars, this will be set to 0xff + * (any other non-NUL would do as well but this is not valid UTF8 + * making it easy to recognize during debugging etc.) */ return node->key.data[7] == 0; } @@ -154,7 +165,7 @@ svn_prefix_tree__create(apr_pool_t *pool tree->pool = pool; tree->root = apr_pcalloc(pool, sizeof(*tree->root)); - tree->root->key.data[7] = '\xff'; + tree->root->key.data[7] = '\xff'; /* This is not a leaf. See is_leaf(). */ return tree; } @@ -188,6 +199,7 @@ svn_prefix_string__create(svn_prefix_tre || node->sub_nodes[idx]->key.data[0] != s[node->length]) break; + /* Yes, it matches - at least the first character does. */ sub_node = node->sub_nodes[idx]; /* fully matching sub-node? */ @@ -198,6 +210,11 @@ svn_prefix_string__create(svn_prefix_tre } else { + /* The string formed by the path from the root down to + * SUB_NODE differs from S. + * + * Is it a prefix? In that case, the chars added by SUB_NODE + * must fully match the respective chars in S. */ apr_size_t sub_node_len = sub_node->length - node->length; if (strncmp(sub_node->key.data, s + node->length, sub_node_len) == 0) @@ -207,14 +224,24 @@ svn_prefix_string__create(svn_prefix_tre } } - /* partial match -> split */ + /* partial match -> split + * + * At this point, S may either be a prefix to the string represented + * by SUB_NODE, or they may diverge at some offset with + * SUB_NODE->KEY.DATA . + * + * MATCH starts with 1 here b/c we already know that at least one + * char matches. Also, the loop will terminate because the strings + * differ before SUB_NODE->KEY.DATA - either at the NUL terminator + * of S or some char before that. + */ while (sub_node->key.data[match] == s[node->length + match]) ++match; new_node = apr_pcalloc(tree->pool, sizeof(*new_node)); new_node->key = sub_node->key; new_node->length = node->length + match; - new_node->key.data[7] = '\xff'; + new_node->key.data[7] = '\xff'; /* This is not a leaf. See is_leaf(). */ new_node->sub_node_count = 1; new_node->sub_nodes = apr_palloc(tree->pool, sizeof(node_t *)); new_node->sub_nodes[0] = sub_node; Modified: subversion/branches/ra-git/subversion/libsvn_subr/prompt.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/prompt.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/prompt.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/prompt.c Tue Oct 11 09:11:50 2016 @@ -814,6 +814,8 @@ plaintext_prompt_helper(svn_boolean_t *m const char *config_path = NULL; terminal_handle_t *terminal; + *may_save_plaintext = FALSE; /* de facto API promise */ + if (pb) SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir, SVN_CONFIG_CATEGORY_SERVERS, pool)); @@ -826,18 +828,7 @@ plaintext_prompt_helper(svn_boolean_t *m do { - svn_error_t *err = prompt(&answer, prompt_string, FALSE, pb, pool); - if (err) - { - if (err->apr_err == SVN_ERR_CANCELLED) - { - svn_error_clear(err); - *may_save_plaintext = FALSE; - return SVN_NO_ERROR; - } - else - return err; - } + SVN_ERR(prompt(&answer, prompt_string, FALSE, pb, pool)); if (apr_strnatcasecmp(answer, _("yes")) == 0 || apr_strnatcasecmp(answer, _("y")) == 0) { Modified: subversion/branches/ra-git/subversion/libsvn_subr/sqlite.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/sqlite.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/sqlite.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/sqlite.c Tue Oct 11 09:11:50 2016 @@ -213,13 +213,6 @@ struct svn_sqlite__value_t sqlite_err__temp, msg); \ } while (0) -#define SVN_ERR_CLOSE(x, db) do \ -{ \ - svn_error_t *svn__err = (x); \ - if (svn__err) \ - return svn_error_compose_create(svn__err, svn_sqlite__close(db)); \ -} while (0) - /* Time (in milliseconds) to wait for sqlite locks before giving up. */ #define BUSY_TIMEOUT 10000 @@ -1141,7 +1134,7 @@ svn_sqlite__open(svn_sqlite__db_t **db, sqlite3_profile((*db)->db3, sqlite_profiler, (*db)->db3); #endif - SVN_ERR_CLOSE(exec_sql(*db, + SVN_SQLITE__ERR_CLOSE(exec_sql(*db, /* The default behavior of the LIKE operator is to ignore case for ASCII characters. Hence, by default 'a' LIKE 'A' is true. The case_sensitive_like pragma installs a new application- @@ -1180,8 +1173,8 @@ svn_sqlite__open(svn_sqlite__db_t **db, /* When running in debug mode, enable the checking of foreign key constraints. This has possible performance implications, so we don't bother to do it for production...for now. */ - SVN_ERR_CLOSE(exec_sql(*db, "PRAGMA foreign_keys=ON;"), - *db); + SVN_SQLITE__ERR_CLOSE(exec_sql(*db, "PRAGMA foreign_keys=ON;"), + *db); #endif #ifdef SVN_SQLITE_REVERSE_UNORDERED_SELECTS @@ -1189,8 +1182,8 @@ svn_sqlite__open(svn_sqlite__db_t **db, clause to emit their results in the reverse order of what they normally would. This can help detecting invalid assumptions about the result order.*/ - SVN_ERR_CLOSE(exec_sql(*db, "PRAGMA reverse_unordered_selects=ON;"), - *db); + SVN_SQLITE__ERR_CLOSE(exec_sql(*db, "PRAGMA reverse_unordered_selects=ON;"), + *db); #endif /* Store temporary tables in RAM instead of in temporary files, but don't @@ -1261,6 +1254,54 @@ reset_all_statements(svn_sqlite__db_t *d return err; } +static svn_error_t * +rollback_transaction(svn_sqlite__db_t *db, + svn_error_t *error_to_wrap) +{ + svn_sqlite__stmt_t *stmt; + svn_error_t *err; + + err = get_internal_statement(&stmt, db, STMT_INTERNAL_ROLLBACK_TRANSACTION); + if (!err) + { + err = svn_error_trace(svn_sqlite__step_done(stmt)); + + if (err && err->apr_err == SVN_ERR_SQLITE_BUSY) + { + /* ### Houston, we have a problem! + + We are trying to rollback but we can't because some + statements are still busy. This leaves the database + unusable for future transactions as the current transaction + is still open. + + As we are returning the actual error as the most relevant + error in the chain, our caller might assume that it can + retry/compensate on this error (e.g. SVN_WC_LOCKED), while + in fact the SQLite database is unusable until the statements + started within this transaction are reset and the transaction + aborted. + + We try to compensate by resetting all prepared but unreset + statements; but we leave the busy error in the chain anyway to + help diagnosing the original error and help in finding where + a reset statement is missing. */ + err = svn_error_trace(reset_all_statements(db, err)); + err = svn_error_compose_create( + svn_error_trace(svn_sqlite__step_done(stmt)), + err); + } + } + + if (err) + { + /* Rollback failed, use a specific error code. */ + err = svn_error_create(SVN_ERR_SQLITE_ROLLBACK_FAILED, err, NULL); + } + + return svn_error_compose_create(error_to_wrap, err); +} + svn_error_t * svn_sqlite__begin_transaction(svn_sqlite__db_t *db) { @@ -1303,46 +1344,37 @@ svn_sqlite__finish_transaction(svn_sqlit /* Commit or rollback the sqlite transaction. */ if (err) { - svn_error_t *err2; - - err2 = get_internal_statement(&stmt, db, - STMT_INTERNAL_ROLLBACK_TRANSACTION); - if (!err2) - err2 = svn_error_trace(svn_sqlite__step_done(stmt)); - - if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) - { - /* ### Houston, we have a problem! - - We are trying to rollback but we can't because some - statements are still busy. This leaves the database - unusable for future transactions as the current transaction - is still open. - - As we are returning the actual error as the most relevant - error in the chain, our caller might assume that it can - retry/compensate on this error (e.g. SVN_WC_LOCKED), while - in fact the SQLite database is unusable until the statements - started within this transaction are reset and the transaction - aborted. - - We try to compensate by resetting all prepared but unreset - statements; but we leave the busy error in the chain anyway to - help diagnosing the original error and help in finding where - a reset statement is missing. */ - - err2 = svn_error_trace(reset_all_statements(db, err2)); - err2 = svn_error_compose_create( - svn_error_trace(svn_sqlite__step_done(stmt)), - err2); - - } - - return svn_error_compose_create(err, err2); + return svn_error_trace(rollback_transaction(db, err)); + } + else + { + err = get_internal_statement(&stmt, db, + STMT_INTERNAL_COMMIT_TRANSACTION); + if (!err) + err = svn_error_trace(svn_sqlite__step_done(stmt)); + + /* Need to rollback if the commit fails as well, because otherwise the + db connection will be left in an unusable state. + + One important case to keep in mind is trying to COMMIT with concurrent + readers. In case the commit fails, because someone else is holding a + shared lock, sqlite keeps the transaction, and *also* keeps the file + locks on the database. While the first part only prevents from using + this connection, the second part prevents everyone else from accessing + the database while the connection is open. + + See https://www.sqlite.org/lang_transaction.html + + COMMIT might also result in an SQLITE_BUSY return code if an another + thread or process has a shared lock on the database that prevented + the database from being updated. When COMMIT fails in this way, the + transaction remains active and the COMMIT can be retried later after + the reader has had a chance to clear. */ + if (err) + return svn_error_trace(rollback_transaction(db, err)); } - SVN_ERR(get_internal_statement(&stmt, db, STMT_INTERNAL_COMMIT_TRANSACTION)); - return svn_error_trace(svn_sqlite__step_done(stmt)); + return SVN_NO_ERROR; } svn_error_t * @@ -1359,20 +1391,22 @@ svn_sqlite__finish_savepoint(svn_sqlite_ STMT_INTERNAL_ROLLBACK_TO_SAVEPOINT_SVN); if (!err2) - err2 = svn_error_trace(svn_sqlite__step_done(stmt)); - - if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) { - /* Ok, we have a major problem. Some statement is still open, which - makes it impossible to release this savepoint. + err2 = svn_error_trace(svn_sqlite__step_done(stmt)); - ### See huge comment in svn_sqlite__finish_transaction for - further details */ + if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) + { + /* Ok, we have a major problem. Some statement is still open, + which makes it impossible to release this savepoint. - err2 = svn_error_trace(reset_all_statements(db, err2)); - err2 = svn_error_compose_create( - svn_error_trace(svn_sqlite__step_done(stmt)), - err2); + ### See huge comment in rollback_transaction() for + further details */ + + err2 = svn_error_trace(reset_all_statements(db, err2)); + err2 = svn_error_compose_create( + svn_error_trace(svn_sqlite__step_done(stmt)), + err2); + } } err = svn_error_compose_create(err, err2); @@ -1388,6 +1422,8 @@ svn_sqlite__finish_savepoint(svn_sqlite_ SVN_ERR(get_internal_statement(&stmt, db, STMT_INTERNAL_RELEASE_SAVEPOINT_SVN)); + /* ### Releasing a savepoint can fail and leave the db connection + unusable; see svn_sqlite__finish_transaction(). */ return svn_error_trace(svn_sqlite__step_done(stmt)); } Modified: subversion/branches/ra-git/subversion/libsvn_subr/stream.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/stream.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/stream.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/stream.c Tue Oct 11 09:11:50 2016 @@ -61,7 +61,7 @@ struct svn_stream_t { svn_stream_mark_fn_t mark_fn; svn_stream_seek_fn_t seek_fn; svn_stream_data_available_fn_t data_available_fn; - svn_stream__is_buffered_fn_t is_buffered_fn; + svn_stream_readline_fn_t readline_fn; apr_file_t *file; /* Maybe NULL */ }; @@ -139,10 +139,10 @@ svn_stream_set_data_available(svn_stream } void -svn_stream__set_is_buffered(svn_stream_t *stream, - svn_stream__is_buffered_fn_t is_buffered_fn) +svn_stream_set_readline(svn_stream_t *stream, + svn_stream_readline_fn_t readline_fn) { - stream->is_buffered_fn = is_buffered_fn; + stream->readline_fn = readline_fn; } /* Standard implementation for svn_stream_read_full() based on @@ -265,15 +265,6 @@ svn_stream_data_available(svn_stream_t * data_available)); } -svn_boolean_t -svn_stream__is_buffered(svn_stream_t *stream) -{ - if (stream->is_buffered_fn == NULL) - return FALSE; - - return stream->is_buffered_fn(stream->baton); -} - svn_error_t * svn_stream_close(svn_stream_t *stream) { @@ -328,11 +319,9 @@ svn_stream_printf_from_utf8(svn_stream_t return svn_error_trace(svn_stream_puts(stream, translated)); } -/* Guts of svn_stream_readline(). +/* Default implementation for svn_stream_readline(). * Returns the line read from STREAM in *STRINGBUF, and indicates - * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator - * is detected automatically and returned in *EOL. - * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line + * end-of-file in *EOF. EOL must point to the desired end-of-line * indicator. STRINGBUF is allocated in POOL. */ static svn_error_t * stream_readline_bytewise(svn_stringbuf_t **stringbuf, @@ -381,161 +370,27 @@ stream_readline_bytewise(svn_stringbuf_t return SVN_NO_ERROR; } -static svn_error_t * -stream_readline_chunky(svn_stringbuf_t **stringbuf, - svn_boolean_t *eof, - const char *eol, - svn_stream_t *stream, - apr_pool_t *pool) +svn_error_t * +svn_stream_readline(svn_stream_t *stream, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) { - /* Read larger chunks of data at once into this buffer and scan - * that for EOL. A good chunk size should be about 80 chars since - * most text lines will be shorter. However, don't use a much - * larger value because filling the buffer from the stream takes - * time as well. - */ - char buffer[SVN__LINE_CHUNK_SIZE+1]; - - /* variables */ - svn_stream_mark_t *mark; - apr_size_t numbytes; - const char *eol_pos; - apr_size_t total_parsed = 0; - - /* invariant for this call */ - const size_t eol_len = strlen(eol); - - /* Remember the line start so this plus the line length will be - * the position to move to at the end of this function. - */ - SVN_ERR(svn_stream_mark(stream, &mark, pool)); - - /* Read the first chunk. */ - numbytes = SVN__LINE_CHUNK_SIZE; - SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes)); - buffer[numbytes] = '\0'; - - /* Look for the EOL in this first chunk. If we find it, we are done here. - */ - eol_pos = strstr(buffer, eol); - if (eol_pos != NULL) - { - *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool); - total_parsed = eol_pos - buffer + eol_len; - } - else if (numbytes < SVN__LINE_CHUNK_SIZE) - { - /* We hit EOF but not EOL. - */ - *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool); - *eof = TRUE; - return SVN_NO_ERROR; - } - else + if (stream->readline_fn) { - /* A larger buffer for the string is needed. */ - svn_stringbuf_t *str; - str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool); - svn_stringbuf_appendbytes(str, buffer, numbytes); - *stringbuf = str; - - /* Loop reading chunks until an EOL was found. If we hit EOF, fall - * back to the standard implementation. */ - do - { - /* Append the next chunk to the string read so far. - */ - svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE); - numbytes = SVN__LINE_CHUNK_SIZE; - SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes)); - str->len += numbytes; - str->data[str->len] = '\0'; - - /* Look for the EOL in the new data plus the last part of the - * previous chunk because the EOL may span over the boundary - * between both chunks. - */ - eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol); - - if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL)) - { - /* We hit EOF instead of EOL. */ - *eof = TRUE; - return SVN_NO_ERROR; - } - } - while (eol_pos == NULL); - - /* Number of bytes we actually consumed (i.e. line + EOF). - * We need to "return" the rest to the stream by moving its - * read pointer. - */ - total_parsed = eol_pos - str->data + eol_len; - - /* Terminate the string at the EOL postion and return it. */ - str->len = eol_pos - str->data; - str->data[str->len] = 0; - } - - /* Move the stream read pointer to the first position behind the EOL. - */ - SVN_ERR(svn_stream_seek(stream, mark)); - return svn_error_trace(svn_stream_skip(stream, total_parsed)); -} - -/* Guts of svn_stream_readline(). - * Returns the line read from STREAM in *STRINGBUF, and indicates - * end-of-file in *EOF. EOL must point to the desired end-of-line - * indicator. STRINGBUF is allocated in POOL. */ -static svn_error_t * -stream_readline(svn_stringbuf_t **stringbuf, - svn_boolean_t *eof, - const char *eol, - svn_stream_t *stream, - apr_pool_t *pool) -{ - *eof = FALSE; - - /* Often, we operate on APR file or string-based streams and know what - * EOL we are looking for. Optimize that common case. - */ - if (svn_stream_supports_mark(stream) && - svn_stream__is_buffered(stream)) - { - /* We can efficiently read chunks speculatively and reposition the - * stream pointer to the end of the line once we found that. - */ - SVN_ERR(stream_readline_chunky(stringbuf, - eof, - eol, - stream, - pool)); + /* Use the specific implementation when it's available. */ + SVN_ERR(stream->readline_fn(stream->baton, stringbuf, eol, eof, pool)); } else { - /* Use the standard byte-byte implementation. - */ - SVN_ERR(stream_readline_bytewise(stringbuf, - eof, - eol, - stream, - pool)); + /* Use the default implementation. */ + SVN_ERR(stream_readline_bytewise(stringbuf, eof, eol, stream, pool)); } return SVN_NO_ERROR; } -svn_error_t * -svn_stream_readline(svn_stream_t *stream, - svn_stringbuf_t **stringbuf, - const char *eol, - svn_boolean_t *eof, - apr_pool_t *pool) -{ - return svn_error_trace(stream_readline(stringbuf, eof, eol, stream, - pool)); -} - svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to, svn_cancel_func_t cancel_func, void *cancel_baton, @@ -664,11 +519,6 @@ seek_handler_empty(void *baton, const sv return SVN_NO_ERROR; } -static svn_boolean_t -is_buffered_handler_empty(void *baton) -{ - return FALSE; -} svn_stream_t * @@ -681,7 +531,6 @@ svn_stream_empty(apr_pool_t *pool) svn_stream_set_write(stream, write_handler_empty); svn_stream_set_mark(stream, mark_handler_empty); svn_stream_set_seek(stream, seek_handler_empty); - svn_stream__set_is_buffered(stream, is_buffered_handler_empty); return stream; } @@ -788,10 +637,15 @@ data_available_disown(void *baton, svn_b return svn_error_trace(svn_stream_data_available(baton, data_available)); } -static svn_boolean_t -is_buffered_handler_disown(void *baton) +static svn_error_t * +readline_handler_disown(void *baton, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) { - return svn_stream__is_buffered(baton); + return svn_error_trace(svn_stream_readline(baton, stringbuf, eol, + eof, pool)); } svn_stream_t * @@ -805,7 +659,7 @@ svn_stream_disown(svn_stream_t *stream, svn_stream_set_mark(s, mark_handler_disown); svn_stream_set_seek(s, seek_handler_disown); svn_stream_set_data_available(s, data_available_disown); - svn_stream__set_is_buffered(s, is_buffered_handler_disown); + svn_stream_set_readline(s, readline_handler_disown); return s; } @@ -997,11 +851,146 @@ data_available_handler_apr(void *baton, #endif } -static svn_boolean_t -is_buffered_handler_apr(void *baton) +static svn_error_t * +readline_apr_lf(apr_file_t *file, + svn_stringbuf_t **stringbuf, + svn_boolean_t *eof, + apr_pool_t *pool) +{ + svn_stringbuf_t *buf; + + buf = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool); + while (1) + { + apr_status_t status; + + status = apr_file_gets(buf->data + buf->len, + (int) (buf->blocksize - buf->len), + file); + buf->len += strlen(buf->data + buf->len); + + if (APR_STATUS_IS_EOF(status)) + { + /* apr_file_gets() keeps the newline; strip it if necessary. */ + if (buf->len > 0 && buf->data[buf->len - 1] == '\n') + svn_stringbuf_chop(buf, 1); + + *eof = TRUE; + *stringbuf = buf; + return SVN_NO_ERROR; + } + else if (status != APR_SUCCESS) + { + const char *fname; + svn_error_t *err = svn_io_file_name_get(&fname, file, pool); + if (err) + fname = NULL; + svn_error_clear(err); + + if (fname) + return svn_error_wrap_apr(status, + _("Can't read a line from file '%s'"), + svn_dirent_local_style(fname, pool)); + else + return svn_error_wrap_apr(status, + _("Can't read a line from stream")); + } + + /* Do we have the EOL? If yes, strip it and return. */ + if (buf->len > 0 && buf->data[buf->len - 1] == '\n') + { + svn_stringbuf_chop(buf, 1); + *eof = FALSE; + *stringbuf = buf; + return SVN_NO_ERROR; + } + + /* Otherwise, prepare to read the next chunk. */ + svn_stringbuf_ensure(buf, buf->blocksize + SVN__LINE_CHUNK_SIZE); + } +} + +static svn_error_t * +readline_apr_generic(apr_file_t *file, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) +{ + apr_size_t eol_len = strlen(eol); + apr_off_t offset; + svn_stringbuf_t *buf; + + SVN_ERR(svn_io_file_get_offset(&offset, file, pool)); + + buf = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool); + while (1) + { + apr_size_t bytes_read; + svn_boolean_t hit_eof; + const char *search_start; + const char *eol_pos; + + /* We look for the EOL in the new data plus the last part of the + previous chunk because the EOL may span over the boundary + between both chunks. */ + if (buf->len < eol_len) + search_start = buf->data; + else + search_start = buf->data + buf->len - eol_len; + + SVN_ERR(svn_io_file_read_full2(file, buf->data + buf->len, + buf->blocksize - buf->len - 1, + &bytes_read, &hit_eof, pool)); + buf->len += bytes_read; + buf->data[buf->len] = '\0'; + + /* Do we have the EOL now? */ + eol_pos = strstr(search_start, eol); + if (eol_pos) + { + svn_stringbuf_chop(buf, buf->data + buf->len - eol_pos); + /* Seek to the first position behind the EOL. */ + offset += (buf->len + eol_len); + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); + + *eof = FALSE; + *stringbuf = buf; + return SVN_NO_ERROR; + } + else if (eol_pos == NULL && hit_eof) + { + *eof = TRUE; + *stringbuf = buf; + return SVN_NO_ERROR; + } + + /* Prepare to read the next chunk. */ + svn_stringbuf_ensure(buf, buf->blocksize + SVN__LINE_CHUNK_SIZE); + } +} + +static svn_error_t * +readline_handler_apr(void *baton, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) { struct baton_apr *btn = baton; - return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0; + + if (eol[0] == '\n' && eol[1] == '\0') + { + /* Optimize the common case when we're looking for an LF ("\n") + end-of-line sequence by using apr_file_gets(). */ + return svn_error_trace(readline_apr_lf(btn->file, stringbuf, + eof, pool)); + } + else + { + return svn_error_trace(readline_apr_generic(btn->file, stringbuf, + eol, eof, pool)); + } } svn_error_t * @@ -1083,10 +1072,10 @@ make_stream_from_apr_file(apr_file_t *fi svn_stream_set_skip(stream, skip_handler_apr); svn_stream_set_mark(stream, mark_handler_apr); svn_stream_set_seek(stream, seek_handler_apr); + svn_stream_set_readline(stream, readline_handler_apr); } svn_stream_set_data_available(stream, data_available_handler_apr); - svn_stream__set_is_buffered(stream, is_buffered_handler_apr); stream->file = file; if (! disown) @@ -1478,6 +1467,51 @@ svn_stream_checksummed2(svn_stream_t *st return s; } +/* Helper for svn_stream_contents_checksum() to compute checksum of + * KIND of STREAM. This function doesn't close source stream. */ +static svn_error_t * +compute_stream_checksum(svn_checksum_t **checksum, + svn_stream_t *stream, + svn_checksum_kind_t kind, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_checksum_ctx_t *ctx = svn_checksum_ctx_create(kind, scratch_pool); + char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); + + while (1) + { + apr_size_t len = SVN__STREAM_CHUNK_SIZE; + + SVN_ERR(svn_stream_read_full(stream, buf, &len)); + + if (len > 0) + SVN_ERR(svn_checksum_update(ctx, buf, len)); + + if (len != SVN__STREAM_CHUNK_SIZE) + break; + } + SVN_ERR(svn_checksum_final(checksum, ctx, result_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream_contents_checksum(svn_checksum_t **checksum, + svn_stream_t *stream, + svn_checksum_kind_t kind, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + + err = compute_stream_checksum(checksum, stream, kind, + result_pool, scratch_pool); + + /* Close source stream in all cases. */ + return svn_error_compose_create(err, svn_stream_close(stream)); +} + /* Miscellaneous stream functions. */ /* @@ -1606,10 +1640,35 @@ data_available_handler_stringbuf(void *b return SVN_NO_ERROR; } -static svn_boolean_t -is_buffered_handler_stringbuf(void *baton) +static svn_error_t * +readline_handler_stringbuf(void *baton, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) { - return TRUE; + struct stringbuf_stream_baton *btn = baton; + const char *pos = btn->str->data + btn->amt_read; + const char *eol_pos; + + eol_pos = strstr(pos, eol); + if (eol_pos) + { + apr_size_t eol_len = strlen(eol); + + *eof = FALSE; + *stringbuf = svn_stringbuf_ncreate(pos, eol_pos - pos, pool); + btn->amt_read += (eol_pos - pos + eol_len); + } + else + { + *eof = TRUE; + *stringbuf = svn_stringbuf_ncreate(pos, btn->str->len - btn->amt_read, + pool); + btn->amt_read = btn->str->len; + } + + return SVN_NO_ERROR; } svn_stream_t * @@ -1632,7 +1691,7 @@ svn_stream_from_stringbuf(svn_stringbuf_ svn_stream_set_mark(stream, mark_handler_stringbuf); svn_stream_set_seek(stream, seek_handler_stringbuf); svn_stream_set_data_available(stream, data_available_handler_stringbuf); - svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf); + svn_stream_set_readline(stream, readline_handler_stringbuf); return stream; } @@ -1711,10 +1770,35 @@ data_available_handler_string(void *bato return SVN_NO_ERROR; } -static svn_boolean_t -is_buffered_handler_string(void *baton) +static svn_error_t * +readline_handler_string(void *baton, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) { - return TRUE; + struct string_stream_baton *btn = baton; + const char *pos = btn->str->data + btn->amt_read; + const char *eol_pos; + + eol_pos = strstr(pos, eol); + if (eol_pos) + { + apr_size_t eol_len = strlen(eol); + + *eof = FALSE; + *stringbuf = svn_stringbuf_ncreate(pos, eol_pos - pos, pool); + btn->amt_read += (eol_pos - pos + eol_len); + } + else + { + *eof = TRUE; + *stringbuf = svn_stringbuf_ncreate(pos, btn->str->len - btn->amt_read, + pool); + btn->amt_read = btn->str->len; + } + + return SVN_NO_ERROR; } svn_stream_t * @@ -1736,7 +1820,7 @@ svn_stream_from_string(const svn_string_ svn_stream_set_seek(stream, seek_handler_string); svn_stream_set_skip(stream, skip_handler_string); svn_stream_set_data_available(stream, data_available_handler_string); - svn_stream__set_is_buffered(stream, is_buffered_handler_string); + svn_stream_set_readline(stream, readline_handler_string); return stream; } @@ -1985,17 +2069,19 @@ data_available_handler_lazyopen(void *ba data_available)); } -/* Implements svn_stream__is_buffered_fn_t */ -static svn_boolean_t -is_buffered_lazyopen(void *baton) +/* Implements svn_stream_readline_fn_t */ +static svn_error_t * +readline_handler_lazyopen(void *baton, + svn_stringbuf_t **stringbuf, + const char *eol, + svn_boolean_t *eof, + apr_pool_t *pool) { lazyopen_baton_t *b = baton; - /* No lazy open as we cannot handle an open error. */ - if (!b->real_stream) - return FALSE; - - return svn_stream__is_buffered(b->real_stream); + SVN_ERR(lazyopen_if_unopened(b)); + return svn_error_trace(svn_stream_readline(b->real_stream, stringbuf, + eol, eof, pool)); } svn_stream_t * @@ -2022,7 +2108,7 @@ svn_stream_lazyopen_create(svn_stream_la svn_stream_set_mark(stream, mark_handler_lazyopen); svn_stream_set_seek(stream, seek_handler_lazyopen); svn_stream_set_data_available(stream, data_available_handler_lazyopen); - svn_stream__set_is_buffered(stream, is_buffered_lazyopen); + svn_stream_set_readline(stream, readline_handler_lazyopen); return stream; } Modified: subversion/branches/ra-git/subversion/libsvn_subr/string.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/string.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/string.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/string.c Tue Oct 11 09:11:50 2016 @@ -479,7 +479,7 @@ svn_stringbuf_set(svn_stringbuf_t *str, { apr_size_t amt = strlen(value); - svn_stringbuf_ensure(str, amt); + membuf_ensure((void**) &str->data, &str->blocksize, amt + 1, str->pool); memcpy(str->data, value, amt + 1); str->len = amt; } Modified: subversion/branches/ra-git/subversion/libsvn_subr/subst.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/subst.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/subst.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/subst.c Tue Oct 11 09:11:50 2016 @@ -1431,14 +1431,6 @@ translated_stream_seek(void *baton, cons return SVN_NO_ERROR; } -/* Implements svn_stream__is_buffered_fn_t. */ -static svn_boolean_t -translated_stream_is_buffered(void *baton) -{ - struct translated_stream_baton *b = baton; - return svn_stream__is_buffered(b->stream); -} - svn_error_t * svn_subst_read_specialfile(svn_stream_t **stream, const char *path, @@ -1546,9 +1538,11 @@ stream_translated(svn_stream_t *stream, translated_stream_read); svn_stream_set_write(s, translated_stream_write); svn_stream_set_close(s, translated_stream_close); - svn_stream_set_mark(s, translated_stream_mark); - svn_stream_set_seek(s, translated_stream_seek); - svn_stream__set_is_buffered(s, translated_stream_is_buffered); + if (svn_stream_supports_mark(stream)) + { + svn_stream_set_mark(s, translated_stream_mark); + svn_stream_set_seek(s, translated_stream_seek); + } return s; } Modified: subversion/branches/ra-git/subversion/libsvn_subr/sysinfo.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/sysinfo.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/sysinfo.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/sysinfo.c Tue Oct 11 09:11:50 2016 @@ -666,7 +666,7 @@ system_info(SYSTEM_INFO *sysinfo, SYSTEM_INFO *local_sysinfo) { FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO) - GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo"); + GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetNativeSystemInfo"); memset(sysinfo, 0, sizeof *sysinfo); if (local_sysinfo) @@ -875,12 +875,12 @@ enum_loaded_modules(apr_pool_t *pool) DWORD size; FNENUMPROCESSMODULES EnumProcessModules_; - psapi_dll = GetModuleHandleA("psapi.dll"); + psapi_dll = GetModuleHandleW(L"psapi.dll"); if (!psapi_dll) { /* Load and never unload, just like static linking */ - psapi_dll = LoadLibraryA("psapi.dll"); + psapi_dll = LoadLibraryW(L"psapi.dll"); } if (!psapi_dll) @@ -1143,6 +1143,8 @@ release_name_from_version(const char *os case 8: return "Mountain Lion"; case 9: return "Mavericks"; case 10: return "Yosemite"; + case 11: return "El Capitan"; + case 12: return "Sierra"; } return NULL; Modified: subversion/branches/ra-git/subversion/libsvn_subr/utf8proc.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/utf8proc.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_subr/utf8proc.c (original) +++ subversion/branches/ra-git/subversion/libsvn_subr/utf8proc.c Tue Oct 11 09:11:50 2016 @@ -126,15 +126,30 @@ decompose_normalized(apr_size_t *result_ * STRING. Upon return, BUFFER->data points at a NUL-terminated string * of UTF-8 characters. * + * If CASEFOLD is non-zero, perform Unicode case folding, e.g., for + * case-insensitive string comparison. If STRIPMARK is non-zero, strip + * all diacritical marks (e.g., accents) from the string. + * * A returned error may indicate that STRING contains invalid UTF-8 or * invalid Unicode codepoints. Any error message comes from utf8proc. */ static svn_error_t * normalize_cstring(apr_size_t *result_length, const char *string, apr_size_t length, + svn_boolean_t casefold, + svn_boolean_t stripmark, svn_membuf_t *buffer) { - ssize_t result = unicode_decomposition(0, string, length, buffer); + int flags = 0; + ssize_t result; + + if (casefold) + flags |= UTF8PROC_CASEFOLD; + + if (stripmark) + flags |= UTF8PROC_STRIPMARK; + + result = unicode_decomposition(flags, string, length, buffer); if (result >= 0) { svn_membuf__resize(buffer, result * sizeof(apr_int32_t) + 1); @@ -202,7 +217,21 @@ svn_utf__normalize(const char **result, svn_membuf_t *buf) { apr_size_t result_length; - SVN_ERR(normalize_cstring(&result_length, str, len, buf)); + SVN_ERR(normalize_cstring(&result_length, str, len, FALSE, FALSE, buf)); + *result = (const char*)(buf->data); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_utf__xfrm(const char **result, + const char *str, apr_size_t len, + svn_boolean_t case_insensitive, + svn_boolean_t accent_insensitive, + svn_membuf_t *buf) +{ + apr_size_t result_length; + SVN_ERR(normalize_cstring(&result_length, str, len, + case_insensitive, accent_insensitive, buf)); *result = (const char*)(buf->data); return SVN_NO_ERROR; } @@ -359,7 +388,8 @@ svn_utf__is_normalized(const char *strin apr_size_t result_length; const apr_size_t length = strlen(string); svn_membuf__create(&buffer, length * sizeof(apr_int32_t), scratch_pool); - err = normalize_cstring(&result_length, string, length, &buffer); + err = normalize_cstring(&result_length, string, length, + FALSE, FALSE, &buffer); if (err) { svn_error_clear(err);