Modified: subversion/branches/ra-git/subversion/libsvn_wc/wc_db_update_move.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_wc/wc_db_update_move.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/libsvn_wc/wc_db_update_move.c (original) +++ subversion/branches/ra-git/subversion/libsvn_wc/wc_db_update_move.c Tue Oct 11 09:11:50 2016 @@ -1197,6 +1197,135 @@ tc_editor_alter_file(node_move_baton_t * } static svn_error_t * +tc_editor_merge_local_file_change(node_move_baton_t *nmb, + const char *dst_relpath, + const char *src_relpath, + const svn_checksum_t *src_checksum, + const svn_checksum_t *dst_checksum, + apr_hash_t *dst_props, + apr_hash_t *src_props, + svn_boolean_t do_text_merge, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *b = nmb->umb; + working_node_version_t old_version, new_version; + const char *dst_abspath = svn_dirent_join(b->wcroot->abspath, + dst_relpath, + scratch_pool); + svn_skel_t *conflict_skel = NULL; + apr_hash_t *actual_props; + apr_array_header_t *propchanges; + enum svn_wc_merge_outcome_t merge_outcome; + svn_wc_notify_state_t prop_state, content_state; + svn_skel_t *work_item, *work_items = NULL; + svn_node_kind_t dst_kind_on_disk; + svn_boolean_t obstructed = FALSE; + + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; + + SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind_on_disk, scratch_pool)); + if (dst_kind_on_disk != svn_node_none && dst_kind_on_disk != svn_node_file) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_file, svn_node_file, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + + old_version.location_and_kind = b->old_version; + new_version.location_and_kind = b->new_version; + + old_version.checksum = src_checksum; + old_version.props = src_props; + new_version.checksum = dst_checksum; + new_version.props = dst_props; + + SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges, + &actual_props, b, dst_relpath, + &old_version, &new_version, + scratch_pool, scratch_pool)); + + if (!obstructed && do_text_merge) + { + const char *old_pristine_abspath; + const char *src_abspath; + + /* + * Run a 3-way merge to update the file at its post-move location, using + * the pre-move file's pristine text as the merge base, the post-move + * content as the merge-left version, and the current content of the + * working file at the pre-move location as the merge-right version. + */ + SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, + b->db, b->wcroot->abspath, + src_checksum, + scratch_pool, scratch_pool)); + src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath, + scratch_pool); + SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, + &merge_outcome, b->db, + old_pristine_abspath, + src_abspath, + dst_abspath, + dst_abspath, + NULL, NULL, NULL, /* diff labels */ + actual_props, + FALSE, /* dry-run */ + NULL, /* diff3-cmd */ + NULL, /* merge options */ + propchanges, + b->cancel_func, b->cancel_baton, + scratch_pool, scratch_pool)); + + work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); + + if (merge_outcome == svn_wc_merge_conflict) + content_state = svn_wc_notify_state_conflicted; + else + content_state = svn_wc_notify_state_merged; + } + else + content_state = svn_wc_notify_state_unchanged; + + /* If there are any conflicts to be stored, convert them into work items + * too. */ + if (conflict_skel) + { + const char *dst_repos_relpath; + + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_item, dst_abspath, b->db, + dst_repos_relpath, conflict_skel, + b->operation, &old_version, &new_version, + svn_node_file, !obstructed, + scratch_pool, scratch_pool)); + + work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); + } + + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, + svn_wc_notify_update_update, + svn_node_file, + content_state, + prop_state, + conflict_skel, work_items, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * tc_editor_delete(node_move_baton_t *nmb, const char *relpath, svn_node_kind_t old_kind, @@ -1805,6 +1934,402 @@ svn_wc__db_update_moved_away_conflict_vi /* Send all queued up notifications. */ SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev, + notify_func, notify_baton, + scratch_pool)); + if (notify_func) + { + svn_wc_notify_t *notify; + + notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, + local_relpath, + scratch_pool), + svn_wc_notify_update_completed, + scratch_pool); + notify->kind = svn_node_none; + notify->content_state = svn_wc_notify_state_inapplicable; + notify->prop_state = svn_wc_notify_state_inapplicable; + notify->revision = new_rev; + notify_func(notify_baton, notify, scratch_pool); + } + + + return SVN_NO_ERROR; +} + +static svn_error_t * +get_working_info(apr_hash_t **props, + const svn_checksum_t **checksum, + apr_array_header_t **children, + svn_node_kind_t *kind, + const char *local_relpath, + svn_wc__db_wcroot_t *wcroot, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_status_t status; + const char *repos_relpath; + svn_node_kind_t db_kind; + svn_error_t *err; + + err = svn_wc__db_read_info_internal(&status, &db_kind, NULL, &repos_relpath, + NULL, NULL, NULL, NULL, NULL, + checksum, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + wcroot, local_relpath, + result_pool, scratch_pool); + + /* If there is no node, or only a node that describes a delete + of a lower layer we report this node as not existing. */ + if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + || (!err && status != svn_wc__db_status_added + && status != svn_wc__db_status_normal)) + { + svn_error_clear(err); + + if (kind) + *kind = svn_node_none; + if (checksum) + *checksum = NULL; + if (props) + *props = NULL; + if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); + + return SVN_NO_ERROR; + } + else + SVN_ERR(err); + + SVN_ERR(svn_wc__db_read_props_internal(props, wcroot, local_relpath, + result_pool, scratch_pool)); + + if (kind) + *kind = db_kind; + + if (children && db_kind == svn_node_dir) + { + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + *children = apr_array_make(result_pool, 16, sizeof(const char *)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_WORKING_CHILDREN)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); + + APR_ARRAY_PUSH(*children, const char *) + = svn_relpath_basename(child_relpath, result_pool); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + SVN_ERR(svn_sqlite__reset(stmt)); + } + else if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); + + return SVN_NO_ERROR; +} + +/* ### Drive TC_EDITOR so as to ... + */ +static svn_error_t * +walk_local_changes(node_move_baton_t *nmb, + svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + const char *dst_relpath, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *b = nmb->umb; + svn_node_kind_t src_kind, dst_kind; + const svn_checksum_t *src_checksum, *dst_checksum; + apr_hash_t *src_props, *dst_props; + apr_array_header_t *src_children, *dst_children; + + if (b->cancel_func) + SVN_ERR(b->cancel_func(b->cancel_baton)); + + SVN_ERR(get_working_info(&src_props, &src_checksum, &src_children, + &src_kind, src_relpath, wcroot, scratch_pool, + scratch_pool)); + + SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind, + dst_relpath, b->dst_op_depth, + wcroot, scratch_pool, scratch_pool)); + + if (src_kind == svn_node_none + || (dst_kind != svn_node_none && src_kind != dst_kind)) + { + SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind, + scratch_pool)); + } + + if (nmb->skip) + return SVN_NO_ERROR; + + if (src_kind != svn_node_none && src_kind != dst_kind) + { + if (src_kind == svn_node_file || src_kind == svn_node_symlink) + { + SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind, + src_checksum, src_props, + scratch_pool)); + } + else if (src_kind == svn_node_dir) + { + SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind, + src_props, scratch_pool)); + } + } + else if (src_kind != svn_node_none) + { + svn_boolean_t props_equal; + + SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool)); + + if (src_kind == svn_node_file || src_kind == svn_node_symlink) + { + svn_boolean_t is_modified; + + SVN_ERR(svn_wc__internal_file_modified_p(&is_modified, b->db, + svn_dirent_join( + b->wcroot->abspath, + src_relpath, + scratch_pool), + FALSE /* exact_comparison */, + scratch_pool)); + if (!props_equal || is_modified) + SVN_ERR(tc_editor_merge_local_file_change(nmb, dst_relpath, + src_relpath, + src_checksum, + dst_checksum, + dst_props, src_props, + is_modified, + scratch_pool)); + } + else if (src_kind == svn_node_dir) + { + if (!props_equal) + SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath, + dst_props, src_props, + scratch_pool)); + } + } + + if (nmb->skip) + return SVN_NO_ERROR; + + if (src_kind == svn_node_dir) + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i = 0, j = 0; + + while (i < src_children->nelts || j < dst_children->nelts) + { + const char *child_name; + svn_boolean_t src_only = FALSE, dst_only = FALSE; + node_move_baton_t cnmb = { 0 }; + + cnmb.pb = nmb; + cnmb.umb = nmb->umb; + cnmb.shadowed = nmb->shadowed; + + svn_pool_clear(iterpool); + if (i >= src_children->nelts) + { + dst_only = TRUE; + child_name = APR_ARRAY_IDX(dst_children, j, const char *); + } + else if (j >= dst_children->nelts) + { + src_only = TRUE; + child_name = APR_ARRAY_IDX(src_children, i, const char *); + } + else + { + const char *src_name = APR_ARRAY_IDX(src_children, i, + const char *); + const char *dst_name = APR_ARRAY_IDX(dst_children, j, + const char *); + int cmp = strcmp(src_name, dst_name); + + if (cmp > 0) + dst_only = TRUE; + else if (cmp < 0) + src_only = TRUE; + + child_name = dst_only ? dst_name : src_name; + } + + cnmb.src_relpath = svn_relpath_join(src_relpath, child_name, + iterpool); + cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name, + iterpool); + + if (!cnmb.shadowed) + SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot, + cnmb.dst_relpath, b->dst_op_depth, + iterpool)); + + SVN_ERR(walk_local_changes(&cnmb, wcroot, cnmb.src_relpath, + cnmb.dst_relpath, iterpool)); + + if (!dst_only) + ++i; + if (!src_only) + ++j; + + if (nmb->skip) /* Does parent now want a skip? */ + break; + } + } + + return SVN_NO_ERROR; +} + +/* The body of svn_wc__db_merge_local_changes(). */ +static svn_error_t * +merge_local_changes(svn_revnum_t *old_rev, + svn_revnum_t *new_rev, + svn_wc__db_t *db, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + const char *dst_relpath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + update_move_baton_t umb = { NULL }; + svn_wc_conflict_version_t old_version; + svn_wc_conflict_version_t new_version; + apr_int64_t repos_id; + node_move_baton_t nmb = { 0 }; + + SVN_ERR_ASSERT(svn_relpath_skip_ancestor(dst_relpath, local_relpath) == NULL); + + /* In case of 'merge' the source is in the BASE tree (+ local mods) and the + * destination is a copied tree. For update/switch the source is a copied + * tree (copied from the pre-update BASE revision when the tree conflict + * was raised), and the destination is in the BASE tree. */ + if (operation == svn_wc_operation_merge) + { + umb.src_op_depth = 0; + umb.dst_op_depth = relpath_depth(dst_relpath); + } + else + { + umb.src_op_depth = relpath_depth(local_relpath); + umb.dst_op_depth = 0; + } + + SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool)); + SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind, + &new_version.peg_rev, + &new_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, local_relpath, umb.src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url, + &new_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind, + &old_version.peg_rev, + &old_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, dst_relpath, umb.dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url, + &old_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + *old_rev = old_version.peg_rev; + *new_rev = new_version.peg_rev; + + umb.operation = operation; + umb.old_version= &old_version; + umb.new_version= &new_version; + umb.db = db; + umb.wcroot = wcroot; + umb.cancel_func = cancel_func; + umb.cancel_baton = cancel_baton; + + if (umb.src_op_depth == 0) + SVN_ERR(suitable_for_move(wcroot, local_relpath, scratch_pool)); + + /* Create a new, and empty, list for notification information. */ + SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, + STMT_CREATE_UPDATE_MOVE_LIST)); + + /* Drive the editor... */ + + nmb.umb = &umb; + nmb.src_relpath = local_relpath; + nmb.dst_relpath = dst_relpath; + /* nmb.shadowed = FALSE; */ + /* nmb.edited = FALSE; */ + /* nmb.skip_children = FALSE; */ + + /* We walk the move source, comparing each node with the equivalent node at + * the move destination and applying any local changes to nodes at the move + destination. */ + SVN_ERR(walk_local_changes(&nmb, wcroot, local_relpath, dst_relpath, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_merge_local_changes(svn_wc__db_t *db, + const char *local_abspath, + const char *dest_abspath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + svn_revnum_t old_rev, new_rev; + const char *local_relpath; + const char *dest_relpath; + + /* ### Check for mixed-rev src or dst? */ + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, + db, local_abspath, + scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + dest_relpath + = svn_dirent_skip_ancestor(wcroot->abspath, dest_abspath); + + SVN_WC__DB_WITH_TXN(merge_local_changes(&old_rev, &new_rev, db, wcroot, + local_relpath, dest_relpath, + operation, action, reason, + cancel_func, cancel_baton, + scratch_pool), + wcroot); + + /* Send all queued up notifications. */ + SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev, notify_func, notify_baton, scratch_pool)); if (notify_func)
Modified: subversion/branches/ra-git/subversion/mod_authz_svn/INSTALL URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_authz_svn/INSTALL?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_authz_svn/INSTALL (original) +++ subversion/branches/ra-git/subversion/mod_authz_svn/INSTALL Tue Oct 11 09:11:50 2016 @@ -186,10 +186,16 @@ II. Configuration The "Require" statement in the previous example is not strictly needed, but has been included for clarity. - H. Example 8: Separate authz and groups files. + H. Example 8: Separating groups and authorization rules - This configuration allows storing the groups separately from the - main authz file with the authorization rules. + It may be convenient to maintain group definitions separately from + the authorization rules. This configuration allows splitting them + into two separate files. + + The file specified by the AuthzSVNGroupsFile directive uses the + same format as the ordinary authz file and should contain a single + section with the group definitions. See section II.2.B for more + details. <Location /svn> DAV svn @@ -205,78 +211,106 @@ II. Configuration Require valid-user </Location> + Configurations with per-repository access files may also use a + single file containing the group definitions. This configuration + avoids the need to duplicate the group definitions across multiple + per-repository access files. + + AuthzSVNReposRelativeAccessFile filename + AuthzSVNGroupsFile /path/to/groups/file + + NOTE: When the AuthzSVNGroupsFile directive is enabled, the + file specified with the AuthzSVNReposRelativeAccessFile or + AuthzSVNAccessFile directive cannot contain any group definitions. + 2. Specifying permissions - The file format of the access file looks like this: + A. File format of the access file - [groups] - <groupname> = <user>[,<user>...] - ... - - [<path in repository>] - @<group> = [rw|r] - <user> = [rw|r] - * = [rw|r] - - [<repository name>:<path in repository>] - @<group> = [rw|r] - <user> = [rw|r] - * = [rw|r] - - An example (line continued lines are supposed to be on one line): - - [groups] - subversion = jimb,sussman,kfogel,gstein,brane,joe,ghudson,fitz, \ - daniel,cmpilato,kevin,philip,jerenkrantz,rooneg, \ - bcollins,blair,striker,naked,dwhedon,dlr,kraai,mbk, \ - epg,bdenny,jaa - subversion-doc = nsd,zbrown,fmatias,dimentiy,patrick - subversion-bindings = xela,yoshiki,morten,jespersm,knacke - subversion-rm = mprice - ...and so on and so on... - - [/] - # Allow everyone read on the entire repository - * = r - # Allow devs with blanket commit to write to the entire repository - @subversion = rw - - [/trunk/doc] - @subversion-doc = rw - - [/trunk/subversion/bindings] - @subversion-bindings = rw - - [/branches] - @subversion-rm = rw - - [/tags] - @subversion-rm = rw - - [/branches/issue-650-ssl-certs] - mass = rw - - [/branches/pluggable-db] - gthompson = rw - - ... - - [/secrets] - # Just for demonstration - * = - @subversion = rw - - # In case of SVNParentPath we can specify which repository we are - # referring to. If no matching repository qualified section is found, - # the general unqualified section is tried. - # - # NOTE: This will work in the case of using SVNPath as well, only the - # repository name (the last element of the url) will always be the - # same. - [dark:/] - * = - @dark = rw + The file format of the access file looks like this: - [light:/] - @light = rw + [groups] + <groupname> = <user>[,<user>...] + ... + + [<path in repository>] + @<group> = [rw|r] + <user> = [rw|r] + * = [rw|r] + + [<repository name>:<path in repository>] + @<group> = [rw|r] + <user> = [rw|r] + * = [rw|r] + + An example (line continued lines are supposed to be on one line): + + [groups] + subversion = jimb,sussman,kfogel,gstein,brane,joe,ghudson,fitz, \ + daniel,cmpilato,kevin,philip,jerenkrantz,rooneg, \ + bcollins,blair,striker,naked,dwhedon,dlr,kraai,mbk, \ + epg,bdenny,jaa + subversion-doc = nsd,zbrown,fmatias,dimentiy,patrick + subversion-bindings = xela,yoshiki,morten,jespersm,knacke + subversion-rm = mprice + ...and so on and so on... + + [/] + # Allow everyone read on the entire repository + * = r + # Allow devs with blanket commit to write to the entire repository + @subversion = rw + + [/trunk/doc] + @subversion-doc = rw + + [/trunk/subversion/bindings] + @subversion-bindings = rw + + [/branches] + @subversion-rm = rw + + [/tags] + @subversion-rm = rw + + [/branches/issue-650-ssl-certs] + mass = rw + + [/branches/pluggable-db] + gthompson = rw + + ... + + [/secrets] + # Just for demonstration + * = + @subversion = rw + + # In case of SVNParentPath we can specify which repository we are + # referring to. If no matching repository qualified section is + # found, the general unqualified section is tried. + # + # NOTE: This will work in the case of using SVNPath as well, only + # the repository name (the last element of the url) will always be + # the same. + [dark:/] + * = + @dark = rw + + [light:/] + @light = rw + + B. File format of the groups file + + The file format of the groups file looks like this: + + [groups] + <groupname> = <user>[,<user>...] + ... + + An example: + + [groups] + developers = harry,sally,john + managers = jim,joe Modified: subversion/branches/ra-git/subversion/mod_authz_svn/mod_authz_svn.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_authz_svn/mod_authz_svn.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_authz_svn/mod_authz_svn.c (original) +++ subversion/branches/ra-git/subversion/mod_authz_svn/mod_authz_svn.c Tue Oct 11 09:11:50 2016 @@ -639,6 +639,8 @@ req_check_access(request_rec *r, if (r->method_number == M_MOVE || r->method_number == M_COPY) { + apr_status_t status; + dest_uri = apr_table_get(r->headers_in, "Destination"); /* Decline MOVE or COPY when there is no Destination uri, this will @@ -647,7 +649,19 @@ req_check_access(request_rec *r, if (!dest_uri) return DECLINED; - apr_uri_parse(r->pool, dest_uri, &parsed_dest_uri); + status = apr_uri_parse(r->pool, dest_uri, &parsed_dest_uri); + if (status) + { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, + "Invalid URI in Destination header"); + return HTTP_BAD_REQUEST; + } + if (!parsed_dest_uri.path) + { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid URI in Destination header"); + return HTTP_BAD_REQUEST; + } ap_unescape_url(parsed_dest_uri.path); dest_uri = parsed_dest_uri.path; Modified: subversion/branches/ra-git/subversion/mod_dav_svn/activity.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/activity.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/activity.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/activity.c Tue Oct 11 09:11:50 2016 @@ -227,7 +227,7 @@ dav_svn__store_activity(const dav_svn_re dav_error * -dav_svn__create_txn(const dav_svn_repos *repos, +dav_svn__create_txn(dav_svn_repos *repos, const char **ptxn_name, apr_hash_t *revprops, apr_pool_t *pool) @@ -248,7 +248,7 @@ dav_svn__create_txn(const dav_svn_repos svn_string_create(repos->username, pool)); } - serr = svn_fs_youngest_rev(&rev, repos->fs, pool); + serr = dav_svn__get_youngest_rev(&rev, repos, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, Modified: subversion/branches/ra-git/subversion/mod_dav_svn/dav_svn.h URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/dav_svn.h?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/dav_svn.h (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/dav_svn.h Tue Oct 11 09:11:50 2016 @@ -149,6 +149,9 @@ typedef struct dav_svn_repos { /* The path to the activities db */ const char *activities_db; + /* Cached yongest revision of the repository. SVN_INVALID_REVNUM if + youngest revision is not fetched yet. */ + svn_revnum_t youngest_rev; } dav_svn_repos; @@ -333,6 +336,10 @@ svn_boolean_t dav_svn__get_fulltext_cach /* for the repository referred to by this request, is revprop caching active? */ svn_boolean_t dav_svn__get_revprop_cache_flag(request_rec *r); +/* for the repository referred to by this request, is node prop caching active? */ +svn_boolean_t +dav_svn__get_nodeprop_cache_flag(request_rec *r); + /* has block read mode been enabled for the repository referred to by this * request? */ svn_boolean_t dav_svn__get_block_read_flag(request_rec *r); @@ -450,6 +457,40 @@ const char *dav_svn__get_vtxn_stub(reque /* For accessing transaction properties (typically "!svn/vtxr") */ const char *dav_svn__get_vtxn_root_stub(request_rec *r); + +/*** Output helpers ***/ + +/* An opaque type which represents an output for a particular request. + + All writes should target a dav_svn__output object by either using + the dav_svn__brigade functions or by preparing a bucket brigade and + passing it to the output with dav_svn__output_pass_brigade(). + + IMPORTANT: Don't write to an ap_filter_t coming from mod_dav, and + use this wrapper and the corresponding private API instead. Using + the ap_filter_t can cause unbounded memory usage with self-removing + output filters (e.g., with the filters installed by mod_headers or + mod_deflate). + + See https://mail-archives.apache.org/mod_mbox/httpd-dev/201608.mbox/%3C20160822151917.GA22369%40redhat.com%3E +*/ +typedef struct dav_svn__output dav_svn__output; + +/* Create the output wrapper for request R, allocated in POOL. */ +dav_svn__output * +dav_svn__output_create(request_rec *r, + apr_pool_t *pool); + +/* Get a bucket allocator to use for all bucket/brigade creations + when writing to OUTPUT. */ +apr_bucket_alloc_t * +dav_svn__output_get_bucket_alloc(dav_svn__output *output); + +/* Pass the bucket brigade BB down to the OUTPUT's filter stack. */ +svn_error_t * +dav_svn__output_pass_brigade(dav_svn__output *output, + apr_bucket_brigade *bb); + /*** activity.c ***/ @@ -462,7 +503,7 @@ const char *dav_svn__get_vtxn_root_stub( NOTE: This function will overwrite the svn:author property, if any, found in REVPROPS. */ dav_error * -dav_svn__create_txn(const dav_svn_repos *repos, +dav_svn__create_txn(dav_svn_repos *repos, const char **ptxn_name, apr_hash_t *revprops, apr_pool_t *pool); @@ -638,7 +679,7 @@ dav_svn__insert_all_liveprops(request_re /* Generate the HTTP response body for a successful MERGE. */ /* ### more docco */ dav_error * -dav_svn__merge_response(ap_filter_t *output, +dav_svn__merge_response(dav_svn__output *output, const dav_svn_repos *repos, svn_revnum_t new_rev, const char *post_commit_err, @@ -672,49 +713,49 @@ static const dav_report_elem dav_svn__re dav_error * dav_svn__update_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__log_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__dated_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_locations_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_location_segments_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__file_revs_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__replay_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_mergeinfo_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_locks_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_deleted_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_inherited_props_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); /*** posts/ ***/ @@ -722,11 +763,11 @@ dav_svn__get_inherited_props_report(cons dav_error * dav_svn__post_create_txn(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__post_create_txn_with_props(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output); + dav_svn__output *output); /*** authz.c ***/ @@ -933,23 +974,28 @@ int dav_svn__find_ns(const apr_array_hea /* Write LEN bytes from DATA to OUTPUT using BB. */ svn_error_t *dav_svn__brigade_write(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *buf, apr_size_t len); /* Write NULL-terminated string STR to OUTPUT using BB. */ svn_error_t *dav_svn__brigade_puts(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *str); /* Write data to OUTPUT using BB, using FMT as the output format string. */ svn_error_t *dav_svn__brigade_printf(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *fmt, ...) __attribute__((format(printf, 3, 4))); +/* Write an unspecified number of strings to OUTPUT using BB. */ +svn_error_t *dav_svn__brigade_putstrs(apr_bucket_brigade *bb, + dav_svn__output *output, + ...) SVN_NEEDS_SENTINEL_NULL; + @@ -983,11 +1029,10 @@ dav_svn__sanitize_error(svn_error_t *ser /* Return a writable generic stream that will encode its output to base64 - and send it to the Apache filter OUTPUT using BB. Allocate the stream in - POOL. */ + and send it to OUTPUT using BB. Allocate the stream in POOL. */ svn_stream_t * dav_svn__make_base64_output_stream(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, apr_pool_t *pool); /* In INFO->r->subprocess_env set "SVN-ACTION" to LINE, "SVN-REPOS" to @@ -1029,7 +1074,8 @@ dav_svn__operational_log(struct dav_reso */ dav_error * dav_svn__final_flush_or_error(request_rec *r, apr_bucket_brigade *bb, - ap_filter_t *output, dav_error *preferred_err, + dav_svn__output *output, + dav_error *preferred_err, apr_pool_t *pool); /* Log a DAV error response. @@ -1055,6 +1101,19 @@ int dav_svn__error_response_tag(request_ int dav_svn__parse_request_skel(svn_skel_t **skel, request_rec *r, apr_pool_t *pool); +/* Set *YOUNGEST_P to the number of the youngest revision in REPOS. + * + * Youngest revision will be cached in REPOS->YOUNGEST_REV to avoid + * fetching the youngest revision multiple times during proccessing + * the request. + * + * Uses SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +dav_svn__get_youngest_rev(svn_revnum_t *youngest_p, + dav_svn_repos *repos, + apr_pool_t *scratch_pool); + /*** mirror.c ***/ /* Perform the fixup hook for the R request. */ Modified: subversion/branches/ra-git/subversion/mod_dav_svn/deadprops.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/deadprops.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/deadprops.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/deadprops.c Tue Oct 11 09:11:50 2016 @@ -110,7 +110,12 @@ get_value(dav_db *db, const dav_prop_nam return NULL; } - /* ### if db->props exists, then try in there first */ + /* If db->props exists, then use it to obtain property value. */ + if (db->props) + { + *pvalue = svn_hash_gets(db->props, propname); + return NULL; + } /* We've got three different types of properties (node, txn, and revision), and we've got two different protocol versions to deal @@ -705,19 +710,14 @@ db_first_name(dav_db *db, dav_prop_name } else { - svn_node_kind_t kind; serr = svn_fs_node_proplist(&db->props, db->resource->info->root.root, get_repos_path(db->resource->info), db->p); - if (! serr) - serr = svn_fs_check_path(&kind, db->resource->info->root.root, - get_repos_path(db->resource->info), - db->p); if (! serr) { - if (kind == svn_node_dir) + if (db->resource->collection) action = svn_log__get_dir(db->resource->info->repos_path, db->resource->info->root.rev, FALSE, TRUE, 0, db->resource->pool); Modified: subversion/branches/ra-git/subversion/mod_dav_svn/liveprops.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/liveprops.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/liveprops.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/liveprops.c Tue Oct 11 09:11:50 2016 @@ -598,8 +598,8 @@ insert_prop_internal(const dav_resource { svn_revnum_t revnum; - serr = svn_fs_youngest_rev(&revnum, resource->info->repos->fs, - scratch_pool); + serr = dav_svn__get_youngest_rev(&revnum, resource->info->repos, + scratch_pool); if (serr != NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, serr->apr_err, @@ -720,7 +720,6 @@ insert_prop_internal(const dav_resource || resource->type == DAV_RESOURCE_TYPE_WORKING || resource->type == DAV_RESOURCE_TYPE_VERSION)) { - svn_node_kind_t kind; svn_checksum_t *checksum; svn_checksum_kind_t checksum_kind; @@ -733,14 +732,20 @@ insert_prop_internal(const dav_resource checksum_kind = svn_checksum_sha1; } - serr = svn_fs_check_path(&kind, resource->info->root.root, - resource->info->repos_path, scratch_pool); - if (!serr && kind == svn_node_file) - serr = svn_fs_file_checksum(&checksum, checksum_kind, - resource->info->root.root, - resource->info->repos_path, TRUE, - scratch_pool); - if (serr != NULL) + serr = svn_fs_file_checksum(&checksum, checksum_kind, + resource->info->root.root, + resource->info->repos_path, TRUE, + scratch_pool); + if (serr && serr->apr_err == SVN_ERR_FS_NOT_FILE) + { + /* It should not happen since we're already checked + RESOURCE->COLLECTION, but svn_fs_check_path() call + was added in r1239596 for some reason. Keep it for + now. */ + svn_error_clear(serr); + return DAV_PROP_INSERT_NOTSUPP; + } + else if (serr) { ap_log_rerror(APLOG_MARK, APLOG_ERR, serr->apr_err, resource->info->r, @@ -756,9 +761,6 @@ insert_prop_internal(const dav_resource break; } - if (kind != svn_node_file) - return DAV_PROP_INSERT_NOTSUPP; - value = svn_checksum_to_cstring(checksum, scratch_pool); if (! value) Modified: subversion/branches/ra-git/subversion/mod_dav_svn/lock.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/lock.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/lock.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/lock.c Tue Oct 11 09:11:50 2016 @@ -717,7 +717,7 @@ append_locks(dav_lockdb *lockdb, /* Commit a 0-byte file: */ - if ((serr = svn_fs_youngest_rev(&rev, repos->fs, resource->pool))) + if ((serr = dav_svn__get_youngest_rev(&rev, repos, resource->pool))) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not determine youngest revision", resource->pool); Modified: subversion/branches/ra-git/subversion/mod_dav_svn/merge.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/merge.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/merge.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/merge.c Tue Oct 11 09:11:50 2016 @@ -72,13 +72,12 @@ send_response(const dav_svn_repos *repos svn_fs_root_t *root, const char *path, svn_boolean_t is_dir, - ap_filter_t *output, + dav_svn__output *output, apr_bucket_brigade *bb, apr_pool_t *pool) { const char *href; const char *vsn_url; - apr_status_t status; svn_revnum_t rev_to_use; href = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_PUBLIC, @@ -86,7 +85,7 @@ send_response(const dav_svn_repos *repos rev_to_use = dav_svn__get_safe_cr(root, path, pool); vsn_url = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VERSION, rev_to_use, path, FALSE /* add_href */, pool); - status = ap_fputstrs(output, bb, + SVN_ERR(dav_svn__brigade_putstrs(bb, output, "<D:response>" DEBUG_CR "<D:href>", apr_xml_quote_string(pool, href, 1), @@ -103,9 +102,7 @@ send_response(const dav_svn_repos *repos "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR "</D:response>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return svn_error_wrap_apr(status, "Can't write response to output"); + SVN_VA_NULL)); return SVN_NO_ERROR; } @@ -115,36 +112,36 @@ static svn_error_t * do_resources(const dav_svn_repos *repos, svn_fs_root_t *root, svn_revnum_t revision, - ap_filter_t *output, + dav_svn__output *output, apr_bucket_brigade *bb, apr_pool_t *pool) { - apr_hash_t *changes; - apr_hash_t *sent = apr_hash_make(pool); - apr_hash_index_t *hi; + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; + + /* Change lists can have >100000 entries, so we must make sure to release + any collection as soon as possible. Allocate them in SUBPOOL. */ apr_pool_t *subpool = svn_pool_create(pool); + apr_hash_t *sent = apr_hash_make(subpool); + + /* Standard iteration pool. */ + apr_pool_t *iterpool = svn_pool_create(subpool); /* Fetch the paths changed in this revision. This will contain everything except otherwise-unchanged parent directories of added and deleted things. Also, note that deleted things don't merit responses of their own -- they are considered modifications to their parent. */ - SVN_ERR(svn_fs_paths_changed2(&changes, root, pool)); + SVN_ERR(svn_fs_paths_changed3(&iterator, root, subpool, subpool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); - for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi)) + while (change) { - const void *key; - void *val; - const char *path; - apr_ssize_t path_len; - svn_fs_path_change2_t *change; svn_boolean_t send_self; svn_boolean_t send_parent; + const char *path = change->path.data; - svn_pool_clear(subpool); - apr_hash_this(hi, &key, &path_len, &val); - path = key; - change = val; + svn_pool_clear(iterpool); /* Figure out who needs to get sent. */ switch (change->change_kind) @@ -171,30 +168,38 @@ do_resources(const dav_svn_repos *repos, { /* If we haven't already sent this path, send it (and then remember that we sent it). */ - if (! apr_hash_get(sent, path, path_len)) + if (! apr_hash_get(sent, path, change->path.len)) { svn_node_kind_t kind; - SVN_ERR(svn_fs_check_path(&kind, root, path, subpool)); - SVN_ERR(send_response(repos, root, path, + SVN_ERR(svn_fs_check_path(&kind, root, path, iterpool)); + SVN_ERR(send_response(repos, root, change->path.data, kind == svn_node_dir, - output, bb, subpool)); - apr_hash_set(sent, path, path_len, (void *)1); + output, bb, iterpool)); + + /* The paths in CHANGES are unique, i.e. they can only + * clash with those that we end in the SEND_PARENT case. + * + * Because file paths cannot be the parent of other paths, + * we only need to track non-file paths. */ + if (change->node_kind != svn_node_file) + { + path = apr_pstrmemdup(subpool, path, change->path.len); + apr_hash_set(sent, path, change->path.len, (void *)1); + } } } if (send_parent) { - /* If it hasn't already been sent, send the parent directory - (and then remember that you sent it). Allocate parent in - pool, not subpool, because it stays in the sent hash - afterwards. */ - const char *parent = svn_fspath__dirname(path, pool); + const char *parent = svn_fspath__dirname(path, iterpool); if (! svn_hash_gets(sent, parent)) { SVN_ERR(send_response(repos, root, parent, - TRUE, output, bb, subpool)); - svn_hash_sets(sent, parent, (void *)1); + TRUE, output, bb, iterpool)); + svn_hash_sets(sent, apr_pstrdup(subpool, parent), (void *)1); } } + + SVN_ERR(svn_fs_path_change_get(&change, iterator)); } svn_pool_destroy(subpool); @@ -209,7 +214,7 @@ do_resources(const dav_svn_repos *repos, */ dav_error * -dav_svn__merge_response(ap_filter_t *output, +dav_svn__merge_response(dav_svn__output *output, const dav_svn_repos *repos, svn_revnum_t new_rev, const char *post_commit_err, @@ -225,7 +230,7 @@ dav_svn__merge_response(ap_filter_t *out svn_string_t *creationdate, *creator_displayname; const char *post_commit_err_elem = NULL, *post_commit_header_info = NULL; - apr_status_t status; + apr_hash_t *revprops; serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool); if (serr != NULL) @@ -236,7 +241,8 @@ dav_svn__merge_response(ap_filter_t *out repos->pool); } - bb = apr_brigade_create(pool, output->c->bucket_alloc); + bb = apr_brigade_create(pool, + dav_svn__output_get_bucket_alloc(output)); /* prep some strings */ @@ -268,25 +274,19 @@ dav_svn__merge_response(ap_filter_t *out /* get the creationdate and creator-displayname of the new revision, too. */ - serr = svn_fs_revision_prop2(&creationdate, repos->fs, new_rev, - SVN_PROP_REVISION_DATE, TRUE, pool, pool); + serr = svn_fs_revision_proplist2(&revprops, repos->fs, new_rev, + TRUE, pool, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, - "Could not get date of newest revision", - repos->pool); - } - serr = svn_fs_revision_prop2(&creator_displayname, repos->fs, new_rev, - SVN_PROP_REVISION_AUTHOR, TRUE, pool, pool); - if (serr != NULL) - { - return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, - "Could not get author of newest revision", - repos->pool); + "Could not get date and author of newest " + "revision", repos->pool); } + creationdate = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE); + creator_displayname = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR); - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, DAV_XML_HEADER DEBUG_CR "<D:merge-response xmlns:D=\"DAV:\"", post_commit_header_info, @@ -305,48 +305,47 @@ dav_svn__merge_response(ap_filter_t *out "<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR, post_commit_err_elem, DEBUG_CR "<D:version-name>", rev, "</D:version-name>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, - 0, status, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); if (creationdate) { - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, "<D:creationdate>", apr_xml_quote_string(pool, creationdate->data, 1), "</D:creationdate>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, - 0, status, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); } if (creator_displayname) { - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, "<D:creator-displayname>", apr_xml_quote_string(pool, creator_displayname->data, 1), "</D:creator-displayname>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, - 0, status, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); } - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, "</D:prop>" DEBUG_CR "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR "</D:response>" DEBUG_CR, - - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, - 0, status, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); /* ONLY have dir_delta drive the editor if the caller asked us to generate a full MERGE response. svn clients can ask us to @@ -375,20 +374,20 @@ dav_svn__merge_response(ap_filter_t *out } /* wrap up the merge response */ - status = ap_fputs(output, bb, - "</D:updated-set>" DEBUG_CR - "</D:merge-response>" DEBUG_CR); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, - 0, status, - "Could not write output"); + serr = dav_svn__brigade_puts(bb, output, + "</D:updated-set>" DEBUG_CR + "</D:merge-response>" DEBUG_CR); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); /* send whatever is left in the brigade */ - status = ap_pass_brigade(output, bb); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, - 0, status, - "Could not write output"); + serr = dav_svn__output_pass_brigade(output, bb); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); return NULL; } Modified: subversion/branches/ra-git/subversion/mod_dav_svn/mod_dav_svn.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/mod_dav_svn.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/mod_dav_svn.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/mod_dav_svn.c Tue Oct 11 09:11:50 2016 @@ -105,6 +105,7 @@ typedef struct dir_conf_t { enum conf_flag txdelta_cache; /* whether to enable txdelta caching */ enum conf_flag fulltext_cache; /* whether to enable fulltext caching */ enum conf_flag revprop_cache; /* whether to enable revprop caching */ + enum conf_flag nodeprop_cache; /* whether to enable nodeprop caching */ enum conf_flag block_read; /* whether to enable block read mode */ const char *hooks_env; /* path to hook script env config file */ } dir_conf_t; @@ -240,6 +241,7 @@ create_dir_config(apr_pool_t *p, char *d conf->v2_protocol = CONF_FLAG_DEFAULT; conf->hooks_env = NULL; conf->txdelta_cache = CONF_FLAG_DEFAULT; + conf->nodeprop_cache = CONF_FLAG_DEFAULT; return conf; } @@ -270,6 +272,7 @@ merge_dir_config(apr_pool_t *p, void *ba newconf->txdelta_cache = INHERIT_VALUE(parent, child, txdelta_cache); newconf->fulltext_cache = INHERIT_VALUE(parent, child, fulltext_cache); newconf->revprop_cache = INHERIT_VALUE(parent, child, revprop_cache); + newconf->nodeprop_cache = INHERIT_VALUE(parent, child, nodeprop_cache); newconf->block_read = INHERIT_VALUE(parent, child, block_read); newconf->root_dir = INHERIT_VALUE(parent, child, root_dir); newconf->hooks_env = INHERIT_VALUE(parent, child, hooks_env); @@ -567,6 +570,19 @@ SVNCacheRevProps_cmd(cmd_parms *cmd, voi } static const char * +SVNCacheNodeProps_cmd(cmd_parms *cmd, void *config, int arg) +{ + dir_conf_t *conf = config; + + if (arg) + conf->nodeprop_cache = CONF_FLAG_ON; + else + conf->nodeprop_cache = CONF_FLAG_OFF; + + return NULL; +} + +static const char * SVNBlockRead_cmd(cmd_parms *cmd, void *config, int arg) { dir_conf_t *conf = config; @@ -991,6 +1007,15 @@ dav_svn__get_revprop_cache_flag(request_ return conf->revprop_cache == CONF_FLAG_ON; } +svn_boolean_t +dav_svn__get_nodeprop_cache_flag(request_rec *r) +{ + dir_conf_t *conf; + + conf = ap_get_module_config(r->per_dir_config, &dav_svn_module); + /* node properties caching is enabled by default. */ + return get_conf_flag(conf->nodeprop_cache, FALSE); +} svn_boolean_t dav_svn__get_block_read_flag(request_rec *r) @@ -1343,6 +1368,13 @@ static const command_rec cmds[] = "(default is Off)."), /* per directory/location */ + AP_INIT_FLAG("SVNCacheNodeProps", SVNCacheNodeProps_cmd, NULL, + ACCESS_CONF|RSRC_CONF, + "speeds up data access by caching node properties " + "if sufficient in-memory cache is available" + "(default is On)."), + + /* per directory/location */ AP_INIT_FLAG("SVNBlockRead", SVNBlockRead_cmd, NULL, ACCESS_CONF|RSRC_CONF, "speeds up operations of FSFS 1.9+ repositories if large" Modified: subversion/branches/ra-git/subversion/mod_dav_svn/posts/create_txn.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/posts/create_txn.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/posts/create_txn.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/posts/create_txn.c Tue Oct 11 09:11:50 2016 @@ -34,7 +34,7 @@ dav_error * dav_svn__post_create_txn(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output) + dav_svn__output *output) { const char *txn_name; const char *vtxn_name; @@ -75,7 +75,7 @@ dav_svn__post_create_txn(const dav_resou dav_error * dav_svn__post_create_txn_with_props(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output) + dav_svn__output *output) { const char *txn_name; const char *vtxn_name; Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/dated-rev.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/dated-rev.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/dated-rev.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/dated-rev.c Tue Oct 11 09:11:50 2016 @@ -50,7 +50,7 @@ dav_error * dav_svn__dated_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { apr_xml_elem *child; int ns; @@ -58,7 +58,6 @@ dav_svn__dated_rev_report(const dav_reso svn_revnum_t rev; apr_bucket_brigade *bb; svn_error_t *err; - apr_status_t apr_err; dav_error *derr = NULL; /* Find the DAV:creationdate element and get the requested time from it. */ @@ -95,15 +94,16 @@ dav_svn__dated_rev_report(const dav_reso "Could not access revision times."); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - apr_err = ap_fprintf(output, bb, + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); + err = dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR "<D:" SVN_DAV__VERSION_NAME ">%ld</D:" SVN_DAV__VERSION_NAME ">""</S:dated-rev-report>", rev); - if (apr_err) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + if (err) + derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/deleted-rev.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/deleted-rev.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/deleted-rev.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/deleted-rev.c Tue Oct 11 09:11:50 2016 @@ -41,7 +41,7 @@ dav_error * dav_svn__get_deleted_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { apr_xml_elem *child; int ns; @@ -52,7 +52,6 @@ dav_svn__get_deleted_rev_report(const da svn_revnum_t deleted_rev; apr_bucket_brigade *bb; svn_error_t *err; - apr_status_t apr_err; dav_error *derr = NULL; /* Sanity check. */ @@ -118,16 +117,17 @@ dav_svn__get_deleted_rev_report(const da "Could not find revision path was deleted."); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - apr_err = ap_fprintf(output, bb, + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); + err = dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:get-deleted-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR "<D:" SVN_DAV__VERSION_NAME ">%ld</D:" SVN_DAV__VERSION_NAME ">""</S:get-deleted-rev-report>", deleted_rev); - if (apr_err) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + if (err) + derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/file-revs.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/file-revs.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/file-revs.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/file-revs.c Tue Oct 11 09:11:50 2016 @@ -43,7 +43,7 @@ struct file_rev_baton { apr_bucket_brigade *bb; /* where to deliver the output */ - ap_filter_t *output; + dav_svn__output *output; /* Whether we've written the <S:file-revs-report> header. Allows for lazy writes to support mod_dav-based error handling. */ @@ -234,7 +234,7 @@ file_rev_handler(void *baton, dav_error * dav_svn__file_revs_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -304,7 +304,7 @@ dav_svn__file_revs_report(const dav_reso "Not all parameters passed"); frb.bb = apr_brigade_create(resource->pool, - output->c->bucket_alloc); + dav_svn__output_get_bucket_alloc(output)); frb.output = output; frb.needs_header = TRUE; frb.svndiff_version = resource->info->svndiff_version; Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-location-segments.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-location-segments.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-location-segments.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-location-segments.c Tue Oct 11 09:11:50 2016 @@ -48,7 +48,7 @@ struct location_segment_baton { svn_boolean_t sent_opener; - ap_filter_t *output; + dav_svn__output *output; apr_bucket_brigade *bb; dav_svn__authz_read_baton arb; }; @@ -79,28 +79,26 @@ location_segment_receiver(svn_location_s apr_pool_t *pool) { struct location_segment_baton *b = baton; - apr_status_t apr_err; SVN_ERR(maybe_send_opener(b)); if (segment->path) { const char *path_quoted = apr_xml_quote_string(pool, segment->path, 1); - apr_err = ap_fprintf(b->output, b->bb, + + SVN_ERR(dav_svn__brigade_printf(b->bb, b->output, "<S:location-segment path=\"%s\" " "range-start=\"%ld\" range-end=\"%ld\"/>" DEBUG_CR, path_quoted, - segment->range_start, segment->range_end); + segment->range_start, segment->range_end)); } else { - apr_err = ap_fprintf(b->output, b->bb, + SVN_ERR(dav_svn__brigade_printf(b->bb, b->output, "<S:location-segment " "range-start=\"%ld\" range-end=\"%ld\"/>" DEBUG_CR, - segment->range_start, segment->range_end); + segment->range_start, segment->range_end)); } - if (apr_err) - return svn_error_create(apr_err, 0, NULL); return SVN_NO_ERROR; } @@ -108,7 +106,7 @@ location_segment_receiver(svn_location_s dav_error * dav_svn__get_location_segments_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -183,8 +181,8 @@ dav_svn__get_location_segments_report(co { svn_revnum_t youngest; - serr = svn_fs_youngest_rev(&youngest, resource->info->repos->fs, - resource->pool); + serr = dav_svn__get_youngest_rev(&youngest, resource->info->repos, + resource->pool); if (serr != NULL) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not determine youngest revision", @@ -216,7 +214,8 @@ dav_svn__get_location_segments_report(co arb.repos = resource->info->repos; /* Build the bucket brigade we'll use for output. */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); /* Do what we came here for. */ location_segment_baton.sent_opener = FALSE; Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locations.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locations.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locations.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locations.c Tue Oct 11 09:11:50 2016 @@ -44,23 +44,20 @@ #include "../dav_svn.h" -static apr_status_t -send_get_locations_report(ap_filter_t *output, +static svn_error_t * +send_get_locations_report(dav_svn__output *output, apr_bucket_brigade *bb, const dav_resource *resource, apr_hash_t *fs_locations) { apr_hash_index_t *hi; - apr_pool_t *pool; - apr_status_t apr_err; + apr_pool_t *pool = resource->pool; - pool = resource->pool; - - apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR + SVN_ERR(dav_svn__brigade_printf( + bb, output, + DAV_XML_HEADER DEBUG_CR "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE - "\" xmlns:D=\"DAV:\">" DEBUG_CR); - if (apr_err) - return apr_err; + "\" xmlns:D=\"DAV:\">" DEBUG_CR)); for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next(hi)) { @@ -70,24 +67,25 @@ send_get_locations_report(ap_filter_t *o apr_hash_this(hi, &key, NULL, &value); path_quoted = apr_xml_quote_string(pool, value, 1); - apr_err = ap_fprintf(output, bb, "<S:location " + SVN_ERR(dav_svn__brigade_printf( + bb, output, "<S:location " "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR, - *(const svn_revnum_t *)key, path_quoted); - if (apr_err) - return apr_err; + *(const svn_revnum_t *)key, path_quoted)); } - return ap_fprintf(output, bb, "</S:get-locations-report>" DEBUG_CR); + + SVN_ERR(dav_svn__brigade_printf(bb, output, + "</S:get-locations-report>" DEBUG_CR)); + return SVN_NO_ERROR; } dav_error * dav_svn__get_locations_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; - apr_status_t apr_err; apr_bucket_brigade *bb; dav_svn__authz_read_baton arb; @@ -171,12 +169,13 @@ dav_svn__get_locations_report(const dav_ resource->pool); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); - apr_err = send_get_locations_report(output, bb, resource, fs_locations); + serr = send_get_locations_report(output, bb, resource, fs_locations); - if (apr_err) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + if (serr) + derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locks.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locks.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locks.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/get-locks.c Tue Oct 11 09:11:50 2016 @@ -44,25 +44,11 @@ report in libsvn_ra_neon/get_locks.c. */ -#define SVN_APR_ERR(expr) \ - do { \ - apr_status_t apr_status__temp = (expr); \ - if (apr_status__temp) \ - return apr_status__temp; \ - } while (0) - - /* Transmit LOCKS (a hash of Subversion filesystem locks keyed by - path) across OUTPUT using BB. Use POOL for necessary allocations. - - NOTE: As written, this function currently returns one of only two - status values -- "success", and "we had trouble writing out to the - output stream". If you need to return something more interesting, - you'll probably want to generate dav_error's here instead of - passing back only apr_status_t's. */ -static apr_status_t + path) across OUTPUT using BB. Use POOL for necessary allocations. */ +static svn_error_t * send_get_lock_response(apr_hash_t *locks, - ap_filter_t *output, + dav_svn__output *output, apr_bucket_brigade *bb, apr_pool_t *pool) { @@ -70,7 +56,7 @@ send_get_lock_response(apr_hash_t *locks apr_hash_index_t *hi; /* start sending report */ - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR)); @@ -86,7 +72,7 @@ send_get_lock_response(apr_hash_t *locks /* Begin the <S:lock> tag, transmitting the path, token, and creation date. */ - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:lock>" DEBUG_CR "<S:path>%s</S:path>" DEBUG_CR "<S:token>%s</S:token>" DEBUG_CR @@ -98,7 +84,7 @@ send_get_lock_response(apr_hash_t *locks /* Got expiration date? Tell the client. */ if (lock->expiration_date) - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:expirationdate>%s</S:expirationdate>" DEBUG_CR, svn_time_to_cstring(lock->expiration_date, @@ -126,7 +112,7 @@ send_get_lock_response(apr_hash_t *locks owner = encoded_owner->data; owner_base64 = TRUE; } - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:owner %s>%s</S:owner>" DEBUG_CR, owner_base64 ? "encoding=\"base64\"" : "", owner)); @@ -154,34 +140,32 @@ send_get_lock_response(apr_hash_t *locks comment = encoded_comment->data; comment_base64 = TRUE; } - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:comment %s>%s</S:comment>" DEBUG_CR, comment_base64 ? "encoding=\"base64\"" : "", comment)); } /* Okay, finish up this lock by closing the <S:lock> tag. */ - SVN_APR_ERR(ap_fprintf(output, bb, "</S:lock>" DEBUG_CR)); + SVN_ERR(dav_svn__brigade_printf(bb, output, "</S:lock>" DEBUG_CR)); } svn_pool_destroy(iterpool); /* Finish the report */ - SVN_APR_ERR(ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR)); + SVN_ERR(dav_svn__brigade_printf(bb, output, + "</S:get-locks-report>" DEBUG_CR)); return APR_SUCCESS; } -#undef SVN_APR_ERR - dav_error * dav_svn__get_locks_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { apr_bucket_brigade *bb; svn_error_t *err; dav_error *derr = NULL; - apr_status_t apr_err; apr_hash_t *locks; dav_svn__authz_read_baton arb; svn_depth_t depth = svn_depth_unknown; @@ -227,10 +211,12 @@ dav_svn__get_locks_report(const dav_reso return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, err->message, resource->pool); - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); - if ((apr_err = send_get_lock_response(locks, output, bb, resource->pool))) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + err = send_get_lock_response(locks, output, bb, resource->pool); + if (err) + derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); Modified: subversion/branches/ra-git/subversion/mod_dav_svn/reports/inherited-props.c URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/mod_dav_svn/reports/inherited-props.c?rev=1764214&r1=1764213&r2=1764214&view=diff ============================================================================== --- subversion/branches/ra-git/subversion/mod_dav_svn/reports/inherited-props.c (original) +++ subversion/branches/ra-git/subversion/mod_dav_svn/reports/inherited-props.c Tue Oct 11 09:11:50 2016 @@ -47,7 +47,7 @@ dav_error * dav_svn__get_inherited_props_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -106,7 +106,8 @@ dav_svn__get_inherited_props_report(cons arb.repos = resource->info->repos; /* Build inherited property brigade */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); serr = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool);