diff options
Diffstat (limited to 'subversion/svnserve/serve.c')
-rw-r--r-- | subversion/svnserve/serve.c | 1326 |
1 files changed, 899 insertions, 427 deletions
diff --git a/subversion/svnserve/serve.c b/subversion/svnserve/serve.c index 8c4708d..23ef6ed 100644 --- a/subversion/svnserve/serve.c +++ b/subversion/svnserve/serve.c @@ -35,6 +35,7 @@ #include "svn_compat.h" #include "svn_private_config.h" /* For SVN_PATH_LOCAL_SEPARATOR */ +#include "svn_hash.h" #include "svn_types.h" #include "svn_string.h" #include "svn_pools.h" @@ -52,6 +53,7 @@ #include "private/svn_log.h" #include "private/svn_mergeinfo_private.h" +#include "private/svn_ra_svn_private.h" #include "private/svn_fspath.h" #ifdef HAVE_UNISTD_H @@ -97,6 +99,10 @@ typedef struct fs_warning_baton_t { apr_pool_t *pool; } fs_warning_baton_t; +typedef struct authz_baton_t { + server_baton_t *server; + svn_ra_svn_conn_t *conn; +} authz_baton_t; /* Write LEN bytes of ERRSTR to LOG_FILE with svn_io_file_write(). */ static svn_error_t * @@ -185,10 +191,10 @@ log_fail_and_flush(svn_error_t *err, server_baton_t *server, svn_error_t *io_err; log_server_error(err, server, conn, pool); - io_err = svn_ra_svn_write_cmd_failure(conn, pool, err); + io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); svn_error_clear(err); SVN_ERR(io_err); - return svn_ra_svn_flush(conn, pool); + return svn_ra_svn__flush(conn, pool); } /* Log a client command. */ @@ -221,37 +227,60 @@ static svn_error_t *log_command(server_baton_t *b, return log_write(b->log_file, line, nbytes, pool); } -svn_error_t *load_configs(svn_config_t **cfg, - svn_config_t **pwdb, - svn_authz_t **authzdb, - enum username_case_type *username_case, - const char *filename, - svn_boolean_t must_exist, - const char *base, - server_baton_t *server, - svn_ra_svn_conn_t *conn, - apr_pool_t *pool) +/* Log an authz failure */ +static svn_error_t * +log_authz_denied(const char *path, + svn_repos_authz_access_t required, + server_baton_t *b, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) { - const char *pwdb_path, *authzdb_path; - svn_error_t *err; + const char *timestr, *remote_host, *line; + + if (b->log_file == NULL) + return SVN_NO_ERROR; + + if (!b->user) + return SVN_NO_ERROR; + + timestr = svn_time_to_cstring(apr_time_now(), pool); + remote_host = svn_ra_svn_conn_remote_host(conn); + + line = apr_psprintf(pool, "%" APR_PID_T_FMT + " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR, + getpid(), timestr, + (remote_host ? remote_host : "-"), + (b->user ? b->user : "-"), + b->repos_name, + (required & svn_authz_recursive ? "recursive " : ""), + (required & svn_authz_write ? "write" : "read"), + (path && path[0] ? path : "/")); + + return log_write(b->log_file, line, strlen(line), pool); +} + - SVN_ERR(svn_config_read2(cfg, filename, must_exist, FALSE, pool)); +svn_error_t *load_pwdb_config(server_baton_t *server, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + const char *pwdb_path; + svn_error_t *err; - svn_config_get(*cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL, + svn_config_get(server->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL, SVN_CONFIG_OPTION_PASSWORD_DB, NULL); - *pwdb = NULL; + server->pwdb = NULL; if (pwdb_path) { - pwdb_path = svn_dirent_canonicalize(pwdb_path, pool); - pwdb_path = svn_dirent_join(base, pwdb_path, pool); + pwdb_path = svn_dirent_internal_style(pwdb_path, pool); + pwdb_path = svn_dirent_join(server->base, pwdb_path, pool); - err = svn_config_read2(pwdb, pwdb_path, TRUE, FALSE, pool); + err = svn_config_read3(&server->pwdb, pwdb_path, TRUE, + FALSE, FALSE, pool); if (err) { - if (server) - /* Called by listening server; log error no matter what it is. */ - log_server_error(err, server, conn, pool); + log_server_error(err, server, conn, pool); /* Because it may be possible to read the pwdb file with some access methods and not others, ignore errors reading the pwdb @@ -265,18 +294,11 @@ svn_error_t *load_configs(svn_config_t **cfg, if (err->apr_err != SVN_ERR_BAD_FILENAME && ! APR_STATUS_IS_EACCES(err->apr_err)) { - if (server) - { - /* Called by listening server: Now that we've logged - * the error, clear it and return a nice, generic - * error to the user - * (http://subversion.tigris.org/issues/show_bug.cgi?id=2271). */ - svn_error_clear(err); - return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL); - } - /* Called during startup; return the error, whereupon it - * will go to standard error for the admin to see. */ - return err; + /* Now that we've logged the error, clear it and return a + * nice, generic error to the user: + * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */ + svn_error_clear(err); + return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL); } else /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */ @@ -284,51 +306,98 @@ svn_error_t *load_configs(svn_config_t **cfg, } } + return SVN_NO_ERROR; +} + +/* Canonicalize *ACCESS_FILE based on the type of argument. Results are + * placed in *ACCESS_FILE. SERVER baton is used to convert relative paths to + * absolute paths rooted at the server root. REPOS_ROOT is used to calculate + * an absolute URL for repos-relative URLs. */ +static svn_error_t * +canonicalize_access_file(const char **access_file, server_baton_t *server, + const char *repos_root, apr_pool_t *pool) +{ + if (svn_path_is_url(*access_file)) + { + *access_file = svn_uri_canonicalize(*access_file, pool); + } + else if (svn_path_is_repos_relative_url(*access_file)) + { + const char *repos_root_url; + + SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root, + pool)); + SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file, + repos_root_url, pool)); + *access_file = svn_uri_canonicalize(*access_file, pool); + } + else + { + *access_file = svn_dirent_internal_style(*access_file, pool); + *access_file = svn_dirent_join(server->base, *access_file, pool); + } + + return SVN_NO_ERROR; +} + +svn_error_t *load_authz_config(server_baton_t *server, + svn_ra_svn_conn_t *conn, + const char *repos_root, + apr_pool_t *pool) +{ + const char *authzdb_path; + const char *groupsdb_path; + svn_error_t *err; + /* Read authz configuration. */ - svn_config_get(*cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL, + svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL, SVN_CONFIG_OPTION_AUTHZ_DB, NULL); + + svn_config_get(server->cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL, + SVN_CONFIG_OPTION_GROUPS_DB, NULL); + if (authzdb_path) { const char *case_force_val; - authzdb_path = svn_dirent_canonicalize(authzdb_path, pool); - authzdb_path = svn_dirent_join(base, authzdb_path, pool); - err = svn_repos_authz_read(authzdb, authzdb_path, TRUE, pool); + /* Canonicalize and add the base onto the authzdb_path (if needed). */ + err = canonicalize_access_file(&authzdb_path, server, + repos_root, pool); + + /* Same for the groupsdb_path if it is present. */ + if (groupsdb_path && !err) + err = canonicalize_access_file(&groupsdb_path, server, + repos_root, pool); + + if (!err) + err = svn_repos_authz_read2(&server->authzdb, authzdb_path, + groupsdb_path, TRUE, pool); + if (err) { - if (server) - { - /* Called by listening server: Log the error, clear it, - * and return a nice, generic error to the user - * (http://subversion.tigris.org/issues/show_bug.cgi?id=2271). */ - log_server_error(err, server, conn, pool); - svn_error_clear(err); - return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL); - } - else - /* Called during startup; return the error, whereupon it - * will go to standard error for the admin to see. */ - return err; + log_server_error(err, server, conn, pool); + svn_error_clear(err); + return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL); } /* Are we going to be case-normalizing usernames when we consult * this authz file? */ - svn_config_get(*cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL, + svn_config_get(server->cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL, SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL); if (case_force_val) { if (strcmp(case_force_val, "upper") == 0) - *username_case = CASE_FORCE_UPPER; + server->username_case = CASE_FORCE_UPPER; else if (strcmp(case_force_val, "lower") == 0) - *username_case = CASE_FORCE_LOWER; + server->username_case = CASE_FORCE_LOWER; else - *username_case = CASE_ASIS; + server->username_case = CASE_ASIS; } } else { - *authzdb = NULL; - *username_case = CASE_ASIS; + server->authzdb = NULL; + server->username_case = CASE_ASIS; } return SVN_NO_ERROR; @@ -381,6 +450,7 @@ static svn_error_t *authz_check_access(svn_boolean_t *allowed, const char *path, svn_repos_authz_access_t required, server_baton_t *b, + svn_ra_svn_conn_t *conn, apr_pool_t *pool) { /* If authz cannot be performed, grant access. This is NOT the same @@ -415,9 +485,13 @@ static svn_error_t *authz_check_access(svn_boolean_t *allowed, b->authz_user = authz_user; } - return svn_repos_authz_check_access(b->authzdb, b->authz_repos_name, - path, b->authz_user, required, - allowed, pool); + SVN_ERR(svn_repos_authz_check_access(b->authzdb, b->authz_repos_name, + path, b->authz_user, required, + allowed, pool)); + if (!*allowed) + SVN_ERR(log_authz_denied(path, required, b, conn, pool)); + + return SVN_NO_ERROR; } /* Set *ALLOWED to TRUE if PATH is readable by the user described in @@ -430,9 +504,10 @@ static svn_error_t *authz_check_access_cb(svn_boolean_t *allowed, void *baton, apr_pool_t *pool) { - server_baton_t *sb = baton; + authz_baton_t *sb = baton; - return authz_check_access(allowed, path, svn_authz_read, sb, pool); + return authz_check_access(allowed, path, svn_authz_read, + sb->server, sb->conn, pool); } /* If authz is enabled in the specified BATON, return a read authorization @@ -456,9 +531,10 @@ static svn_error_t *authz_commit_cb(svn_repos_authz_access_t required, void *baton, apr_pool_t *pool) { - server_baton_t *sb = baton; + authz_baton_t *sb = baton; - return authz_check_access(allowed, path, required, sb, pool); + return authz_check_access(allowed, path, required, + sb->server, sb->conn, pool); } @@ -488,11 +564,11 @@ static svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t needs_username) { if (!needs_username && get_access(b, UNAUTHENTICATED) >= required) - SVN_ERR(svn_ra_svn_write_word(conn, pool, "ANONYMOUS")); + SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS")); if (b->tunnel_user && get_access(b, AUTHENTICATED) >= required) - SVN_ERR(svn_ra_svn_write_word(conn, pool, "EXTERNAL")); + SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL")); if (b->pwdb && get_access(b, AUTHENTICATED) >= required) - SVN_ERR(svn_ra_svn_write_word(conn, pool, "CRAM-MD5")); + SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5")); return SVN_NO_ERROR; } @@ -565,10 +641,10 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, && b->tunnel_user && strcmp(mech, "EXTERNAL") == 0) { if (*mecharg && strcmp(mecharg, b->tunnel_user) != 0) - return svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure", - "Requested username does not match"); + return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", + "Requested username does not match"); b->user = b->tunnel_user; - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w()", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); *success = TRUE; return SVN_NO_ERROR; } @@ -576,7 +652,7 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (get_access(b, UNAUTHENTICATED) >= required && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username) { - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w()", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); *success = TRUE; return SVN_NO_ERROR; } @@ -589,7 +665,7 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } - return svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure", + return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", "Must authenticate with listed mechanism"); } @@ -602,12 +678,12 @@ internal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t success; const char *mech, *mecharg; - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); SVN_ERR(send_mechs(conn, pool, b, required, needs_username)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)c)", b->realm)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->realm)); do { - SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &mech, &mecharg)); + SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg)); if (!*mech) break; SVN_ERR(auth(conn, pool, mech, mecharg, b, required, needs_username, @@ -640,7 +716,7 @@ static svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, static svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, server_baton_t *b) { - return svn_ra_svn_write_cmd_response(conn, pool, "()c", ""); + return svn_ra_svn__write_cmd_response(conn, pool, "()c", ""); } /* Ensure that the client has the REQUIRED access by checking the @@ -667,7 +743,7 @@ static svn_boolean_t lookup_access(apr_pool_t *pool, svn_error_t *err; /* Get authz's opinion on the access. */ - err = authz_check_access(&authorized, path, required, baton, pool); + err = authz_check_access(&authorized, path, required, baton, conn, pool); /* If an error made lookup fail, deny access. */ if (err) @@ -763,7 +839,7 @@ static svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_depth_t depth = svn_depth_infinity; svn_boolean_t start_empty; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "crb?(?c)?w", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crb?(?c)?w", &path, &rev, &start_empty, &lock_token, &depth_word)); if (depth_word) @@ -786,7 +862,7 @@ static svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, report_driver_baton_t *b = baton; const char *path; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", &path)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); path = svn_relpath_canonicalize(path, pool); if (!b->err) b->err = svn_repos_delete_path(b->report_baton, path, pool); @@ -803,7 +879,7 @@ static svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Default to infinity, for old clients that don't send depth. */ svn_depth_t depth = svn_depth_infinity; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "ccrb?(?c)?w", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccrb?(?c)?w", &path, &url, &rev, &start_empty, &lock_token, &depth_word)); @@ -882,17 +958,22 @@ static svn_error_t *accept_report(svn_boolean_t *only_empty_entry, void *edit_baton, *report_baton; report_driver_baton_t rb; svn_error_t *err; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; /* Make an svn_repos report baton. Tell it to drive the network editor * when the report is complete. */ svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); - SVN_CMD_ERR(svn_repos_begin_report2(&report_baton, rev, b->repos, + SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, b->repos, b->fs_path->data, target, tgt_path, text_deltas, depth, ignore_ancestry, send_copyfrom_args, editor, edit_baton, authz_check_access_cb_func(b), - b, pool)); + &ab, svn_ra_svn_zero_copy_limit(conn), + pool)); rb.sb = b; rb.repos_url = svn_path_uri_decode(b->repos_url, pool); @@ -903,7 +984,7 @@ static svn_error_t *accept_report(svn_boolean_t *only_empty_entry, rb.from_rev = from_rev; if (from_rev) *from_rev = SVN_INVALID_REVNUM; - err = svn_ra_svn_handle_commands2(conn, pool, report_commands, &rb, TRUE); + err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE); if (err) { /* Network or protocol error while handling commands. */ @@ -915,7 +996,7 @@ static svn_error_t *accept_report(svn_boolean_t *only_empty_entry, /* Some failure during the reporting or editing operations. */ SVN_CMD_ERR(rb.err); } - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); if (only_empty_entry) *only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries; @@ -937,8 +1018,8 @@ static svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn, { const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "c(?s)", - prop->name, prop->value)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)", + prop->name, prop->value)); } return SVN_NO_ERROR; @@ -954,41 +1035,58 @@ static svn_error_t *write_lock(svn_ra_svn_conn_t *conn, cdate = svn_time_to_cstring(lock->creation_date, pool); edate = lock->expiration_date ? svn_time_to_cstring(lock->expiration_date, pool) : NULL; - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path, - lock->token, lock->owner, lock->comment, - cdate, edate)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path, + lock->token, lock->owner, lock->comment, + cdate, edate)); return SVN_NO_ERROR; } /* ### This really belongs in libsvn_repos. */ -/* Get the properties for a path, with hardcoded committed-info values. */ -static svn_error_t *get_props(apr_hash_t **props, svn_fs_root_t *root, - const char *path, apr_pool_t *pool) -{ - svn_string_t *str; - svn_revnum_t crev; - const char *cdate, *cauthor, *uuid; - - /* Get the properties. */ - SVN_ERR(svn_fs_node_proplist(props, root, path, pool)); - - /* Hardcode the values for the committed revision, date, and author. */ - SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root, - path, pool)); - str = svn_string_create(apr_psprintf(pool, "%ld", crev), - pool); - apr_hash_set(*props, SVN_PROP_ENTRY_COMMITTED_REV, APR_HASH_KEY_STRING, str); - str = (cdate) ? svn_string_create(cdate, pool) : NULL; - apr_hash_set(*props, SVN_PROP_ENTRY_COMMITTED_DATE, APR_HASH_KEY_STRING, - str); - str = (cauthor) ? svn_string_create(cauthor, pool) : NULL; - apr_hash_set(*props, SVN_PROP_ENTRY_LAST_AUTHOR, APR_HASH_KEY_STRING, str); - - /* Hardcode the values for the UUID. */ - SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool)); - str = (uuid) ? svn_string_create(uuid, pool) : NULL; - apr_hash_set(*props, SVN_PROP_ENTRY_UUID, APR_HASH_KEY_STRING, str); +/* Get the explicit properties and/or inherited properties for a PATH in + ROOT, with hardcoded committed-info values. */ +static svn_error_t * +get_props(apr_hash_t **props, + apr_array_header_t **iprops, + authz_baton_t *b, + svn_fs_root_t *root, + const char *path, + apr_pool_t *pool) +{ + /* Get the explicit properties. */ + if (props) + { + svn_string_t *str; + svn_revnum_t crev; + const char *cdate, *cauthor, *uuid; + + SVN_ERR(svn_fs_node_proplist(props, root, path, pool)); + + /* Hardcode the values for the committed revision, date, and author. */ + SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root, + path, pool)); + str = svn_string_create(apr_psprintf(pool, "%ld", crev), + pool); + svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str); + str = (cdate) ? svn_string_create(cdate, pool) : NULL; + svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str); + str = (cauthor) ? svn_string_create(cauthor, pool) : NULL; + svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str); + + /* Hardcode the values for the UUID. */ + SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool)); + str = (uuid) ? svn_string_create(uuid, pool) : NULL; + svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str); + } + + /* Get any inherited properties the user is authorized to. */ + if (iprops) + { + SVN_ERR(svn_repos_fs_get_inherited_props( + iprops, root, path, NULL, + authz_check_access_cb_func(b->server), + b, pool, pool)); + } return SVN_NO_ERROR; } @@ -1001,7 +1099,7 @@ static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *url; const char *fs_path; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", &url)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url)); url = svn_uri_canonicalize(url, pool); SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool), @@ -1009,7 +1107,7 @@ static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, &fs_path)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool))); svn_stringbuf_set(b->fs_path, fs_path); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -1023,7 +1121,7 @@ static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "r", rev)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); return SVN_NO_ERROR; } @@ -1035,13 +1133,13 @@ static svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_time_t tm; const char *timestr; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", ×tr)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", ×tr)); SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr)); SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool)); SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repos, tm, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "r", rev)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); return SVN_NO_ERROR; } @@ -1054,15 +1152,20 @@ static svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn, const svn_string_t *value, apr_pool_t *pool) { + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; + SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__change_rev_prop(rev, name, pool))); SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repos, rev, b->user, name, old_value_p, value, TRUE, TRUE, - authz_check_access_cb_func(b), b, + authz_check_access_cb_func(b), &ab, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -1078,9 +1181,9 @@ static svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_string_t *old_value; svn_boolean_t dont_care; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rc(?s)(b?s)", - &rev, &name, &value, - &dont_care, &old_value)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)", + &rev, &name, &value, + &dont_care, &old_value)); /* Argument parsing. */ if (dont_care) @@ -1114,7 +1217,7 @@ static svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Because the revprop value was at one time mandatory, the usual optional element pattern "(?s)" isn't used. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rc?s", &rev, &name, &value)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value)); SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool)); @@ -1127,17 +1230,21 @@ static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, server_baton_t *b = baton; svn_revnum_t rev; apr_hash_t *props; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "r", &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool))); SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev, - authz_check_access_cb_func(b), b, + authz_check_access_cb_func(b), &ab, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success")); - SVN_ERR(svn_ra_svn_write_proplist(conn, pool, props)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); + SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); return SVN_NO_ERROR; } @@ -1148,16 +1255,20 @@ static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev; const char *name; svn_string_t *value; + authz_baton_t ab; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rc", &rev, &name)); + ab.server = b; + ab.conn = conn; + + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_prop(rev, name, pool))); SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repos, rev, name, - authz_check_access_cb_func(b), b, + authz_check_access_cb_func(b), &ab, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "(?s)", value)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value)); return SVN_NO_ERROR; } @@ -1294,12 +1405,16 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t aborted; commit_callback_baton_t ccb; svn_revnum_t new_rev; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; if (params->nelts == 1) { /* Clients before 1.2 don't send lock-tokens, keep-locks, and rev-props fields. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", &log_msg)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg)); lock_tokens = NULL; keep_locks = TRUE; revprop_list = NULL; @@ -1307,9 +1422,9 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, else { /* Clients before 1.5 don't send the rev-props field. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "clb?l", &log_msg, - &lock_tokens, &keep_locks, - &revprop_list)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg, + &lock_tokens, &keep_locks, + &revprop_list)); } /* The handling for locks is a little problematic, because the @@ -1326,19 +1441,20 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (lock_tokens && lock_tokens->nelts) SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool)); + /* Ignore LOG_MSG, per the protocol. See ra_svn_commit(). */ if (revprop_list) - SVN_ERR(svn_ra_svn_parse_proplist(revprop_list, pool, &revprop_table)); + SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table)); else { revprop_table = apr_hash_make(pool); - apr_hash_set(revprop_table, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, - svn_string_create(log_msg, pool)); + svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, + svn_string_create(log_msg, pool)); } /* Get author from the baton, making sure clients can't circumvent the authentication via the revision props. */ - apr_hash_set(revprop_table, SVN_PROP_REVISION_AUTHOR, APR_HASH_KEY_STRING, - b->user ? svn_string_create(b->user, pool) : NULL); + svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, + b->user ? svn_string_create(b->user, pool) : NULL); ccb.pool = pool; ccb.new_rev = &new_rev; @@ -1351,9 +1467,10 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_path_uri_decode(b->repos_url, pool), b->fs_path->data, revprop_table, commit_done, &ccb, - authz_commit_cb, baton, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); - SVN_ERR(svn_ra_svn_drive_editor(conn, pool, editor, edit_baton, &aborted)); + authz_commit_cb, &ab, pool)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton, + &aborted, FALSE)); if (!aborted) { SVN_ERR(log_command(b, conn, pool, "%s", @@ -1372,8 +1489,8 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (! keep_locks && lock_tokens && lock_tokens->nelts) SVN_ERR(unlock_paths(lock_tokens, b, conn, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "r(?c)(?c)(?c)", - new_rev, date, author, post_commit_err)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)", + new_rev, date, author, post_commit_err)); if (! b->tunnel) SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool)); @@ -1390,16 +1507,27 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_fs_root_t *root; svn_stream_t *contents; apr_hash_t *props = NULL; + apr_array_header_t *inherited_props; svn_string_t write_str; char buf[4096]; apr_size_t len; svn_boolean_t want_props, want_contents; + apr_uint64_t wants_inherited_props; svn_checksum_t *checksum; svn_error_t *err, *write_err; + int i; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; /* Parse arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)bb", &path, &rev, - &want_props, &want_contents)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev, + &want_props, &want_contents, + &wants_inherited_props)); + + if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) + wants_inherited_props = FALSE; full_path = svn_fspath__join(b->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -1420,16 +1548,44 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root, full_path, TRUE, pool)); hex_digest = svn_checksum_to_cstring_display(checksum, pool); - if (want_props) - SVN_CMD_ERR(get_props(&props, root, full_path, pool)); + + /* Fetch the file's explicit and/or inherited properties if + requested. Although the wants-iprops boolean was added to the + protocol in 1.8 a standard 1.8 client never requests iprops. */ + if (want_props || wants_inherited_props) + SVN_CMD_ERR(get_props(want_props ? &props : NULL, + wants_inherited_props ? &inherited_props : NULL, + &ab, root, full_path, + pool)); if (want_contents) SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool)); /* Send successful command response with revision and props. */ - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((?c)r(!", "success", - hex_digest, rev)); - SVN_ERR(svn_ra_svn_write_proplist(conn, pool, props)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success", + hex_digest, rev)); + SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); + + if (wants_inherited_props) + { + apr_pool_t *iterpool = svn_pool_create(pool); + + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); + for (i = 0; i < inherited_props->nelts; i++) + { + svn_prop_inherited_item_t *iprop = + APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); + + svn_pool_clear(iterpool); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", + iprop->path_or_url)); + SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", + iprop->path_or_url)); + } + svn_pool_destroy(iterpool); + } + + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); /* Now send the file's contents. */ if (want_contents) @@ -1445,7 +1601,7 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { write_str.data = buf; write_str.len = len; - SVN_ERR(svn_ra_svn_write_string(conn, pool, &write_str)); + SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str)); } if (len < sizeof(buf)) { @@ -1453,14 +1609,14 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, break; } } - write_err = svn_ra_svn_write_cstring(conn, pool, ""); + write_err = svn_ra_svn__write_cstring(conn, pool, ""); if (write_err) { svn_error_clear(err); return write_err; } SVN_CMD_ERR(err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); } return SVN_NO_ERROR; @@ -1470,20 +1626,31 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_array_header_t *params, void *baton) { server_baton_t *b = baton; - const char *path, *full_path, *file_path, *cauthor, *cdate; + const char *path, *full_path; svn_revnum_t rev; - apr_hash_t *entries, *props = NULL, *file_props; + apr_hash_t *entries, *props = NULL; + apr_array_header_t *inherited_props; apr_hash_index_t *hi; svn_fs_root_t *root; apr_pool_t *subpool; svn_boolean_t want_props, want_contents; + apr_uint64_t wants_inherited_props; apr_uint64_t dirent_fields; apr_array_header_t *dirent_fields_list = NULL; svn_ra_svn_item_t *elt; + int i; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)bb?l", &path, &rev, - &want_props, &want_contents, - &dirent_fields_list)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev, + &want_props, &want_contents, + &dirent_fields_list, + &wants_inherited_props)); + + if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) + wants_inherited_props = FALSE; if (! dirent_fields_list) { @@ -1491,8 +1658,6 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } else { - int i; - dirent_fields = 0; for (i = 0; i < dirent_fields_list->nelts; ++i) @@ -1536,14 +1701,30 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Fetch the root of the appropriate revision. */ SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); - /* Fetch the directory properties if requested. */ - if (want_props) - SVN_CMD_ERR(get_props(&props, root, full_path, pool)); + /* Fetch the directory's explicit and/or inherited properties if + requested. Although the wants-iprops boolean was added to the + protocol in 1.8 a standard 1.8 client never requests iprops. */ + if (want_props || wants_inherited_props) + SVN_CMD_ERR(get_props(want_props ? &props : NULL, + wants_inherited_props ? &inherited_props : NULL, + &ab, root, full_path, + pool)); + + /* Fetch the directories' entries before starting the response, to allow + proper error handling in cases like when FULL_PATH doesn't exist */ + if (want_contents) + SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool)); - /* Fetch the directory entries if requested. */ + /* Begin response ... */ + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev)); + SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!")); + + /* Fetch the directory entries if requested and send them immediately. */ if (want_contents) { - SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool)); + /* Use epoch for a placeholder for a missing date. */ + const char *missing_date = svn_time_to_cstring(0, pool); /* Transform the hash table's FS entries into dirents. This probably * belongs in libsvn_repos. */ @@ -1552,43 +1733,44 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { const char *name = svn__apr_hash_index_key(hi); svn_fs_dirent_t *fsent = svn__apr_hash_index_val(hi); - svn_dirent_t *entry; + const char *file_path; + + /* The fields in the entry tuple. */ + svn_node_kind_t entry_kind = svn_node_none; + svn_filesize_t entry_size = 0; + svn_boolean_t has_props = FALSE; + /* If 'created rev' was not requested, send 0. We can't use + * SVN_INVALID_REVNUM as the tuple field is not optional. + * See the email thread on dev@, 2012-03-28, subject + * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra", + * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */ + svn_revnum_t created_rev = 0; + const char *cdate = NULL; + const char *last_author = NULL; svn_pool_clear(subpool); file_path = svn_fspath__join(full_path, name, subpool); - if (! lookup_access(subpool, b, conn, svn_authz_read, file_path, FALSE)) - { - apr_hash_set(entries, name, APR_HASH_KEY_STRING, NULL); - continue; - } - - entry = apr_pcalloc(pool, sizeof(*entry)); + continue; if (dirent_fields & SVN_DIRENT_KIND) - { - /* kind */ - entry->kind = fsent->kind; - } + entry_kind = fsent->kind; if (dirent_fields & SVN_DIRENT_SIZE) - { - /* size */ - if (entry->kind == svn_node_dir) - entry->size = 0; - else - SVN_CMD_ERR(svn_fs_file_length(&entry->size, root, file_path, + if (entry_kind != svn_node_dir) + SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path, subpool)); - } if (dirent_fields & SVN_DIRENT_HAS_PROPS) { + apr_hash_t *file_props; + /* has_props */ SVN_CMD_ERR(svn_fs_node_proplist(&file_props, root, file_path, subpool)); - entry->has_props = (apr_hash_count(file_props) > 0); + has_props = (apr_hash_count(file_props) > 0); } if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR) @@ -1596,53 +1778,53 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, || (dirent_fields & SVN_DIRENT_CREATED_REV)) { /* created_rev, last_author, time */ - SVN_CMD_ERR(svn_repos_get_committed_info(&entry->created_rev, + SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev, &cdate, - &cauthor, root, + &last_author, + root, file_path, subpool)); - entry->last_author = apr_pstrdup(pool, cauthor); - if (cdate) - SVN_CMD_ERR(svn_time_from_cstring(&entry->time, cdate, - subpool)); - else - entry->time = (time_t) -1; } - /* Store the entry. */ - apr_hash_set(entries, name, APR_HASH_KEY_STRING, entry); + /* The client does not properly handle a missing CDATE. For + interoperability purposes, we must fill in some junk. + + See libsvn_ra_svn/client.c:ra_svn_get_dir() */ + if (cdate == NULL) + cdate = missing_date; + + /* Send the entry. */ + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name, + svn_node_kind_to_word(entry_kind), + (apr_uint64_t) entry_size, + has_props, created_rev, + cdate, last_author)); } svn_pool_destroy(subpool); } - /* Write out response. */ - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(r(!", "success", rev)); - SVN_ERR(svn_ra_svn_write_proplist(conn, pool, props)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(!")); - if (want_contents) + if (wants_inherited_props) { - const char *missing_date = svn_time_to_cstring(0, pool); - for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) - { - const char *name = svn__apr_hash_index_key(hi); - svn_dirent_t *entry = svn__apr_hash_index_val(hi); + apr_pool_t *iterpool = svn_pool_create(pool); - /* The client does not properly handle a missing CDATE. For - interoperability purposes, we must fill in some junk. - - See libsvn_ra_svn/client.c:ra_svn_get_dir() */ - cdate = (entry->time == (time_t) -1) - ? missing_date - : svn_time_to_cstring(entry->time, pool); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "cwnbr(?c)(?c)", name, - svn_node_kind_to_word(entry->kind), - (apr_uint64_t) entry->size, - entry->has_props, entry->created_rev, - cdate, entry->last_author)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); + for (i = 0; i < inherited_props->nelts; i++) + { + svn_prop_inherited_item_t *iprop = + APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); + + svn_pool_clear(iterpool); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", + iprop->path_or_url)); + SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", + iprop->path_or_url)); } + svn_pool_destroy(iterpool); } - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); - return SVN_NO_ERROR; + + /* Finish response. */ + return svn_ra_svn__write_tuple(conn, pool, "!))"); } static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -1652,16 +1834,17 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev; const char *target, *full_path, *depth_word; svn_boolean_t recurse; - svn_boolean_t send_copyfrom_args; - apr_uint64_t send_copyfrom_param; + apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */ + apr_uint64_t ignore_ancestry; /* Optional; default FALSE */ /* Default to unknown. Old clients won't send depth, but we'll handle that by converting recurse if necessary. */ svn_depth_t depth = svn_depth_unknown; svn_boolean_t is_checkout; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cb?wB", &rev, &target, - &recurse, &depth_word, &send_copyfrom_param)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?wB?B", &rev, &target, + &recurse, &depth_word, + &send_copyfrom_args, &ignore_ancestry)); target = svn_relpath_canonicalize(target, pool); if (depth_word) @@ -1669,9 +1852,6 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, else depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); - send_copyfrom_args = (send_copyfrom_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) ? - FALSE : (svn_boolean_t) send_copyfrom_param; - full_path = svn_fspath__join(b->fs_path->data, target, pool); /* Check authorization and authenticate the user if necessary. */ SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE)); @@ -1681,7 +1861,9 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(accept_report(&is_checkout, NULL, conn, pool, b, rev, target, NULL, TRUE, - depth, send_copyfrom_args, FALSE)); + depth, + (send_copyfrom_args == TRUE) /* send_copyfrom_args */, + (ignore_ancestry == TRUE) /* ignore_ancestry */)); if (is_checkout) { SVN_ERR(log_command(b, conn, pool, "%s", @@ -1709,10 +1891,13 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Default to unknown. Old clients won't send depth, but we'll handle that by converting recurse if necessary. */ svn_depth_t depth = svn_depth_unknown; + apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */ + apr_uint64_t ignore_ancestry; /* Optional; default TRUE */ /* Parse the arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbc?w", &rev, &target, - &recurse, &switch_url, &depth_word)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target, + &recurse, &switch_url, &depth_word, + &send_copyfrom_args, &ignore_ancestry)); target = svn_relpath_canonicalize(target, pool); switch_url = svn_uri_canonicalize(switch_url, pool); @@ -1739,8 +1924,8 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return accept_report(NULL, NULL, conn, pool, b, rev, target, switch_path, TRUE, depth, - FALSE /* TODO(sussman): no copyfrom args for now */, - TRUE); + (send_copyfrom_args == TRUE) /* send_copyfrom_args */, + (ignore_ancestry != FALSE) /* ignore_ancestry */); } static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -1755,8 +1940,8 @@ static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_depth_t depth = svn_depth_unknown; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "cb?(?r)?w", - &target, &recurse, &rev, &depth_word)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w", + &target, &recurse, &rev, &depth_word)); target = svn_relpath_canonicalize(target, pool); if (depth_word) @@ -1794,17 +1979,17 @@ static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (params->nelts == 5) { /* Clients before 1.4 don't send the text_deltas boolean or depth. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbbc", &rev, &target, - &recurse, &ignore_ancestry, &versus_url)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target, + &recurse, &ignore_ancestry, &versus_url)); text_deltas = TRUE; depth_word = NULL; } else { - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbbcb?w", - &rev, &target, &recurse, - &ignore_ancestry, &versus_url, - &text_deltas, &depth_word)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w", + &rev, &target, &recurse, + &ignore_ancestry, &versus_url, + &text_deltas, &depth_word)); } target = svn_relpath_canonicalize(target, pool); versus_url = svn_uri_canonicalize(versus_url, pool); @@ -1855,9 +2040,13 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_mergeinfo_inheritance_t inherit; svn_boolean_t include_descendants; apr_pool_t *iterpool; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "l(?r)wb", &paths, &rev, - &inherit_word, &include_descendants)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev, + &inherit_word, &include_descendants)); inherit = svn_inheritance_from_word(inherit_word); /* Canonicalize the paths which mergeinfo has been requested for. */ @@ -1885,11 +2074,11 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, canonical_paths, rev, inherit, include_descendants, - authz_check_access_cb_func(b), b, + authz_check_access_cb_func(b), &ab, pool)); SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo, b->fs_path->data, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) { @@ -1900,11 +2089,11 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_pool_clear(iterpool); SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, iterpool, "cs", key, - mergeinfo_string)); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key, + mergeinfo_string)); } svn_pool_destroy(iterpool); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); return SVN_NO_ERROR; } @@ -1936,7 +2125,7 @@ static svn_error_t *log_receiver(void *baton, b->stack_depth--; } - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "(!")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!")); if (log_entry->changed_paths2) { for (h = apr_hash_first(pool, log_entry->changed_paths2); h; @@ -1947,7 +2136,7 @@ static svn_error_t *log_receiver(void *baton, action[0] = change->action; action[1] = '\0'; - SVN_ERR(svn_ra_svn_write_tuple( + SVN_ERR(svn_ra_svn__write_tuple( conn, pool, "cw(?cr)(cbb)", path, action, @@ -1965,14 +2154,14 @@ static svn_error_t *log_receiver(void *baton, revprop_count = apr_hash_count(log_entry->revprops); else revprop_count = 0; - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!", - log_entry->revision, - author, date, message, - log_entry->has_children, - invalid_revnum, revprop_count)); - SVN_ERR(svn_ra_svn_write_proplist(conn, pool, log_entry->revprops)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)b", - log_entry->subtractive_merge)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!", + log_entry->revision, + author, date, message, + log_entry->has_children, + invalid_revnum, revprop_count)); + SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b", + log_entry->subtractive_merge)); if (log_entry->has_children) b->stack_depth++; @@ -1987,19 +2176,23 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, server_baton_t *b = baton; svn_revnum_t start_rev, end_rev; const char *full_path; - svn_boolean_t changed_paths, strict_node, include_merged_revisions; + svn_boolean_t send_changed_paths, strict_node, include_merged_revisions; apr_array_header_t *paths, *full_paths, *revprop_items, *revprops; char *revprop_word; svn_ra_svn_item_t *elt; int i; apr_uint64_t limit, include_merged_revs_param; log_baton_t lb; + authz_baton_t ab; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths, - &start_rev, &end_rev, &changed_paths, - &strict_node, &limit, - &include_merged_revs_param, - &revprop_word, &revprop_items)); + ab.server = b; + ab.conn = conn; + + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths, + &start_rev, &end_rev, &send_changed_paths, + &strict_node, &limit, + &include_merged_revs_param, + &revprop_word, &revprop_items)); if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) include_merged_revisions = FALSE; @@ -2053,28 +2246,28 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(log_command(b, conn, pool, "%s", svn_log__log(full_paths, start_rev, end_rev, - limit, changed_paths, strict_node, - include_merged_revisions, revprops, - pool))); + (int) limit, send_changed_paths, + strict_node, include_merged_revisions, + revprops, pool))); /* Get logs. (Can't report errors back to the client at this point.) */ lb.fs_path = b->fs_path->data; lb.conn = conn; lb.stack_depth = 0; err = svn_repos_get_logs4(b->repos, full_paths, start_rev, end_rev, - (int) limit, changed_paths, strict_node, + (int) limit, send_changed_paths, strict_node, include_merged_revisions, revprops, - authz_check_access_cb_func(b), b, log_receiver, + authz_check_access_cb_func(b), &ab, log_receiver, &lb, pool); - write_err = svn_ra_svn_write_word(conn, pool, "done"); + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (write_err) { svn_error_clear(err); return write_err; } SVN_CMD_ERR(err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2087,7 +2280,7 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_fs_root_t *root; svn_node_kind_t kind; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)", &path, &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); full_path = svn_fspath__join(b->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2103,8 +2296,8 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "w", - svn_node_kind_to_word(kind))); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w", + svn_node_kind_to_word(kind))); return SVN_NO_ERROR; } @@ -2117,7 +2310,7 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_fs_root_t *root; svn_dirent_t *dirent; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)", &path, &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); full_path = svn_fspath__join(b->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2139,18 +2332,18 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (dirent == NULL) { - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "()")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()")); return SVN_NO_ERROR; } cdate = (dirent->time == (time_t) -1) ? NULL : svn_time_to_cstring(dirent->time, pool); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "((wnbr(?c)(?c)))", - svn_node_kind_to_word(dirent->kind), - (apr_uint64_t) dirent->size, - dirent->has_props, dirent->created_rev, - cdate, dirent->last_author)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))", + svn_node_kind_to_word(dirent->kind), + (apr_uint64_t) dirent->size, + dirent->has_props, dirent->created_rev, + cdate, dirent->last_author)); return SVN_NO_ERROR; } @@ -2168,11 +2361,15 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t peg_revision; apr_hash_t *fs_locations; const char *abs_path; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "crl", &relative_path, - &peg_revision, - &loc_revs_proto)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path, + &peg_revision, + &loc_revs_proto)); relative_path = svn_relpath_canonicalize(relative_path, pool); abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); @@ -2202,7 +2399,8 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path, peg_revision, location_revisions, - authz_check_access_cb_func(b), b, pool); + authz_check_access_cb_func(b), &ab, + pool); /* Now, write the results to the connection. */ if (!err) @@ -2217,13 +2415,13 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const svn_revnum_t *iter_key = svn__apr_hash_index_key(iter); const char *iter_value = svn__apr_hash_index_val(iter); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "rc", - *iter_key, iter_value)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc", + *iter_key, iter_value)); } } } - write_err = svn_ra_svn_write_word(conn, pool, "done"); + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (write_err) { svn_error_clear(err); @@ -2231,7 +2429,7 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } SVN_CMD_ERR(err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2241,10 +2439,10 @@ static svn_error_t *gls_receiver(svn_location_segment_t *segment, apr_pool_t *pool) { svn_ra_svn_conn_t *conn = baton; - return svn_ra_svn_write_tuple(conn, pool, "rr(?c)", - segment->range_start, - segment->range_end, - segment->path); + return svn_ra_svn__write_tuple(conn, pool, "rr(?c)", + segment->range_start, + segment->range_end, + segment->path); } static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, @@ -2257,18 +2455,43 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, svn_revnum_t peg_revision, start_rev, end_rev; const char *relative_path; const char *abs_path; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)(?r)(?r)", - &relative_path, &peg_revision, - &start_rev, &end_rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)", + &relative_path, &peg_revision, + &start_rev, &end_rev)); relative_path = svn_relpath_canonicalize(relative_path, pool); abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); - if (SVN_IS_VALID_REVNUM(start_rev) - && SVN_IS_VALID_REVNUM(end_rev) - && (end_rev > start_rev)) + SVN_ERR(trivial_auth_request(conn, pool, b)); + SVN_ERR(log_command(baton, conn, pool, "%s", + svn_log__get_location_segments(abs_path, peg_revision, + start_rev, end_rev, + pool))); + + /* No START_REV or PEG_REVISION? We'll use HEAD. */ + if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision)) + { + svn_revnum_t youngest; + + SVN_CMD_ERR(svn_fs_youngest_rev(&youngest, b->fs, pool)); + + if (!SVN_IS_VALID_REVNUM(start_rev)) + start_rev = youngest; + if (!SVN_IS_VALID_REVNUM(peg_revision)) + peg_revision = youngest; + } + + /* No END_REV? We'll use 0. */ + if (!SVN_IS_VALID_REVNUM(end_rev)) + end_rev = 0; + + if (end_rev > start_rev) { err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, "Get-location-segments end revision must not be " @@ -2276,9 +2499,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, return log_fail_and_flush(err, b, conn, pool); } - if (SVN_IS_VALID_REVNUM(peg_revision) - && SVN_IS_VALID_REVNUM(start_rev) - && (start_rev > peg_revision)) + if (start_rev > peg_revision) { err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, "Get-location-segments start revision must not " @@ -2286,12 +2507,6 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, return log_fail_and_flush(err, b, conn, pool); } - SVN_ERR(trivial_auth_request(conn, pool, b)); - SVN_ERR(log_command(baton, conn, pool, "%s", - svn_log__get_location_segments(abs_path, peg_revision, - start_rev, end_rev, - pool))); - /* All the parameters are fine - let's perform the query against the * repository. */ @@ -2301,9 +2516,9 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, err = svn_repos_node_location_segments(b->repos, abs_path, peg_revision, start_rev, end_rev, gls_receiver, (void *)conn, - authz_check_access_cb_func(b), b, + authz_check_access_cb_func(b), &ab, pool); - write_err = svn_ra_svn_write_word(conn, pool, "done"); + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (write_err) { svn_error_clear(err); @@ -2311,7 +2526,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, } SVN_CMD_ERR(err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2326,7 +2541,7 @@ static svn_error_t *svndiff_handler(void *baton, const char *data, str.data = data; str.len = *len; - return svn_ra_svn_write_string(b->conn, b->pool, &str); + return svn_ra_svn__write_string(b->conn, b->pool, &str); } /* This implements svn_close_fn_t. Mark the end of the data by writing an @@ -2335,7 +2550,7 @@ static svn_error_t *svndiff_close_handler(void *baton) { file_revs_baton_t *b = baton; - SVN_ERR(svn_ra_svn_write_cstring(b->conn, b->pool, "")); + SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, "")); return SVN_NO_ERROR; } @@ -2351,12 +2566,12 @@ static svn_error_t *file_rev_handler(void *baton, const char *path, file_revs_baton_t *frb = baton; svn_stream_t *stream; - SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "cr(!", - path, rev)); - SVN_ERR(svn_ra_svn_write_proplist(frb->conn, pool, rev_props)); - SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "!)(!")); + SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!", + path, rev)); + SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props)); + SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!")); SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs)); - SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "!)b", merged_revision)); + SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision)); /* Store the pool for the delta stream. */ frb->pool = pool; @@ -2379,7 +2594,7 @@ static svn_error_t *file_rev_handler(void *baton, const char *path, svn_ra_svn_compression_level(frb->conn), pool); } else - SVN_ERR(svn_ra_svn_write_cstring(frb->conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, "")); return SVN_NO_ERROR; } @@ -2395,11 +2610,15 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *full_path; apr_uint64_t include_merged_revs_param; svn_boolean_t include_merged_revisions; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; /* Parse arguments. */ - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)(?r)?B", - &path, &start_rev, &end_rev, - &include_merged_revs_param)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B", + &path, &start_rev, &end_rev, + &include_merged_revs_param)); path = svn_relpath_canonicalize(path, pool); SVN_ERR(trivial_auth_request(conn, pool, b)); full_path = svn_fspath__join(b->fs_path->data, path, pool); @@ -2419,16 +2638,16 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev, include_merged_revisions, - authz_check_access_cb_func(b), b, + authz_check_access_cb_func(b), &ab, file_rev_handler, &frb, pool); - write_err = svn_ra_svn_write_word(conn, pool, "done"); + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (write_err) { svn_error_clear(err); return write_err; } SVN_CMD_ERR(err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2444,8 +2663,8 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t current_rev; svn_lock_t *l; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, - &steal_lock, ¤t_rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, + &steal_lock, ¤t_rev)); full_path = svn_fspath__join(b->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2458,9 +2677,9 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 0, /* No expiration time. */ current_rev, steal_lock, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(!", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success")); SVN_ERR(write_lock(conn, pool, l)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)")); return SVN_NO_ERROR; } @@ -2481,8 +2700,8 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_lock_t *l; svn_error_t *err = SVN_NO_ERROR, *write_err; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, - &path_revs)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, + &path_revs)); subpool = svn_pool_create(pool); @@ -2505,8 +2724,8 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Lock requests should be list of lists"); - SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "c(?r)", &path, - ¤t_rev)); + SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "c(?r)", &path, + ¤t_rev)); /* Allocate the full_path out of pool so it will survive for use * by operational logging, after this loop. */ @@ -2532,7 +2751,7 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { if (SVN_ERR_IS_LOCK_ERROR(err)) { - write_err = svn_ra_svn_write_cmd_failure(conn, pool, err); + write_err = svn_ra_svn__write_cmd_failure(conn, pool, err); svn_error_clear(err); err = NULL; SVN_ERR(write_err); @@ -2542,9 +2761,9 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } else { - SVN_ERR(svn_ra_svn_write_tuple(conn, subpool, "w!", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w!", "success")); SVN_ERR(write_lock(conn, subpool, l)); - SVN_ERR(svn_ra_svn_write_tuple(conn, subpool, "!")); + SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "!")); } } @@ -2554,12 +2773,12 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_log__lock(log_paths, steal_lock, pool))); /* NOTE: err might contain a fatal locking error from the loop above. */ - write_err = svn_ra_svn_write_word(conn, pool, "done"); + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (!write_err) SVN_CMD_ERR(err); svn_error_clear(err); SVN_ERR(write_err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2571,7 +2790,7 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path, *token, *full_path; svn_boolean_t break_lock; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?c)b", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token, &break_lock)); full_path = svn_fspath__join(b->fs_path->data, @@ -2586,7 +2805,7 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2605,8 +2824,8 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token; svn_error_t *err = SVN_NO_ERROR, *write_err; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "bl", &break_lock, - &unlock_tokens)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock, + &unlock_tokens)); /* Username required unless break_lock was specified. */ SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); @@ -2626,8 +2845,8 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Unlock request should be a list of lists"); - SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, subpool, "c(?c)", &path, - &token)); + SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, + &token)); /* Allocate the full_path out of pool so it will survive for use * by operational logging, after this loop. */ @@ -2650,7 +2869,7 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { if (SVN_ERR_IS_UNLOCK_ERROR(err)) { - write_err = svn_ra_svn_write_cmd_failure(conn, pool, err); + write_err = svn_ra_svn__write_cmd_failure(conn, pool, err); svn_error_clear(err); err = NULL; SVN_ERR(write_err); @@ -2659,8 +2878,8 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, break; } else - SVN_ERR(svn_ra_svn_write_tuple(conn, subpool, "w(c)", "success", - path)); + SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", + path)); } svn_pool_destroy(subpool); @@ -2669,11 +2888,11 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_log__unlock(log_paths, break_lock, pool))); /* NOTE: err might contain a fatal unlocking error from the loop above. */ - write_err = svn_ra_svn_write_word(conn, pool, "done"); + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (! write_err) SVN_CMD_ERR(err); svn_error_clear(err); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2686,7 +2905,7 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *full_path; svn_lock_t *l; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", &path)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); full_path = svn_fspath__join(b->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2698,10 +2917,10 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); if (l) SVN_ERR(write_lock(conn, pool, l)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); return SVN_NO_ERROR; } @@ -2717,8 +2936,12 @@ static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_hash_t *locks; apr_hash_index_t *hi; svn_error_t *err; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c?(?w)", &path, &depth_word)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word)); depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity; if ((depth != svn_depth_empty) && @@ -2739,16 +2962,17 @@ static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(log_command(b, conn, pool, "get-locks %s", svn_path_uri_encode(full_path, pool))); SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repos, full_path, depth, - authz_check_access_cb_func(b), b, pool)); + authz_check_access_cb_func(b), &ab, + pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) { svn_lock_t *l = svn__apr_hash_index_val(hi); SVN_ERR(write_lock(conn, pool, l)); } - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); return SVN_NO_ERROR; } @@ -2764,10 +2988,13 @@ static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, void *edit_baton; svn_fs_root_t *root; svn_error_t *err; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; SVN_ERR(log_command(b, conn, pool, - svn_log__replay(b->fs_path->data, low_water_mark, - pool))); + svn_log__replay(b->fs_path->data, rev, pool))); svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); @@ -2776,13 +3003,13 @@ static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, if (! err) err = svn_repos_replay2(root, b->fs_path->data, low_water_mark, send_deltas, editor, edit_baton, - authz_check_access_cb_func(b), b, pool); + authz_check_access_cb_func(b), &ab, pool); if (err) svn_error_clear(editor->abort_edit(edit_baton, pool)); SVN_CMD_ERR(err); - return svn_ra_svn_write_cmd(conn, pool, "finish-replay", ""); + return svn_ra_svn__write_cmd_finish_replay(conn, pool); } static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -2792,7 +3019,7 @@ static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t send_deltas; server_baton_t *b = baton; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rrb", &rev, &low_water_mark, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark, &send_deltas)); SVN_ERR(trivial_auth_request(conn, pool, b)); @@ -2800,7 +3027,7 @@ static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, send_deltas, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2812,8 +3039,12 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t send_deltas; server_baton_t *b = baton; apr_pool_t *iterpool; + authz_baton_t ab; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rrrb", &start_rev, + ab.server = b; + ab.conn = conn; + + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev, &end_rev, &low_water_mark, &send_deltas)); @@ -2828,11 +3059,11 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev, authz_check_access_cb_func(b), - b, + &ab, iterpool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, iterpool, "w(!", "revprops")); - SVN_ERR(svn_ra_svn_write_proplist(conn, iterpool, props)); - SVN_ERR(svn_ra_svn_write_tuple(conn, iterpool, "!)")); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops")); + SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props)); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)")); SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, send_deltas, iterpool)); @@ -2840,7 +3071,7 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } svn_pool_destroy(iterpool); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -2857,7 +3088,7 @@ get_deleted_rev(svn_ra_svn_conn_t *conn, svn_revnum_t end_revision; svn_revnum_t revision_deleted; - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "crr", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr", &path, &peg_revision, &end_revision)); full_path = svn_fspath__join(b->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2865,7 +3096,70 @@ get_deleted_rev(svn_ra_svn_conn_t *conn, SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_ERR(svn_repos_deleted_rev(b->fs, full_path, peg_revision, end_revision, &revision_deleted, pool)); - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "r", revision_deleted)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted)); + return SVN_NO_ERROR; +} + +static svn_error_t * +get_inherited_props(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + apr_array_header_t *params, + void *baton) +{ + server_baton_t *b = baton; + const char *path, *full_path; + svn_revnum_t rev; + svn_fs_root_t *root; + apr_array_header_t *inherited_props; + int i; + apr_pool_t *iterpool = svn_pool_create(pool); + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; + + /* Parse arguments. */ + SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev)); + + full_path = svn_fspath__join(b->fs_path->data, + svn_relpath_canonicalize(path, iterpool), + pool); + + /* Check authorizations */ + SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read, + full_path, FALSE)); + + if (!SVN_IS_VALID_REVNUM(rev)) + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); + + SVN_ERR(log_command(b, conn, pool, "%s", + svn_log__get_inherited_props(full_path, rev, + iterpool))); + + /* Fetch the properties and a stream for the contents. */ + SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, iterpool)); + SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); + + /* Send successful command response with revision and props. */ + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success")); + + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!")); + + for (i = 0; i < inherited_props->nelts; i++) + { + svn_prop_inherited_item_t *iprop = + APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); + + svn_pool_clear(iterpool); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", + iprop->path_or_url)); + SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", + iprop->path_or_url)); + } + + SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))")); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -2900,6 +3194,7 @@ static const svn_ra_svn_cmd_entry_t main_commands[] = { { "replay", replay }, { "replay-range", replay_range }, { "get-deleted-rev", get_deleted_rev }, + { "get-iprops", get_inherited_props }, { NULL } }; @@ -2939,7 +3234,7 @@ repos_path_valid(const char *path) consisting of just dots and spaces. Win32 functions treat paths such as ".. " and "......." inconsistently. Make sure no one can escape out of the root. */ - if (path - s >= 2 && strspn(s, ". ") == path - s) + if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s)) return FALSE; #else /* ! WIN32 */ if (path - s == 2 && s[0] == '.' && s[1] == '.') @@ -2967,7 +3262,7 @@ static svn_error_t *find_repos(const char *url, const char *root, const apr_array_header_t *capabilities, apr_pool_t *pool) { - const char *path, *full_path, *repos_root, *fs_path; + const char *path, *full_path, *repos_root, *fs_path, *hooks_env; svn_stringbuf_t *url_buf; /* Skip past the scheme and authority part. */ @@ -2976,9 +3271,13 @@ static svn_error_t *find_repos(const char *url, const char *root, return svn_error_createf(SVN_ERR_BAD_URL, NULL, "Non-svn URL passed to svn server: '%s'", url); - - path = strchr(path, '/'); - path = (path == NULL) ? "" : svn_relpath_canonicalize(path, pool); + if (! b->vhost) + { + path = strchr(path, '/'); + if (path == NULL) + path = ""; + } + path = svn_relpath_canonicalize(path, pool); path = svn_path_uri_decode(path, pool); /* Ensure that it isn't possible to escape the root by disallowing @@ -3014,19 +3313,32 @@ static svn_error_t *find_repos(const char *url, const char *root, b->repos_name = b->authz_repos_name; b->repos_name = svn_path_uri_encode(b->repos_name, pool); - /* If the svnserve configuration files have not been loaded then - load them from the repository. */ + /* If the svnserve configuration has not been loaded then load it from the + * repository. */ if (NULL == b->cfg) - SVN_ERR(load_configs(&b->cfg, &b->pwdb, &b->authzdb, &b->username_case, - svn_repos_svnserve_conf(b->repos, pool), FALSE, - svn_repos_conf_dir(b->repos, pool), - b, conn, - pool)); + { + b->base = svn_repos_conf_dir(b->repos, pool); + + SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->repos, pool), + FALSE, /* must_exist */ + FALSE, /* section_names_case_sensitive */ + FALSE, /* option_names_case_sensitive */ + pool)); + SVN_ERR(load_pwdb_config(b, conn, pool)); + SVN_ERR(load_authz_config(b, conn, repos_root, pool)); + } + /* svnserve.conf has been loaded via the --config-file option so need + * to load pwdb and authz. */ + else + { + SVN_ERR(load_pwdb_config(b, conn, pool)); + SVN_ERR(load_authz_config(b, conn, repos_root, pool)); + } #ifdef SVN_HAVE_SASL /* Should we use Cyrus SASL? */ - svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL, - SVN_CONFIG_OPTION_USE_SASL, FALSE); + SVN_ERR(svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL, + SVN_CONFIG_OPTION_USE_SASL, FALSE)); #endif /* Use the repository UUID as the default realm. */ @@ -3045,6 +3357,13 @@ static svn_error_t *find_repos(const char *url, const char *root, "No access allowed to this repository", b, conn, pool); + /* Configure hook script environment variables. */ + svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL, + SVN_CONFIG_OPTION_HOOKS_ENV, NULL); + if (hooks_env) + hooks_env = svn_dirent_internal_style(hooks_env, pool); + SVN_ERR(svn_repos_hooks_setenv(b->repos, hooks_env, pool)); + return SVN_NO_ERROR; } @@ -3071,6 +3390,134 @@ fs_warning_func(void *baton, svn_error_t *err) svn_pool_clear(b->pool); } +/* Return the normalized repository-relative path for the given PATH + * (may be a URL, full path or relative path) and fs contained in the + * server baton BATON. Allocate the result in POOL. + */ +static const char * +get_normalized_repo_rel_path(void *baton, + const char *path, + apr_pool_t *pool) +{ + server_baton_t *sb = baton; + + if (svn_path_is_url(path)) + { + /* This is a copyfrom URL. */ + path = svn_uri_skip_ancestor(sb->repos_url, path, pool); + path = svn_fspath__canonicalize(path, pool); + } + else + { + /* This is a base-relative path. */ + if ((path)[0] != '/') + /* Get an absolute path for use in the FS. */ + path = svn_fspath__join(sb->fs_path->data, path, pool); + } + + return path; +} + +/* Get the revision root for REVISION in fs given by server baton BATON + * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM. + * Use POOL for allocations. + */ +static svn_error_t * +get_revision_root(svn_fs_root_t **fs_root, + void *baton, + svn_revnum_t revision, + apr_pool_t *pool) +{ + server_baton_t *sb = baton; + + if (!SVN_IS_VALID_REVNUM(revision)) + SVN_ERR(svn_fs_youngest_rev(&revision, sb->fs, pool)); + + SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +fetch_props_func(apr_hash_t **props, + void *baton, + const char *path, + svn_revnum_t base_revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_root_t *fs_root; + svn_error_t *err; + + path = get_normalized_repo_rel_path(baton, path, scratch_pool); + SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); + + err = svn_fs_node_proplist(props, fs_root, path, result_pool); + if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *props = apr_hash_make(result_pool); + return SVN_NO_ERROR; + } + else if (err) + return svn_error_trace(err); + + return SVN_NO_ERROR; +} + +static svn_error_t * +fetch_kind_func(svn_node_kind_t *kind, + void *baton, + const char *path, + svn_revnum_t base_revision, + apr_pool_t *scratch_pool) +{ + svn_fs_root_t *fs_root; + + path = get_normalized_repo_rel_path(baton, path, scratch_pool); + SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); + + SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +fetch_base_func(const char **filename, + void *baton, + const char *path, + svn_revnum_t base_revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *contents; + svn_stream_t *file_stream; + const char *tmp_filename; + svn_fs_root_t *fs_root; + svn_error_t *err; + + path = get_normalized_repo_rel_path(baton, path, scratch_pool); + SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); + + err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool); + if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *filename = NULL; + return SVN_NO_ERROR; + } + else if (err) + return svn_error_trace(err); + SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool)); + + *filename = apr_pstrdup(result_pool, tmp_filename); + + return SVN_NO_ERROR; +} + svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, apr_pool_t *pool) { @@ -3080,7 +3527,7 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, apr_array_header_t *caplist, *cap_words; server_baton_t b; fs_warning_baton_t warn_baton; - svn_stringbuf_t *cap_log = svn_stringbuf_create("", pool); + svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(pool); b.tunnel = params->tunnel; b.tunnel_user = get_tunnel_user(params, pool); @@ -3088,52 +3535,64 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, b.user = NULL; b.username_case = params->username_case; b.authz_user = NULL; + b.base = params->base; b.cfg = params->cfg; - b.pwdb = params->pwdb; - b.authzdb = params->authzdb; + b.pwdb = NULL; + b.authzdb = NULL; b.realm = NULL; b.log_file = params->log_file; b.pool = pool; b.use_sasl = FALSE; + b.vhost = params->vhost; /* construct FS configuration parameters */ b.fs_config = apr_hash_make(pool); - apr_hash_set(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, - APR_HASH_KEY_STRING, params->cache_txdeltas ? "1" : "0"); - apr_hash_set(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, - APR_HASH_KEY_STRING, params->cache_fulltexts ? "1" : "0"); + svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, + params->cache_txdeltas ? "1" :"0"); + svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, + params->cache_fulltexts ? "1" :"0"); + svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, + params->cache_revprops ? "1" :"0"); /* Send greeting. We don't support version 1 any more, so we can * send an empty mechlist. */ if (params->compression_level > 0) - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwwwww)", - (apr_uint64_t) 2, (apr_uint64_t) 2, - SVN_RA_SVN_CAP_EDIT_PIPELINE, - SVN_RA_SVN_CAP_SVNDIFF1, - SVN_RA_SVN_CAP_ABSENT_ENTRIES, - SVN_RA_SVN_CAP_COMMIT_REVPROPS, - SVN_RA_SVN_CAP_DEPTH, - SVN_RA_SVN_CAP_LOG_REVPROPS, - SVN_RA_SVN_CAP_ATOMIC_REVPROPS, - SVN_RA_SVN_CAP_PARTIAL_REPLAY)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)", + (apr_uint64_t) 2, (apr_uint64_t) 2, + SVN_RA_SVN_CAP_EDIT_PIPELINE, + SVN_RA_SVN_CAP_SVNDIFF1, + SVN_RA_SVN_CAP_ABSENT_ENTRIES, + SVN_RA_SVN_CAP_COMMIT_REVPROPS, + SVN_RA_SVN_CAP_DEPTH, + SVN_RA_SVN_CAP_LOG_REVPROPS, + SVN_RA_SVN_CAP_ATOMIC_REVPROPS, + SVN_RA_SVN_CAP_PARTIAL_REPLAY, + SVN_RA_SVN_CAP_INHERITED_PROPS, + SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, + SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE + )); else - SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwwww)", - (apr_uint64_t) 2, (apr_uint64_t) 2, - SVN_RA_SVN_CAP_EDIT_PIPELINE, - SVN_RA_SVN_CAP_ABSENT_ENTRIES, - SVN_RA_SVN_CAP_COMMIT_REVPROPS, - SVN_RA_SVN_CAP_DEPTH, - SVN_RA_SVN_CAP_LOG_REVPROPS, - SVN_RA_SVN_CAP_ATOMIC_REVPROPS, - SVN_RA_SVN_CAP_PARTIAL_REPLAY)); + SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)", + (apr_uint64_t) 2, (apr_uint64_t) 2, + SVN_RA_SVN_CAP_EDIT_PIPELINE, + SVN_RA_SVN_CAP_ABSENT_ENTRIES, + SVN_RA_SVN_CAP_COMMIT_REVPROPS, + SVN_RA_SVN_CAP_DEPTH, + SVN_RA_SVN_CAP_LOG_REVPROPS, + SVN_RA_SVN_CAP_ATOMIC_REVPROPS, + SVN_RA_SVN_CAP_PARTIAL_REPLAY, + SVN_RA_SVN_CAP_INHERITED_PROPS, + SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, + SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE + )); /* Read client response, which we assume to be in version 2 format: * version, capability list, and client URL; then we do an auth * request. */ - SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "nlc?c(?c)", - &ver, &caplist, &client_url, - &ra_client_string, - &client_string)); + SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "nlc?c(?c)", + &ver, &caplist, &client_url, + &ra_client_string, + &client_string)); if (ver != 2) return SVN_NO_ERROR; @@ -3188,10 +3647,10 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, { log_error(err, b.log_file, svn_ra_svn_conn_remote_host(conn), b.user, NULL, pool); - io_err = svn_ra_svn_write_cmd_failure(conn, pool, err); + io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); svn_error_clear(err); SVN_ERR(io_err); - return svn_ra_svn_flush(conn, pool); + return svn_ra_svn__flush(conn, pool); } /* Log the open. */ @@ -3227,12 +3686,25 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo, SVN_REPOS_CAPABILITY_MERGEINFO, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(cc(!", - "success", uuid, b.repos_url)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!", + "success", uuid, b.repos_url)); if (supports_mergeinfo) - SVN_ERR(svn_ra_svn_write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); + SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); + } + + /* Set up editor shims. */ + { + svn_delta_shim_callbacks_t *callbacks = + svn_delta_shim_callbacks_default(pool); + + callbacks->fetch_base_func = fetch_base_func; + callbacks->fetch_props_func = fetch_props_func; + callbacks->fetch_kind_func = fetch_kind_func; + callbacks->fetch_baton = &b; + + SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks)); } - return svn_ra_svn_handle_commands2(conn, pool, main_commands, &b, FALSE); + return svn_ra_svn__handle_commands2(conn, pool, main_commands, &b, FALSE); } |