diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-03-18 13:33:26 +0000 |
---|---|---|
committer | <> | 2015-07-08 14:41:01 +0000 |
commit | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (patch) | |
tree | 98bae10dde41c746c51ae97ec4f879e330415aa7 /subversion/svnrdump/svnrdump.c | |
parent | 239dfafe71711b2f4c43d7b90a1228d7bdc5195e (diff) | |
download | subversion-tarball-bb0ef45f7c46b0ae221b26265ef98a768c33f820.tar.gz |
Imported from /home/lorry/working-area/delta_subversion-tarball/subversion-1.8.13.tar.gz.subversion-1.8.13
Diffstat (limited to 'subversion/svnrdump/svnrdump.c')
-rw-r--r-- | subversion/svnrdump/svnrdump.c | 389 |
1 files changed, 297 insertions, 92 deletions
diff --git a/subversion/svnrdump/svnrdump.c b/subversion/svnrdump/svnrdump.c index 0947f75..6bf409c 100644 --- a/subversion/svnrdump/svnrdump.c +++ b/subversion/svnrdump/svnrdump.c @@ -23,6 +23,7 @@ */ #include <apr_signal.h> +#include <apr_uri.h> #include "svn_pools.h" #include "svn_cmdline.h" @@ -39,6 +40,7 @@ #include "svnrdump.h" #include "private/svn_cmdline_private.h" +#include "private/svn_ra_private.h" @@ -78,6 +80,7 @@ enum svn_svnrdump__longopt_t opt_auth_password, opt_auth_nocache, opt_non_interactive, + opt_force_interactive, opt_incremental, opt_trust_server_cert, opt_version @@ -89,7 +92,8 @@ enum svn_svnrdump__longopt_t opt_auth_password, \ opt_auth_nocache, \ opt_trust_server_cert, \ - opt_non_interactive + opt_non_interactive, \ + opt_force_interactive static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] = { @@ -125,7 +129,13 @@ static const apr_getopt_option_t svnrdump__options[] = {"password", opt_auth_password, 1, N_("specify a password ARG")}, {"non-interactive", opt_non_interactive, 0, - N_("do no interactive prompting")}, + N_("do no interactive prompting (default is to prompt\n" + " " + "only if standard input is a terminal device)")}, + {"force-interactive", opt_force_interactive, 0, + N_("do interactive prompting even if standard input\n" + " " + "is not a terminal device")}, {"no-auth-cache", opt_auth_nocache, 0, N_("do not cache authentication tokens")}, {"help", 'h', 0, @@ -151,11 +161,11 @@ static const apr_getopt_option_t svnrdump__options[] = /* Baton for the RA replay session. */ struct replay_baton { - /* The editor producing diffs. */ - const svn_delta_editor_t *editor; + /* A backdoor ra session for fetching information. */ + svn_ra_session_t *extra_ra_session; - /* Baton for the editor. */ - void *edit_baton; + /* The output stream */ + svn_stream_t *stdout_stream; /* Whether to be quiet. */ svn_boolean_t quiet; @@ -217,13 +227,12 @@ replay_revstart(svn_revnum_t revision, SVN_ERR(svn_stream_write(stdout_stream, propstring->data, &(propstring->len))); - SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n")); + SVN_ERR(svn_stream_puts(stdout_stream, "\n")); SVN_ERR(svn_stream_close(stdout_stream)); - /* Extract editor and editor_baton from the replay_baton and - set them so that the editor callbacks can use them. */ - *editor = rb->editor; - *edit_baton = rb->edit_baton; + SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision, + rb->stdout_stream, rb->extra_ra_session, + NULL, check_cancel, NULL, pool)); return SVN_NO_ERROR; } @@ -240,16 +249,96 @@ replay_revend(svn_revnum_t revision, { /* No resources left to free. */ struct replay_baton *rb = replay_baton; + + SVN_ERR(editor->close_edit(edit_baton, pool)); + if (! rb->quiet) SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", revision)); return SVN_NO_ERROR; } +#ifdef USE_EV2_IMPL +/* Print dumpstream-formatted information about REVISION. + * Implements the `svn_ra_replay_revstart_callback_t' interface. + */ +static svn_error_t * +replay_revstart_v2(svn_revnum_t revision, + void *replay_baton, + svn_editor_t **editor, + apr_hash_t *rev_props, + apr_pool_t *pool) +{ + struct replay_baton *rb = replay_baton; + apr_hash_t *normal_props; + svn_stringbuf_t *propstring; + svn_stream_t *stdout_stream; + svn_stream_t *revprop_stream; + + SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); + + /* Revision-number: 19 */ + SVN_ERR(svn_stream_printf(stdout_stream, pool, + SVN_REPOS_DUMPFILE_REVISION_NUMBER + ": %ld\n", revision)); + SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); + propstring = svn_stringbuf_create_ensure(0, pool); + revprop_stream = svn_stream_from_stringbuf(propstring, pool); + SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); + SVN_ERR(svn_stream_close(revprop_stream)); + + /* Prop-content-length: 13 */ + SVN_ERR(svn_stream_printf(stdout_stream, pool, + SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH + ": %" APR_SIZE_T_FMT "\n", propstring->len)); + + /* Content-length: 29 */ + SVN_ERR(svn_stream_printf(stdout_stream, pool, + SVN_REPOS_DUMPFILE_CONTENT_LENGTH + ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); + + /* Property data. */ + SVN_ERR(svn_stream_write(stdout_stream, propstring->data, + &(propstring->len))); + + SVN_ERR(svn_stream_puts(stdout_stream, "\n")); + SVN_ERR(svn_stream_close(stdout_stream)); + + SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision, + rb->stdout_stream, + rb->extra_ra_session, + NULL, check_cancel, NULL, pool, pool)); + + return SVN_NO_ERROR; +} + +/* Print progress information about the dump of REVISION. + Implements the `svn_ra_replay_revfinish_callback_t' interface. */ +static svn_error_t * +replay_revend_v2(svn_revnum_t revision, + void *replay_baton, + svn_editor_t *editor, + apr_hash_t *rev_props, + apr_pool_t *pool) +{ + /* No resources left to free. */ + struct replay_baton *rb = replay_baton; + + SVN_ERR(svn_editor_complete(editor)); + + if (! rb->quiet) + SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", + revision)); + return SVN_NO_ERROR; +} +#endif + /* Initialize the RA layer, and set *CTX to a new client context baton * allocated from POOL. Use CONFIG_DIR and pass USERNAME, PASSWORD, * CONFIG_DIR and NO_AUTH_CACHE to initialize the authorization baton. * CONFIG_OPTIONS (if not NULL) is a list of configuration overrides. + * REPOS_URL is used to fiddle with server-specific configuration + * options. */ static svn_error_t * init_client_context(svn_client_ctx_t **ctx_p, @@ -257,18 +346,19 @@ init_client_context(svn_client_ctx_t **ctx_p, const char *username, const char *password, const char *config_dir, + const char *repos_url, svn_boolean_t no_auth_cache, svn_boolean_t trust_server_cert, apr_array_header_t *config_options, apr_pool_t *pool) { svn_client_ctx_t *ctx = NULL; - svn_config_t *cfg_config; + svn_config_t *cfg_config, *cfg_servers; SVN_ERR(svn_ra_initialize(pool)); SVN_ERR(svn_config_ensure(config_dir, pool)); - SVN_ERR(svn_client_create_context(&ctx, pool)); + SVN_ERR(svn_client_create_context2(&ctx, NULL, pool)); SVN_ERR(svn_config_get_config(&(ctx->config), config_dir, pool)); @@ -276,8 +366,47 @@ init_client_context(svn_client_ctx_t **ctx_p, SVN_ERR(svn_cmdline__apply_config_options(ctx->config, config_options, "svnrdump: ", "--config-option")); - cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, - APR_HASH_KEY_STRING); + cfg_config = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG); + + /* ### FIXME: This is a hack to work around the fact that our dump + ### editor simply can't handle the way ra_serf violates the + ### editor v1 drive ordering requirements. + ### + ### We'll override both the global value and server-specific one + ### for the 'http-bulk-updates' and 'http-max-connections' + ### options in order to get ra_serf to try a bulk-update if the + ### server will allow it, or at least try to limit all its + ### auxiliary GETs/PROPFINDs to happening (well-ordered) on a + ### single server connection. + ### + ### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116. + */ + cfg_servers = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_SERVERS); + svn_config_set_bool(cfg_servers, SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, TRUE); + svn_config_set_int64(cfg_servers, SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 2); + if (cfg_servers) + { + apr_status_t status; + apr_uri_t parsed_url; + + status = apr_uri_parse(pool, repos_url, &parsed_url); + if (! status) + { + const char *server_group; + + server_group = svn_config_find_group(cfg_servers, parsed_url.hostname, + SVN_CONFIG_SECTION_GROUPS, pool); + if (server_group) + { + svn_config_set_bool(cfg_servers, server_group, + SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, TRUE); + svn_config_set_int64(cfg_servers, server_group, + SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 2); + } + } + } /* Set up our cancellation support. */ ctx->cancel_func = check_cancel; @@ -311,7 +440,7 @@ dump_revision_header(svn_ra_session_t *session, ": %ld\n", revision)); prophash = apr_hash_make(pool); - propstring = svn_stringbuf_create("", pool); + propstring = svn_stringbuf_create_empty(pool); SVN_ERR(svn_ra_rev_proplist(session, revision, &prophash, pool)); propstream = svn_stream_from_stringbuf(propstring, pool); @@ -330,40 +459,88 @@ dump_revision_header(svn_ra_session_t *session, /* The properties */ SVN_ERR(svn_stream_write(stdout_stream, propstring->data, &(propstring->len))); - SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n")); + SVN_ERR(svn_stream_puts(stdout_stream, "\n")); + + return SVN_NO_ERROR; +} + +static svn_error_t * +dump_initial_full_revision(svn_ra_session_t *session, + svn_ra_session_t *extra_ra_session, + svn_stream_t *stdout_stream, + svn_revnum_t revision, + svn_boolean_t quiet, + apr_pool_t *pool) +{ + const svn_ra_reporter3_t *reporter; + void *report_baton; + const svn_delta_editor_t *dump_editor; + void *dump_baton; + const char *session_url, *source_relpath; + + /* Determine whether we're dumping the repository root URL or some + child thereof. If we're dumping a subtree of the repository + rather than the root, we have to jump through some hoops to make + our update-driven dump generation work the way a replay-driven + one would. + + See http://subversion.tigris.org/issues/show_bug.cgi?id=4101 + */ + SVN_ERR(svn_ra_get_session_url(session, &session_url, pool)); + SVN_ERR(svn_ra_get_path_relative_to_root(session, &source_relpath, + session_url, pool)); + + /* Start with a revision record header. */ + SVN_ERR(dump_revision_header(session, stdout_stream, revision, pool)); + + /* Then, we'll drive the dump editor with what would look like a + full checkout of the repository as it looked in START_REVISION. + We do this by manufacturing a basic 'report' to the update + reporter, telling it that we have nothing to start with. The + delta between nothing and everything-at-REV is, effectively, a + full dump of REV. */ + SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton, revision, + stdout_stream, extra_ra_session, + source_relpath, check_cancel, NULL, pool)); + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, revision, + "", svn_depth_infinity, FALSE, FALSE, + dump_editor, dump_baton, pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", revision, + svn_depth_infinity, TRUE, NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + + /* All finished with START_REVISION! */ + if (! quiet) + SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", + revision)); return SVN_NO_ERROR; } /* Replay revisions START_REVISION thru END_REVISION (inclusive) of - * the repository located at URL, using callbacks which generate - * Subversion repository dumpstreams describing the changes made in - * those revisions. If QUIET is set, don't generate progress - * messages. + * the repository URL at which SESSION is rooted, using callbacks + * which generate Subversion repository dumpstreams describing the + * changes made in those revisions. If QUIET is set, don't generate + * progress messages. */ static svn_error_t * replay_revisions(svn_ra_session_t *session, - const char *url, + svn_ra_session_t *extra_ra_session, svn_revnum_t start_revision, svn_revnum_t end_revision, svn_boolean_t quiet, svn_boolean_t incremental, apr_pool_t *pool) { - const svn_delta_editor_t *dump_editor; struct replay_baton *replay_baton; - void *dump_baton; const char *uuid; svn_stream_t *stdout_stream; SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton, stdout_stream, - check_cancel, NULL, pool)); - replay_baton = apr_pcalloc(pool, sizeof(*replay_baton)); - replay_baton->editor = dump_editor; - replay_baton->edit_baton = dump_baton; + replay_baton->stdout_stream = stdout_stream; + replay_baton->extra_ra_session = extra_ra_session; replay_baton->quiet = quiet; /* Write the magic header and UUID */ @@ -391,46 +568,29 @@ replay_revisions(svn_ra_session_t *session, incremental = TRUE; } - if (incremental) + /* If what remains to be dumped is not going to be dumped + incrementally, then dump the first revision in full. */ + if (!incremental) { + SVN_ERR(dump_initial_full_revision(session, extra_ra_session, + stdout_stream, start_revision, + quiet, pool)); + start_revision++; + } + + /* If there are still revisions left to be dumped, do so. */ + if (start_revision <= end_revision) + { +#ifndef USE_EV2_IMPL SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, 0, TRUE, replay_revstart, replay_revend, replay_baton, pool)); - } - else - { - const svn_ra_reporter3_t *reporter; - void *report_baton; - - /* First, we need to dump the start_revision in full. We'll - start with a revision record header. */ - SVN_ERR(dump_revision_header(session, stdout_stream, - start_revision, pool)); - - /* Then, we'll drive the dump editor with what would look like a - full checkout of the repository as it looked in - START_REVISION. We do this by manufacturing a basic 'report' - to the update reporter, telling it that we have nothing to - start with. The delta between nothing and everything-at-REV - is, effectively, a full dump of REV. */ - SVN_ERR(svn_ra_do_update2(session, &reporter, &report_baton, - start_revision, "", svn_depth_infinity, - FALSE, dump_editor, dump_baton, pool)); - SVN_ERR(reporter->set_path(report_baton, "", start_revision, - svn_depth_infinity, TRUE, NULL, pool)); - SVN_ERR(reporter->finish_report(report_baton, pool)); - - /* All finished with START_REVISION! */ - if (! quiet) - SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", - start_revision)); - start_revision++; - - /* Now go pick up additional revisions in the range, if any. */ - if (start_revision <= end_revision) - SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, - 0, TRUE, replay_revstart, replay_revend, - replay_baton, pool)); +#else + SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision, + 0, TRUE, replay_revstart_v2, + replay_revend_v2, replay_baton, + NULL, NULL, NULL, NULL, pool)); +#endif } SVN_ERR(svn_stream_close(stdout_stream)); @@ -501,8 +661,8 @@ version(const char *progname, pool); SVN_ERR(svn_ra_print_modules(version_footer, pool)); - return svn_opt_print_help3(NULL, ensure_appname(progname, pool), - TRUE, quiet, version_footer->data, + return svn_opt_print_help4(NULL, ensure_appname(progname, pool), + TRUE, quiet, FALSE, version_footer->data, NULL, NULL, NULL, NULL, NULL, pool); } @@ -531,7 +691,16 @@ dump_cmd(apr_getopt_t *os, apr_pool_t *pool) { opt_baton_t *opt_baton = baton; - return replay_revisions(opt_baton->session, opt_baton->url, + svn_ra_session_t *extra_ra_session; + const char *repos_root; + + SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, + opt_baton->url, NULL, + opt_baton->ctx, pool, pool)); + SVN_ERR(svn_ra_get_repos_root2(extra_ra_session, &repos_root, pool)); + SVN_ERR(svn_ra_reparent(extra_ra_session, repos_root, pool)); + + return replay_revisions(opt_baton->session, extra_ra_session, opt_baton->start_revision.value.number, opt_baton->end_revision.value.number, opt_baton->quiet, opt_baton->incremental, pool); @@ -546,8 +715,8 @@ load_cmd(apr_getopt_t *os, opt_baton_t *opt_baton = baton; svn_ra_session_t *aux_session; - SVN_ERR(svn_client_open_ra_session(&aux_session, opt_baton->url, - opt_baton->ctx, pool)); + SVN_ERR(svn_client_open_ra_session2(&aux_session, opt_baton->url, NULL, + opt_baton->ctx, pool, pool)); return load_revisions(opt_baton->session, aux_session, opt_baton->url, opt_baton->quiet, pool); } @@ -565,9 +734,9 @@ help_cmd(apr_getopt_t *os, "\n" "Available subcommands:\n"); - return svn_opt_print_help3(os, "svnrdump", FALSE, FALSE, NULL, header, - svnrdump__cmd_table, svnrdump__options, NULL, - NULL, pool); + return svn_opt_print_help4(os, "svnrdump", FALSE, FALSE, FALSE, NULL, + header, svnrdump__cmd_table, svnrdump__options, + NULL, NULL, pool); } /* Examine the OPT_BATON's 'start_revision' and 'end_revision' @@ -676,11 +845,11 @@ main(int argc, const char **argv) svn_boolean_t no_auth_cache = FALSE; svn_boolean_t trust_server_cert = FALSE; svn_boolean_t non_interactive = FALSE; + svn_boolean_t force_interactive = FALSE; apr_array_header_t *config_options = NULL; apr_getopt_t *os; const char *first_arg; apr_array_header_t *received_opts; - apr_allocator_t *allocator; int i; if (svn_cmdline_init ("svnrdump", stderr) != EXIT_SUCCESS) @@ -689,13 +858,7 @@ main(int argc, const char **argv) /* Create our top-level pool. Use a separate mutexless allocator, * given this application is single threaded. */ - if (apr_allocator_create(&allocator)) - return EXIT_FAILURE; - - apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE); - - pool = svn_pool_create_ex(NULL, allocator); - apr_allocator_owner_set(allocator, pool); + pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); opt_baton = apr_pcalloc(pool, sizeof(*opt_baton)); opt_baton->start_revision.kind = svn_opt_revision_unspecified; @@ -801,6 +964,9 @@ main(int argc, const char **argv) case opt_non_interactive: non_interactive = TRUE; break; + case opt_force_interactive: + force_interactive = TRUE; + break; case opt_incremental: opt_baton->incremental = TRUE; break; @@ -819,6 +985,16 @@ main(int argc, const char **argv) } } + /* The --non-interactive and --force-interactive options are mutually + * exclusive. */ + if (non_interactive && force_interactive) + { + err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); + return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + } + if (opt_baton->help) { subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table, @@ -858,9 +1034,10 @@ main(int argc, const char **argv) err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, pool); if (err) return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); - svn_error_clear(svn_cmdline_fprintf(stderr, pool, - _("Unknown command: '%s'\n"), - first_arg_utf8)); + svn_error_clear( + svn_cmdline_fprintf(stderr, pool, + _("Unknown subcommand: '%s'\n"), + first_arg_utf8)); SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); svn_pool_destroy(pool); exit(EXIT_FAILURE); @@ -948,31 +1125,59 @@ main(int argc, const char **argv) opt_baton->url = svn_uri_canonicalize(repos_url, pool); } + if (strcmp(subcommand->name, "load") == 0) + { + /* + * By default (no --*-interactive options given), the 'load' subcommand + * is interactive unless username and password were provided on the + * command line. This allows prompting for auth creds to work without + * requiring users to remember to use --force-interactive. + * See issue #3913, "svnrdump load is not working in interactive mode". + */ + if (!non_interactive && !force_interactive) + force_interactive = (username == NULL || password == NULL); + } + + non_interactive = !svn_cmdline__be_interactive(non_interactive, + force_interactive); + SVNRDUMP_ERR(init_client_context(&(opt_baton->ctx), non_interactive, username, password, config_dir, + opt_baton->url, no_auth_cache, trust_server_cert, config_options, pool)); - SVNRDUMP_ERR(svn_client_open_ra_session(&(opt_baton->session), - opt_baton->url, - opt_baton->ctx, pool)); + err = svn_client_open_ra_session2(&(opt_baton->session), + opt_baton->url, NULL, + opt_baton->ctx, pool, pool); /* Have sane opt_baton->start_revision and end_revision defaults if unspecified. */ - SVNRDUMP_ERR(svn_ra_get_latest_revnum(opt_baton->session, - &latest_revision, pool)); + if (!err) + err = svn_ra_get_latest_revnum(opt_baton->session, &latest_revision, pool); /* Make sure any provided revisions make sense. */ - SVNRDUMP_ERR(validate_and_resolve_revisions(opt_baton, - latest_revision, pool)); + if (!err) + err = validate_and_resolve_revisions(opt_baton, latest_revision, pool); /* Dispatch the subcommand */ - SVNRDUMP_ERR((*subcommand->cmd_func)(os, opt_baton, pool)); + if (!err) + err = (*subcommand->cmd_func)(os, opt_baton, pool); + + if (err && err->apr_err == SVN_ERR_AUTHN_FAILED && non_interactive) + { + err = svn_error_quick_wrap(err, + _("Authentication failed and interactive" + " prompting is disabled; see the" + " --force-interactive option")); + } + + SVNRDUMP_ERR(err); svn_pool_destroy(pool); |