diff options
author | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
commit | 3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch) | |
tree | 7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/svnadmin/svnadmin.c | |
parent | a55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff) |
Notes
Diffstat (limited to 'subversion/svnadmin/svnadmin.c')
-rw-r--r-- | subversion/svnadmin/svnadmin.c | 823 |
1 files changed, 593 insertions, 230 deletions
diff --git a/subversion/svnadmin/svnadmin.c b/subversion/svnadmin/svnadmin.c index 2ee5410d24e3..eb26c5a8f0fc 100644 --- a/subversion/svnadmin/svnadmin.c +++ b/subversion/svnadmin/svnadmin.c @@ -23,7 +23,6 @@ #include <apr_file_io.h> -#include <apr_signal.h> #include "svn_hash.h" #include "svn_pools.h" @@ -48,6 +47,8 @@ #include "private/svn_opt_private.h" #include "private/svn_sorts_private.h" #include "private/svn_subr_private.h" +#include "private/svn_cmdline_private.h" +#include "private/svn_fspath.h" #include "svn_private_config.h" @@ -59,46 +60,7 @@ * The current threshold is 64MB. */ #define BLOCK_READ_CACHE_THRESHOLD (0x40 * 0x100000) -/* A flag to see if we've been cancelled by the client or not. */ -static volatile sig_atomic_t cancelled = FALSE; - -/* A signal handler to support cancellation. */ -static void -signal_handler(int signum) -{ - apr_signal(signum, SIG_IGN); - cancelled = TRUE; -} - - -/* A helper to set up the cancellation signal handlers. */ -static void -setup_cancellation_signals(void (*handler)(int signum)) -{ - apr_signal(SIGINT, handler); -#ifdef SIGBREAK - /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ - apr_signal(SIGBREAK, handler); -#endif -#ifdef SIGHUP - apr_signal(SIGHUP, handler); -#endif -#ifdef SIGTERM - apr_signal(SIGTERM, handler); -#endif -} - - -/* Our cancellation callback. */ -static svn_error_t * -check_cancel(void *baton) -{ - if (cancelled) - return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); - else - return SVN_NO_ERROR; -} - +static svn_cancel_func_t check_cancel = NULL; /* Custom filesystem warning function. */ static void @@ -111,34 +73,6 @@ warning_func(void *baton, } -/* Helper to open a repository and set a warning func (so we don't - * SEGFAULT when libsvn_fs's default handler gets run). */ -static svn_error_t * -open_repos(svn_repos_t **repos, - const char *path, - apr_pool_t *pool) -{ - /* Enable the "block-read" feature (where it applies)? */ - svn_boolean_t use_block_read - = svn_cache_config_get()->cache_size > BLOCK_READ_CACHE_THRESHOLD; - - /* construct FS configuration parameters: enable caches for r/o data */ - apr_hash_t *fs_config = apr_hash_make(pool); - svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1"); - svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1"); - svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2"); - svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS, - svn_uuid_generate(pool)); - svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, - use_block_read ? "1" : "0"); - - /* now, open the requested repository */ - SVN_ERR(svn_repos_open3(repos, path, fs_config, pool, pool)); - svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL); - return SVN_NO_ERROR; -} - - /* Version compatibility check */ static svn_error_t * check_lib_versions(void) @@ -166,11 +100,13 @@ static svn_opt_subcommand_t subcommand_delrevprop, subcommand_deltify, subcommand_dump, + subcommand_dump_revprops, subcommand_freeze, subcommand_help, subcommand_hotcopy, subcommand_info, subcommand_load, + subcommand_load_revprops, subcommand_list_dblogs, subcommand_list_unused_dblogs, subcommand_lock, @@ -214,7 +150,12 @@ enum svnadmin__cmdline_options_t svnadmin__pre_1_6_compatible, svnadmin__compatible_version, svnadmin__check_normalization, - svnadmin__metadata_only + svnadmin__metadata_only, + svnadmin__no_flush_to_disk, + svnadmin__normalize_props, + svnadmin__exclude, + svnadmin__include, + svnadmin__glob }; /* Option codes and descriptions. @@ -333,6 +274,26 @@ static const apr_getopt_option_t options_table[] = " checking against external corruption in\n" " Subversion 1.9+ format repositories.\n")}, + {"no-flush-to-disk", svnadmin__no_flush_to_disk, 0, + N_("disable flushing to disk during the operation\n" + " (faster, but unsafe on power off)")}, + + {"normalize-props", svnadmin__normalize_props, 0, + N_("normalize property values found in the dumpstream\n" + " (currently, only translates non-LF line endings)")}, + + {"exclude", svnadmin__exclude, 1, + N_("filter out nodes with given prefix(es) from dump")}, + + {"include", svnadmin__include, 1, + N_("filter out nodes without given prefix(es) from dump")}, + + {"pattern", svnadmin__glob, 0, + N_("treat the path prefixes as file glob patterns.\n" + " Glob special characters are '*' '?' '[]' and '\\'.\n" + " Character '/' is not treated specially, so\n" + " pattern /*/foo matches paths /a/foo and /a/b/foo.") }, + {NULL} }; @@ -359,7 +320,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {"delrevprop", subcommand_delrevprop, {0}, N_ ("usage: 1. svnadmin delrevprop REPOS_PATH -r REVISION NAME\n" - " 2. svnadmin delrevprop REPO_PATH -t TXN NAME\n\n" + " 2. svnadmin delrevprop REPOS_PATH -t TXN NAME\n\n" "1. Delete the property NAME on revision REVISION.\n\n" "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" "trigger the revision property-related hooks (for example, if you want\n" @@ -389,8 +350,24 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = "only the paths changed in that revision; otherwise it will describe\n" "every path present in the repository as of that revision. (In either\n" "case, the second and subsequent revisions, if any, describe only paths\n" - "changed in those revisions.)\n"), - {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M'} }, + "changed in those revisions.)\n" + "\n" + "Using --exclude or --include gives results equivalent to authz-based\n" + "path exclusions. In particular, when the source of a copy is\n" + "excluded, the copy is transformed into an add (unlike in 'svndumpfilter').\n"), + {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M', 'F', + svnadmin__exclude, svnadmin__include, svnadmin__glob }, + {{'F', N_("write to file ARG instead of stdout")}} }, + + {"dump-revprops", subcommand_dump_revprops, {0}, N_ + ("usage: svnadmin dump-revprops REPOS_PATH [-r LOWER[:UPPER]]\n\n" + "Dump the revision properties of filesystem to stdout in a 'dumpfile'\n" + "portable format, sending feedback to stderr. Dump revisions\n" + "LOWER rev through UPPER rev. If no revisions are given, dump the\n" + "properties for all revisions. If only LOWER is given, dump the\n" + "properties for that one revision.\n"), + {'r', 'q', 'F'}, + {{'F', N_("write to file ARG instead of stdout")}} }, {"freeze", subcommand_freeze, {0}, N_ ("usage: 1. svnadmin freeze REPOS_PATH PROGRAM [ARG...]\n" @@ -401,7 +378,8 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = "2. Like 1 except all repositories listed in FILE are locked. The file\n" " format is repository paths separated by newlines. Repositories are\n" " locked in the same order as they are listed in the file.\n"), - {'F'} }, + {'F'}, + {{'F', N_("read repository paths from file ARG")}} }, {"help", subcommand_help, {"?", "h"}, N_ ("usage: svnadmin help [SUBCOMMAND...]\n\n" @@ -443,14 +421,28 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid, svnadmin__ignore_dates, svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook, - svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} }, + svnadmin__parent_dir, svnadmin__normalize_props, + svnadmin__bypass_prop_validation, 'M', + svnadmin__no_flush_to_disk, 'F'}, + {{'F', N_("read from file ARG instead of stdin")}} }, + + {"load-revprops", subcommand_load_revprops, {0}, N_ + ("usage: svnadmin load-revprops REPOS_PATH\n\n" + "Read a 'dumpfile'-formatted stream from stdin, setting the revision\n" + "properties in the repository's filesystem. Revisions not found in the\n" + "repository will cause an error. Progress feedback is sent to stdout.\n" + "If --revision is specified, limit the loaded revisions to only those\n" + "in the dump stream whose revision numbers match the specified range.\n"), + {'q', 'r', svnadmin__force_uuid, svnadmin__normalize_props, + svnadmin__bypass_prop_validation, svnadmin__no_flush_to_disk, 'F'}, + {{'F', N_("read from file ARG instead of stdin")}} }, {"lock", subcommand_lock, {0}, N_ ("usage: svnadmin lock REPOS_PATH PATH USERNAME COMMENT-FILE [TOKEN]\n\n" "Lock PATH by USERNAME setting comments from COMMENT-FILE.\n" "If provided, use TOKEN as lock token. Use --bypass-hooks to avoid\n" "triggering the pre-lock and post-lock hook scripts.\n"), - {svnadmin__bypass_hooks} }, + {svnadmin__bypass_hooks, 'q'} }, {"lslocks", subcommand_lslocks, {0}, N_ ("usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n\n" @@ -460,8 +452,12 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {"lstxns", subcommand_lstxns, {0}, N_ ("usage: svnadmin lstxns REPOS_PATH\n\n" - "Print the names of all uncommitted transactions.\n"), - {0} }, + "Print the names of uncommitted transactions. With -rN skip the output\n" + "of those that have a base revision more recent than rN. Transactions\n" + "with base revisions much older than HEAD are likely to have been\n" + "abandonded and are candidates to be removed.\n"), + {'r'}, + { {'r', "transaction base revision ARG"} } }, {"pack", subcommand_pack, {0}, N_ ("usage: svnadmin pack REPOS_PATH\n\n" @@ -480,7 +476,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {"rmlocks", subcommand_rmlocks, {0}, N_ ("usage: svnadmin rmlocks REPOS_PATH LOCKED_PATH...\n\n" "Unconditionally remove lock from each LOCKED_PATH.\n"), - {0} }, + {'q'} }, {"rmtxns", subcommand_rmtxns, {0}, N_ ("usage: svnadmin rmtxns REPOS_PATH TXN_NAME...\n\n" @@ -524,7 +520,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = "Unlock LOCKED_PATH (as USERNAME) after verifying that the token\n" "associated with the lock matches TOKEN. Use --bypass-hooks to avoid\n" "triggering the pre-unlock and post-unlock hook scripts.\n"), - {svnadmin__bypass_hooks} }, + {svnadmin__bypass_hooks, 'q'} }, {"upgrade", subcommand_upgrade, {0}, N_ ("usage: svnadmin upgrade REPOS_PATH\n\n" @@ -576,16 +572,53 @@ struct svnadmin_opt_state svn_boolean_t metadata_only; /* --metadata-only */ svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */ svn_boolean_t ignore_dates; /* --ignore-dates */ + svn_boolean_t no_flush_to_disk; /* --no-flush-to-disk */ + svn_boolean_t normalize_props; /* --normalize_props */ enum svn_repos_load_uuid uuid_action; /* --ignore-uuid, --force-uuid */ apr_uint64_t memory_cache_size; /* --memory-cache-size M */ const char *parent_dir; /* --parent-dir */ - svn_stringbuf_t *filedata; /* --file */ + const char *file; /* --file */ + apr_array_header_t *exclude; /* --exclude */ + apr_array_header_t *include; /* --include */ + svn_boolean_t glob; /* --pattern */ const char *config_dir; /* Overriding Configuration Directory */ }; +/* Helper to open a repository and set a warning func (so we don't + * SEGFAULT when libsvn_fs's default handler gets run). */ +static svn_error_t * +open_repos(svn_repos_t **repos, + const char *path, + struct svnadmin_opt_state *opt_state, + apr_pool_t *pool) +{ + /* Enable the "block-read" feature (where it applies)? */ + svn_boolean_t use_block_read + = svn_cache_config_get()->cache_size > BLOCK_READ_CACHE_THRESHOLD; + + /* construct FS configuration parameters: enable caches for r/o data */ + apr_hash_t *fs_config = apr_hash_make(pool); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1"); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1"); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS, "1"); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2"); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS, + svn_uuid_generate(pool)); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, + use_block_read ? "1" : "0"); + svn_hash_sets(fs_config, SVN_FS_CONFIG_NO_FLUSH_TO_DISK, + opt_state->no_flush_to_disk ? "1" : "0"); + + /* now, open the requested repository */ + SVN_ERR(svn_repos_open3(repos, path, fs_config, pool, pool)); + svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL); + return SVN_NO_ERROR; +} + + /* Set *REVNUM to the revision specified by REVISION (or to SVN_INVALID_REVNUM if that has the type 'unspecified'), possibly making use of the YOUNGEST revision number in REPOS. */ @@ -614,16 +647,25 @@ get_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision, return SVN_NO_ERROR; } -/* Set *PATH to an internal-style, UTF8-encoded, local dirent path - allocated from POOL and parsed from raw command-line argument ARG. */ +/* Set *FSPATH to an internal-style fspath parsed from ARG. */ static svn_error_t * -target_arg_to_dirent(const char **dirent, +target_arg_to_fspath(const char **fspath, const char *arg, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - const char *path; + /* ### Using a private API. This really shouldn't be needed. */ + *fspath = svn_fspath__canonicalize(arg, result_pool); + return SVN_NO_ERROR; +} - SVN_ERR(svn_utf_cstring_to_utf8(&path, arg, pool)); +/* Set *DIRENT to an internal-style, local dirent path + allocated from POOL and parsed from PATH. */ +static svn_error_t * +target_arg_to_dirent(const char **dirent, + const char *path, + apr_pool_t *pool) +{ if (svn_path_is_url(path)) return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Path '%s' is not a local path"), path); @@ -665,8 +707,12 @@ parse_args(apr_array_header_t **args, if (num_args) while (os->ind < os->argc) - APR_ARRAY_PUSH(*args, const char *) = - apr_pstrdup(pool, os->argv[os->ind++]); + { + const char *arg; + + SVN_ERR(svn_utf_cstring_to_utf8(&arg, os->argv[os->ind++], pool)); + APR_ARRAY_PUSH(*args, const char *) = arg; + } } return SVN_NO_ERROR; @@ -692,7 +738,7 @@ subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool) svn_repos_t *repos; (void)svn_error_set_malfunction_handler(crashtest_malfunction_handler); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); SVN_ERR(svn_cmdline_printf(pool, _("Successfully opened repository '%s'.\n" "Will now crash to simulate a crashing " @@ -809,7 +855,7 @@ subcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); @@ -1066,8 +1112,7 @@ repos_notify_handler(void *baton, return; case svn_repos_notify_mutex_acquired: - /* Enable cancellation signal handlers. */ - setup_cancellation_signals(signal_handler); + svn_cmdline__setup_cancellation_handler(); return; case svn_repos_notify_recover_start: @@ -1126,6 +1171,24 @@ repos_notify_handler(void *baton, _("* Copied revisions from %ld to %ld.\n"), notify->start_revision, notify->end_revision)); } + return; + + case svn_repos_notify_pack_noop: + /* For best backward compatibility, we keep silent if there were just + no more shards to pack. */ + if (notify->shard == -1) + { + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("svnadmin: Warning - this repository is not sharded." + " Packing has no effect.\n"))); + } + return; + + case svn_repos_notify_load_revprop_set: + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("Properties set on revision %ld.\n"), + notify->new_revision)); + return; default: return; @@ -1172,57 +1235,207 @@ recode_stream_create(FILE *std_stream, apr_pool_t *pool) return rw_stream; } - -/* This implements `svn_opt_subcommand_t'. */ +/* Read the min / max revision from the OPT_STATE, verify them against REPOS + and return them in *LOWER and *UPPER, respectively. Use SCRATCH_POOL + for temporary allocations. */ static svn_error_t * -subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool) +get_dump_range(svn_revnum_t *lower, + svn_revnum_t *upper, + svn_repos_t *repos, + struct svnadmin_opt_state *opt_state, + apr_pool_t *scratch_pool) { - struct svnadmin_opt_state *opt_state = baton; - svn_repos_t *repos; svn_fs_t *fs; - svn_stream_t *stdout_stream; - svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM; svn_revnum_t youngest; - svn_stream_t *feedback_stream = NULL; - /* Expect no more arguments. */ - SVN_ERR(parse_args(NULL, os, 0, 0, pool)); + *lower = SVN_INVALID_REVNUM; + *upper = SVN_INVALID_REVNUM; - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); fs = svn_repos_fs(repos); - SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); + SVN_ERR(svn_fs_youngest_rev(&youngest, fs, scratch_pool)); /* Find the revision numbers at which to start and end. */ - SVN_ERR(get_revnum(&lower, &opt_state->start_revision, - youngest, repos, pool)); - SVN_ERR(get_revnum(&upper, &opt_state->end_revision, - youngest, repos, pool)); + SVN_ERR(get_revnum(lower, &opt_state->start_revision, + youngest, repos, scratch_pool)); + SVN_ERR(get_revnum(upper, &opt_state->end_revision, + youngest, repos, scratch_pool)); /* Fill in implied revisions if necessary. */ - if (lower == SVN_INVALID_REVNUM) + if (*lower == SVN_INVALID_REVNUM) { - lower = 0; - upper = youngest; + *lower = 0; + *upper = youngest; } - else if (upper == SVN_INVALID_REVNUM) + else if (*upper == SVN_INVALID_REVNUM) { - upper = lower; + *upper = *lower; } - if (lower > upper) + if (*lower > *upper) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("First revision cannot be higher than second")); - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); + return SVN_NO_ERROR; +} + +/* Compare the node-path PATH with the (const char *) prefixes in PFXLIST. + * Return TRUE if any prefix is a prefix of PATH (matching whole path + * components); FALSE otherwise. + * PATH starts with a '/', as do the (const char *) paths in PREFIXES. */ +/* This function is a duplicate of svndumpfilter.c:ary_prefix_match(). */ +static svn_boolean_t +ary_prefix_match(const apr_array_header_t *pfxlist, const char *path) +{ + int i; + size_t path_len = strlen(path); + + for (i = 0; i < pfxlist->nelts; i++) + { + const char *pfx = APR_ARRAY_IDX(pfxlist, i, const char *); + size_t pfx_len = strlen(pfx); + + if (path_len < pfx_len) + continue; + if (strncmp(path, pfx, pfx_len) == 0 + && (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] == '/')) + return TRUE; + } + + return FALSE; +} + +/* Baton for dump_filter_func(). */ +struct dump_filter_baton_t +{ + apr_array_header_t *prefixes; + svn_boolean_t glob; + svn_boolean_t do_exclude; +}; + +/* Implements svn_repos_dump_filter_func_t. */ +static svn_error_t * +dump_filter_func(svn_boolean_t *include, + svn_fs_root_t *root, + const char *path, + void *baton, + apr_pool_t *scratch_pool) +{ + struct dump_filter_baton_t *b = baton; + const svn_boolean_t matches = + (b->glob + ? svn_cstring_match_glob_list(path, b->prefixes) + : ary_prefix_match(b->prefixes, path)); + + *include = b->do_exclude ? !matches : matches; + return SVN_NO_ERROR; +} + +/* This implements `svn_opt_subcommand_t'. */ +static svn_error_t * +subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + struct svnadmin_opt_state *opt_state = baton; + svn_repos_t *repos; + svn_stream_t *out_stream; + svn_revnum_t lower, upper; + svn_stream_t *feedback_stream = NULL; + struct dump_filter_baton_t filter_baton = {0}; + + /* Expect no more arguments. */ + SVN_ERR(parse_args(NULL, os, 0, 0, pool)); + + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); + SVN_ERR(get_dump_range(&lower, &upper, repos, opt_state, pool)); + + /* Open the file or STDOUT, depending on whether -F was specified. */ + if (opt_state->file) + { + apr_file_t *file; + + /* Overwrite existing files, same as with > redirection. */ + SVN_ERR(svn_io_file_open(&file, opt_state->file, + APR_WRITE | APR_CREATE | APR_TRUNCATE + | APR_BUFFERED, APR_OS_DEFAULT, pool)); + out_stream = svn_stream_from_aprfile2(file, FALSE, pool); + } + else + SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); /* Progress feedback goes to STDERR, unless they asked to suppress it. */ if (! opt_state->quiet) feedback_stream = recode_stream_create(stderr, pool); - SVN_ERR(svn_repos_dump_fs3(repos, stdout_stream, lower, upper, + /* Initialize the filter baton. */ + filter_baton.glob = opt_state->glob; + + if (opt_state->exclude && !opt_state->include) + { + filter_baton.prefixes = opt_state->exclude; + filter_baton.do_exclude = TRUE; + } + else if (opt_state->include && !opt_state->exclude) + { + filter_baton.prefixes = opt_state->include; + filter_baton.do_exclude = FALSE; + } + else if (opt_state->include && opt_state->exclude) + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'--exclude' and '--include' options " + "cannot be used simultaneously")); + } + + SVN_ERR(svn_repos_dump_fs4(repos, out_stream, lower, upper, opt_state->incremental, opt_state->use_deltas, + TRUE, TRUE, !opt_state->quiet ? repos_notify_handler : NULL, - feedback_stream, check_cancel, NULL, pool)); + feedback_stream, + filter_baton.prefixes ? dump_filter_func : NULL, + &filter_baton, + check_cancel, NULL, pool)); + + return SVN_NO_ERROR; +} + +/* This implements `svn_opt_subcommand_t'. */ +static svn_error_t * +subcommand_dump_revprops(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + struct svnadmin_opt_state *opt_state = baton; + svn_repos_t *repos; + svn_stream_t *out_stream; + svn_revnum_t lower, upper; + svn_stream_t *feedback_stream = NULL; + + /* Expect no more arguments. */ + SVN_ERR(parse_args(NULL, os, 0, 0, pool)); + + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); + SVN_ERR(get_dump_range(&lower, &upper, repos, opt_state, pool)); + + /* Open the file or STDOUT, depending on whether -F was specified. */ + if (opt_state->file) + { + apr_file_t *file; + + /* Overwrite existing files, same as with > redirection. */ + SVN_ERR(svn_io_file_open(&file, opt_state->file, + APR_WRITE | APR_CREATE | APR_TRUNCATE + | APR_BUFFERED, APR_OS_DEFAULT, pool)); + out_stream = svn_stream_from_aprfile2(file, FALSE, pool); + } + else + SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); + + /* Progress feedback goes to STDERR, unless they asked to suppress it. */ + if (! opt_state->quiet) + feedback_stream = recode_stream_create(stderr, pool); + + SVN_ERR(svn_repos_dump_fs4(repos, out_stream, lower, upper, + FALSE, FALSE, TRUE, FALSE, + !opt_state->quiet ? repos_notify_handler : NULL, + feedback_stream, NULL, NULL, + check_cancel, NULL, pool)); return SVN_NO_ERROR; } @@ -1268,13 +1481,13 @@ subcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool) int i; struct freeze_baton_t b; - SVN_ERR(svn_opt_parse_all_args(&args, os, pool)); + SVN_ERR(parse_args(&args, os, -1, -1, pool)); if (!args->nelts) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, _("No program provided")); - if (!opt_state->filedata) + if (!opt_state->file) { /* One repository on the command line. */ paths = apr_array_make(pool, 1, sizeof(const char *)); @@ -1282,9 +1495,11 @@ subcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool) } else { + svn_stringbuf_t *buf; const char *utf8; - /* All repositories in filedata. */ - SVN_ERR(svn_utf_cstring_to_utf8(&utf8, opt_state->filedata->data, pool)); + /* Read repository paths from the -F file. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, opt_state->file, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8, buf->data, pool)); paths = svn_cstring_split(utf8, "\r\n", FALSE, pool); } @@ -1362,69 +1577,156 @@ optrev_to_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *opt_rev) return SVN_NO_ERROR; } - -/* This implements `svn_opt_subcommand_t'. */ +/* Read the min / max revision from the OPT_STATE, verify them and return + them in *LOWER and *UPPER, respectively. */ static svn_error_t * -subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool) +get_load_range(svn_revnum_t *lower, + svn_revnum_t *upper, + struct svnadmin_opt_state *opt_state) { - svn_error_t *err; - struct svnadmin_opt_state *opt_state = baton; - svn_repos_t *repos; - svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM; - svn_stream_t *stdin_stream; - svn_stream_t *feedback_stream = NULL; - - /* Expect no more arguments. */ - SVN_ERR(parse_args(NULL, os, 0, 0, pool)); - /* Find the revision numbers at which to start and end. We only support a limited set of revision kinds: number and unspecified. */ - SVN_ERR(optrev_to_revnum(&lower, &opt_state->start_revision)); - SVN_ERR(optrev_to_revnum(&upper, &opt_state->end_revision)); + SVN_ERR(optrev_to_revnum(lower, &opt_state->start_revision)); + SVN_ERR(optrev_to_revnum(upper, &opt_state->end_revision)); /* Fill in implied revisions if necessary. */ - if ((upper == SVN_INVALID_REVNUM) && (lower != SVN_INVALID_REVNUM)) + if ((*upper == SVN_INVALID_REVNUM) && (*lower != SVN_INVALID_REVNUM)) { - upper = lower; + *upper = *lower; } - else if ((upper != SVN_INVALID_REVNUM) && (lower == SVN_INVALID_REVNUM)) + else if ((*upper != SVN_INVALID_REVNUM) && (*lower == SVN_INVALID_REVNUM)) { - lower = upper; + *lower = *upper; } /* Ensure correct range ordering. */ - if (lower > upper) + if (*lower > *upper) { return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("First revision cannot be higher than second")); } - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + return SVN_NO_ERROR; +} - /* Read the stream from STDIN. Users can redirect a file. */ - SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool)); + +/* This implements `svn_opt_subcommand_t'. */ +static svn_error_t * +subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + svn_error_t *err; + struct svnadmin_opt_state *opt_state = baton; + svn_repos_t *repos; + svn_revnum_t lower, upper; + svn_stream_t *in_stream; + svn_stream_t *feedback_stream = NULL; + + /* Expect no more arguments. */ + SVN_ERR(parse_args(NULL, os, 0, 0, pool)); + + /* Find the revision numbers at which to start and end. We only + support a limited set of revision kinds: number and unspecified. */ + SVN_ERR(get_load_range(&lower, &upper, opt_state)); + + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); + + /* Open the file or STDIN, depending on whether -F was specified. */ + if (opt_state->file) + SVN_ERR(svn_stream_open_readonly(&in_stream, opt_state->file, + pool, pool)); + else + SVN_ERR(svn_stream_for_stdin2(&in_stream, TRUE, pool)); /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ if (! opt_state->quiet) feedback_stream = recode_stream_create(stdout, pool); - err = svn_repos_load_fs5(repos, stdin_stream, lower, upper, + err = svn_repos_load_fs6(repos, in_stream, lower, upper, opt_state->uuid_action, opt_state->parent_dir, opt_state->use_pre_commit_hook, opt_state->use_post_commit_hook, !opt_state->bypass_prop_validation, opt_state->ignore_dates, + opt_state->normalize_props, opt_state->quiet ? NULL : repos_notify_handler, feedback_stream, check_cancel, NULL, pool); - if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE) - return svn_error_quick_wrap(err, - _("Invalid property value found in " - "dumpstream; consider repairing the source " - "or using --bypass-prop-validation while " - "loading.")); + + if (svn_error_find_cause(err, SVN_ERR_BAD_PROPERTY_VALUE_EOL)) + { + return svn_error_quick_wrap(err, + _("A property with invalid line ending " + "found in dumpstream; consider using " + "--normalize-props while loading.")); + } + else if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE) + { + return svn_error_quick_wrap(err, + _("Invalid property value found in " + "dumpstream; consider repairing the " + "source or using --bypass-prop-validation " + "while loading.")); + } + return err; } +static svn_error_t * +subcommand_load_revprops(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + svn_error_t *err; + struct svnadmin_opt_state *opt_state = baton; + svn_repos_t *repos; + svn_revnum_t lower, upper; + svn_stream_t *in_stream; + + svn_stream_t *feedback_stream = NULL; + + /* Expect no more arguments. */ + SVN_ERR(parse_args(NULL, os, 0, 0, pool)); + + /* Find the revision numbers at which to start and end. We only + support a limited set of revision kinds: number and unspecified. */ + SVN_ERR(get_load_range(&lower, &upper, opt_state)); + + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); + + /* Open the file or STDIN, depending on whether -F was specified. */ + if (opt_state->file) + SVN_ERR(svn_stream_open_readonly(&in_stream, opt_state->file, + pool, pool)); + else + SVN_ERR(svn_stream_for_stdin2(&in_stream, TRUE, pool)); + + /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ + if (! opt_state->quiet) + feedback_stream = recode_stream_create(stdout, pool); + + err = svn_repos_load_fs_revprops(repos, in_stream, lower, upper, + !opt_state->bypass_prop_validation, + opt_state->ignore_dates, + opt_state->normalize_props, + opt_state->quiet ? NULL + : repos_notify_handler, + feedback_stream, check_cancel, NULL, pool); + + if (svn_error_find_cause(err, SVN_ERR_BAD_PROPERTY_VALUE_EOL)) + { + return svn_error_quick_wrap(err, + _("A property with invalid line ending " + "found in dumpstream; consider using " + "--normalize-props while loading.")); + } + else if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE) + { + return svn_error_quick_wrap(err, + _("Invalid property value found in " + "dumpstream; consider repairing the " + "source or using --bypass-prop-validation " + "while loading.")); + } + + return err; +} /* This implements `svn_opt_subcommand_t'. */ static svn_error_t * @@ -1434,21 +1736,46 @@ subcommand_lstxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) svn_repos_t *repos; svn_fs_t *fs; apr_array_header_t *txns; + apr_pool_t *iterpool; + svn_revnum_t youngest, limit; int i; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); + if (opt_state->end_revision.kind != svn_opt_revision_unspecified) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Revision range is not allowed")); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); SVN_ERR(svn_fs_list_transactions(&txns, fs, pool)); - /* Loop, printing revisions. */ + SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); + SVN_ERR(get_revnum(&limit, &opt_state->start_revision, youngest, repos, + pool)); + + iterpool = svn_pool_create(pool); for (i = 0; i < txns->nelts; i++) { - SVN_ERR(svn_cmdline_printf(pool, "%s\n", - APR_ARRAY_IDX(txns, i, const char *))); + const char *name = APR_ARRAY_IDX(txns, i, const char *); + svn_boolean_t show = TRUE; + + svn_pool_clear(iterpool); + if (limit != SVN_INVALID_REVNUM) + { + svn_fs_txn_t *txn; + svn_revnum_t base; + + SVN_ERR(svn_fs_open_txn(&txn, fs, name, iterpool)); + base = svn_fs_txn_base_revision(txn); + + if (base > limit) + show = FALSE; + } + if (show) + SVN_ERR(svn_cmdline_printf(pool, "%s\n", name)); } + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -1472,7 +1799,7 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Restore default signal handlers until after we have acquired the * exclusive lock so that the user interrupt before we actually * touch the repository. */ - setup_cancellation_signals(SIG_DFL); + svn_cmdline__disable_cancellation_handler(); err = svn_repos_recover4(opt_state->repository_path, TRUE, repos_notify_handler, feedback_stream, @@ -1502,7 +1829,7 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Since db transactions may have been replayed, it's nice to tell people what the latest revision is. It also proves that the recovery actually worked. */ - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); SVN_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos), pool)); SVN_ERR(svn_cmdline_printf(pool, _("The latest repos revision is %ld.\n"), youngest_rev)); @@ -1533,12 +1860,12 @@ list_dblogs(apr_getopt_t *os, void *baton, svn_boolean_t only_unused, style before printing. */ for (i = 0; i < logfiles->nelts; i++) { - const char *log_utf8; - log_utf8 = svn_dirent_join(opt_state->repository_path, + const char *log_path; + log_path = svn_dirent_join(opt_state->repository_path, APR_ARRAY_IDX(logfiles, i, const char *), pool); - log_utf8 = svn_dirent_local_style(log_utf8, pool); - SVN_ERR(svn_cmdline_printf(pool, "%s\n", log_utf8)); + log_path = svn_dirent_local_style(log_path, pool); + SVN_ERR(svn_cmdline_printf(pool, "%s\n", log_path)); } return SVN_NO_ERROR; @@ -1578,24 +1905,21 @@ subcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) int i; apr_pool_t *subpool = svn_pool_create(pool); - SVN_ERR(svn_opt_parse_all_args(&args, os, pool)); + SVN_ERR(parse_args(&args, os, -1, -1, pool)); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); /* All the rest of the arguments are transaction names. */ for (i = 0; i < args->nelts; i++) { const char *txn_name = APR_ARRAY_IDX(args, i, const char *); - const char *txn_name_utf8; svn_error_t *err; svn_pool_clear(subpool); - SVN_ERR(svn_utf_cstring_to_utf8(&txn_name_utf8, txn_name, subpool)); - /* Try to open the txn. If that succeeds, try to abort it. */ - err = svn_fs_open_txn(&txn, fs, txn_name_utf8, subpool); + err = svn_fs_open_txn(&txn, fs, txn_name, subpool); if (! err) err = svn_fs_abort_txn(txn, subpool); @@ -1606,7 +1930,7 @@ subcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) if (err && (err->apr_err == SVN_ERR_FS_TRANSACTION_DEAD)) { svn_error_clear(err); - err = svn_fs_purge_txn(fs, txn_name_utf8, subpool); + err = svn_fs_purge_txn(fs, txn_name, subpool); } /* If we had a real from the txn open, abort, or purge, we clear @@ -1660,7 +1984,7 @@ set_revprop(const char *prop_name, const char *filename, } /* Open the filesystem */ - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); if (opt_state->txn_id) { @@ -1736,7 +2060,7 @@ subcommand_setuuid(apr_getopt_t *os, void *baton, apr_pool_t *pool) if (args->nelts == 1) uuid = APR_ARRAY_IDX(args, 0, const char *); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); return svn_fs_set_uuid(fs, uuid, pool); } @@ -1784,7 +2108,7 @@ subcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ if (! opt_state->quiet) @@ -1819,7 +2143,7 @@ subcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool) "are mutually exclusive")); } - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); @@ -1970,11 +2294,12 @@ subcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) svn_fs_t *fs; int fs_format; const char *uuid; + svn_revnum_t head_rev; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), svn_dirent_local_style(svn_repos_path(repos, pool), @@ -1982,6 +2307,9 @@ subcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); SVN_ERR(svn_cmdline_printf(pool, _("UUID: %s\n"), uuid)); + + SVN_ERR(svn_fs_youngest_rev(&head_rev, fs, pool)); + SVN_ERR(svn_cmdline_printf(pool, _("Revisions: %ld\n"), head_rev)); { int repos_format, minor; svn_version_t *repos_version, *fs_version; @@ -2036,8 +2364,6 @@ subcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) if (!strcmp(info->fs_type, SVN_FS_TYPE_FSFS)) { const svn_fs_fsfs_info_t *fsfs_info = (const void *)info; - svn_revnum_t youngest; - SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); if (fsfs_info->shard_size) SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: yes\n"))); @@ -2053,7 +2379,7 @@ subcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) { const int shard_size = fsfs_info->shard_size; const long shards_packed = fsfs_info->min_unpacked_rev / shard_size; - const long shards_full = (youngest + 1) / shard_size; + const long shards_full = (head_rev + 1) / shard_size; SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shards Packed: %ld/%ld\n"), shards_packed, shards_full)); } @@ -2063,6 +2389,19 @@ subcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) else SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: no\n"))); } + else if (!strcmp(info->fs_type, SVN_FS_TYPE_FSX)) + { + const svn_fs_fsx_info_t *fsx_info = (const void *)info; + + const int shard_size = fsx_info->shard_size; + const long shards_packed = fsx_info->min_unpacked_rev / shard_size; + long shards_full = (head_rev + 1) / shard_size; + + SVN_ERR(svn_cmdline_printf(pool, _("FSX Shard Size: %d\n"), + shard_size)); + SVN_ERR(svn_cmdline_printf(pool, _("FSX Shards Packed: %ld/%ld\n"), + shards_packed, shards_full)); + } } { @@ -2097,7 +2436,6 @@ subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) const char *lock_path; const char *comment_file_name; svn_stringbuf_t *file_contents; - const char *lock_path_utf8; svn_lock_t *lock; const char *lock_token = NULL; @@ -2113,7 +2451,7 @@ subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_ERR(target_arg_to_dirent(&comment_file_name, comment_file_name, pool)); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); /* Create an access context describing the user. */ @@ -2124,10 +2462,10 @@ subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_ERR(svn_stringbuf_from_file2(&file_contents, comment_file_name, pool)); - SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool)); + SVN_ERR(target_arg_to_fspath(&lock_path, lock_path, pool, pool)); if (opt_state->bypass_hooks) - SVN_ERR(svn_fs_lock(&lock, fs, lock_path_utf8, + SVN_ERR(svn_fs_lock(&lock, fs, lock_path, lock_token, file_contents->data, /* comment */ 0, /* is_dav_comment */ @@ -2135,7 +2473,7 @@ subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_INVALID_REVNUM, FALSE, pool)); else - SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path_utf8, + SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path, lock_token, file_contents->data, /* comment */ 0, /* is_dav_comment */ @@ -2143,8 +2481,10 @@ subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_INVALID_REVNUM, FALSE, pool)); - SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), - lock_path, username)); + if (! opt_state->quiet) + SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), + lock_path, username)); + return SVN_NO_ERROR; } @@ -2154,7 +2494,7 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) struct svnadmin_opt_state *opt_state = baton; apr_array_header_t *targets; svn_repos_t *repos; - const char *fs_path = "/"; + const char *fs_path; apr_hash_t *locks; apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(pool); @@ -2168,8 +2508,11 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) _("Too many arguments given")); if (targets->nelts) fs_path = APR_ARRAY_IDX(targets, 0, const char *); + else + fs_path = "/"; + SVN_ERR(target_arg_to_fspath(&fs_path, fs_path, pool, pool)); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); /* Fetch all locks on or below the root directory. */ SVN_ERR(svn_repos_fs_get_locks2(&locks, repos, fs_path, svn_depth_infinity, @@ -2227,7 +2570,7 @@ subcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) const char *username; apr_pool_t *subpool = svn_pool_create(pool); - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); /* svn_fs_unlock() demands that some username be associated with the @@ -2243,7 +2586,7 @@ subcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_ERR(svn_fs_set_access(fs, access)); /* Parse out any options. */ - SVN_ERR(svn_opt_parse_all_args(&args, os, pool)); + SVN_ERR(parse_args(&args, os, -1, -1, pool)); /* Our usage requires at least one FS path. */ if (args->nelts == 0) @@ -2254,31 +2597,34 @@ subcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) for (i = 0; i < args->nelts; i++) { const char *lock_path = APR_ARRAY_IDX(args, i, const char *); - const char *lock_path_utf8; svn_lock_t *lock; - SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, subpool)); + SVN_ERR(target_arg_to_fspath(&lock_path, lock_path, subpool, subpool)); /* Fetch the path's svn_lock_t. */ - err = svn_fs_get_lock(&lock, fs, lock_path_utf8, subpool); + err = svn_fs_get_lock(&lock, fs, lock_path, subpool); if (err) goto move_on; if (! lock) { - SVN_ERR(svn_cmdline_printf(subpool, - _("Path '%s' isn't locked.\n"), - lock_path)); + if (! opt_state->quiet) + SVN_ERR(svn_cmdline_printf(subpool, + _("Path '%s' isn't locked.\n"), + lock_path)); continue; } + lock = NULL; /* Don't access LOCK after this point. */ /* Now forcibly destroy the lock. */ - err = svn_fs_unlock(fs, lock_path_utf8, - lock->token, 1 /* force */, subpool); + err = svn_fs_unlock(fs, lock_path, + NULL, 1 /* force */, subpool); if (err) goto move_on; - SVN_ERR(svn_cmdline_printf(subpool, - _("Removed lock on '%s'.\n"), lock->path)); + if (! opt_state->quiet) + SVN_ERR(svn_cmdline_printf(subpool, + _("Removed lock on '%s'.\n"), + lock_path)); move_on: if (err) @@ -2307,7 +2653,6 @@ subcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool) apr_array_header_t *args; const char *username; const char *lock_path; - const char *lock_path_utf8; const char *lock_token = NULL; /* Expect three more arguments: PATH USERNAME TOKEN */ @@ -2318,21 +2663,23 @@ subcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Open the repos/FS, and associate an access context containing USERNAME. */ - SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); fs = svn_repos_fs(repos); SVN_ERR(svn_fs_create_access(&access, username, pool)); SVN_ERR(svn_fs_set_access(fs, access)); - SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool)); + SVN_ERR(target_arg_to_fspath(&lock_path, lock_path, pool, pool)); if (opt_state->bypass_hooks) - SVN_ERR(svn_fs_unlock(fs, lock_path_utf8, lock_token, + SVN_ERR(svn_fs_unlock(fs, lock_path, lock_token, FALSE, pool)); else - SVN_ERR(svn_repos_fs_unlock(repos, lock_path_utf8, lock_token, + SVN_ERR(svn_repos_fs_unlock(repos, lock_path, lock_token, FALSE, pool)); - SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"), - lock_path, username)); + if (! opt_state->quiet) + SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"), + lock_path, username)); + return SVN_NO_ERROR; } @@ -2351,7 +2698,7 @@ subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool)); /* Restore default signal handlers. */ - setup_cancellation_signals(SIG_DFL); + svn_cmdline__disable_cancellation_handler(); err = svn_repos_upgrade2(opt_state->repository_path, TRUE, repos_notify_handler, feedback_stream, pool); @@ -2532,14 +2879,17 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) opt_state.help = TRUE; break; case 'M': - opt_state.memory_cache_size - = 0x100000 * apr_strtoi64(opt_arg, NULL, 0); + { + apr_uint64_t sz_val; + SVN_ERR(svn_cstring_atoui64(&sz_val, opt_arg)); + + opt_state.memory_cache_size = 0x100000 * sz_val; + } break; case 'F': - SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); - SVN_ERR(svn_stringbuf_from_file2(&(opt_state.filedata), - utf8_opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&(opt_state.file), opt_arg, pool)); dash_F_arg = TRUE; + break; case svnadmin__version: opt_state.version = TRUE; break; @@ -2662,6 +3012,29 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) case svnadmin__wait: opt_state.wait = TRUE; break; + case svnadmin__no_flush_to_disk: + opt_state.no_flush_to_disk = TRUE; + break; + case svnadmin__normalize_props: + opt_state.normalize_props = TRUE; + break; + case svnadmin__exclude: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + + if (! opt_state.exclude) + opt_state.exclude = apr_array_make(pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(opt_state.exclude, const char *) = utf8_opt_arg; + break; + case svnadmin__include: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + + if (! opt_state.include) + opt_state.include = apr_array_make(pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(opt_state.include, const char *) = utf8_opt_arg; + break; + case svnadmin__glob: + opt_state.glob = TRUE; + break; default: { SVN_ERR(subcommand_help(NULL, NULL, pool)); @@ -2706,17 +3079,17 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) } else { - const char *first_arg = os->argv[os->ind++]; + const char *first_arg; + + SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++], + pool)); subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg); if (subcommand == NULL) { - const char *first_arg_utf8; - SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, - first_arg, pool)); svn_error_clear( svn_cmdline_fprintf(stderr, pool, _("Unknown subcommand: '%s'\n"), - first_arg_utf8)); + first_arg)); SVN_ERR(subcommand_help(NULL, NULL, pool)); *exit_code = EXIT_FAILURE; return SVN_NO_ERROR; @@ -2781,20 +3154,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) } } - /* Set up our cancellation support. */ - setup_cancellation_signals(signal_handler); - -#ifdef SIGPIPE - /* Disable SIGPIPE generation for the platforms that have it. */ - apr_signal(SIGPIPE, SIG_IGN); -#endif - -#ifdef SIGXFSZ - /* Disable SIGXFSZ generation for the platforms that have it, otherwise - * working with large files when compiled against an APR that doesn't have - * large file support will crash the program, which is uncool. */ - apr_signal(SIGXFSZ, SIG_IGN); -#endif + check_cancel = svn_cmdline__setup_cancellation_handler(); /* Configure FSFS caches for maximum efficiency with svnadmin. * Also, apply the respective command line parameters, if given. */ @@ -2854,5 +3214,8 @@ main(int argc, const char *argv[]) } svn_pool_destroy(pool); + + svn_cmdline__cancellation_exit(); + return exit_code; } |