summaryrefslogtreecommitdiff
path: root/subversion/svnadmin
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
commitcf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch)
treeda27775a2161723ef342e91af41a8b51fedef405 /subversion/svnadmin
parentbb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff)
downloadsubversion-tarball-master.tar.gz
Diffstat (limited to 'subversion/svnadmin')
-rw-r--r--subversion/svnadmin/svnadmin.c874
1 files changed, 685 insertions, 189 deletions
diff --git a/subversion/svnadmin/svnadmin.c b/subversion/svnadmin/svnadmin.c
index cfbcf66..2ee5410 100644
--- a/subversion/svnadmin/svnadmin.c
+++ b/subversion/svnadmin/svnadmin.c
@@ -39,19 +39,26 @@
#include "svn_cache_config.h"
#include "svn_version.h"
#include "svn_props.h"
+#include "svn_sorts.h"
#include "svn_time.h"
#include "svn_user.h"
#include "svn_xml.h"
+#include "private/svn_cmdline_private.h"
#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 "svn_private_config.h"
/*** Code. ***/
+/* FSFS format 7's "block-read" feature performs poorly with small caches.
+ * Enable it only if caches above this threshold have been configured.
+ * 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;
@@ -100,7 +107,7 @@ warning_func(void *baton,
{
if (! err)
return;
- svn_handle_error2(err, stderr, FALSE, "svnadmin: ");
+ svn_handle_warning2(stderr, err, "svnadmin: ");
}
@@ -111,6 +118,10 @@ 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");
@@ -118,9 +129,11 @@ open_repos(svn_repos_t **repos,
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_open2(repos, path, fs_config, pool));
+ 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;
}
@@ -150,11 +163,13 @@ check_lib_versions(void)
static svn_opt_subcommand_t
subcommand_crashtest,
subcommand_create,
+ subcommand_delrevprop,
subcommand_deltify,
subcommand_dump,
subcommand_freeze,
subcommand_help,
subcommand_hotcopy,
+ subcommand_info,
subcommand_load,
subcommand_list_dblogs,
subcommand_list_unused_dblogs,
@@ -176,6 +191,7 @@ enum svnadmin__cmdline_options_t
{
svnadmin__version = SVN_OPT_FIRST_LONGOPT_ID,
svnadmin__incremental,
+ svnadmin__keep_going,
svnadmin__deltas,
svnadmin__ignore_uuid,
svnadmin__force_uuid,
@@ -186,6 +202,7 @@ enum svnadmin__cmdline_options_t
svnadmin__config_dir,
svnadmin__bypass_hooks,
svnadmin__bypass_prop_validation,
+ svnadmin__ignore_dates,
svnadmin__use_pre_commit_hook,
svnadmin__use_post_commit_hook,
svnadmin__use_pre_revprop_change_hook,
@@ -195,7 +212,9 @@ enum svnadmin__cmdline_options_t
svnadmin__pre_1_4_compatible,
svnadmin__pre_1_5_compatible,
svnadmin__pre_1_6_compatible,
- svnadmin__compatible_version
+ svnadmin__compatible_version,
+ svnadmin__check_normalization,
+ svnadmin__metadata_only
};
/* Option codes and descriptions.
@@ -231,8 +250,11 @@ static const apr_getopt_option_t options_table[] =
{"bypass-prop-validation", svnadmin__bypass_prop_validation, 0,
N_("bypass property validation logic")},
+ {"ignore-dates", svnadmin__ignore_dates, 0,
+ N_("ignore revision datestamps found in the stream")},
+
{"quiet", 'q', 0,
- N_("no progress (only errors) to stderr")},
+ N_("no progress (only errors to stderr)")},
{"ignore-uuid", svnadmin__ignore_uuid, 0,
N_("ignore any repos UUID found in the stream")},
@@ -241,7 +263,9 @@ static const apr_getopt_option_t options_table[] =
N_("set repos UUID to that found in stream, if any")},
{"fs-type", svnadmin__fs_type, 1,
- N_("type of repository: 'fsfs' (default) or 'bdb'")},
+ N_("type of repository:\n"
+ " 'fsfs' (default), 'bdb' or 'fsx'\n"
+ " CAUTION: FSX is for EXPERIMENTAL use only!")},
{"parent-dir", svnadmin__parent_dir, 1,
N_("load at specified directory in repository")},
@@ -284,6 +308,9 @@ static const apr_getopt_option_t options_table[] =
{"pre-1.6-compatible", svnadmin__pre_1_6_compatible, 0,
N_("deprecated; see --compatible-version")},
+ {"keep-going", svnadmin__keep_going, 0,
+ N_("continue verification after detecting a corruption")},
+
{"memory-cache-size", 'M', 1,
N_("size of the extra in-memory cache in MB used to\n"
" minimize redundant operations. Default: 16.\n"
@@ -295,6 +322,17 @@ static const apr_getopt_option_t options_table[] =
{"file", 'F', 1, N_("read repository paths from file ARG")},
+ {"check-normalization", svnadmin__check_normalization, 0,
+ N_("report any names within the same directory or\n"
+ " svn:mergeinfo property value that differ only\n"
+ " in character representation, but are otherwise\n"
+ " identical")},
+
+ {"metadata-only", svnadmin__metadata_only, 0,
+ N_("verify metadata only (ignored for BDB),\n"
+ " checking against external corruption in\n"
+ " Subversion 1.9+ format repositories.\n")},
+
{NULL}
};
@@ -319,6 +357,19 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
svnadmin__pre_1_6_compatible
} },
+ {"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"
+ "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"
+ "an email notification sent from your post-revprop-change hook).\n\n"
+ "NOTE: Revision properties are not versioned, so this command will\n"
+ "irreversibly destroy the previous value of the property.\n\n"
+ "2. Delete the property NAME on transaction TXN.\n"),
+ {'r', 't', svnadmin__use_pre_revprop_change_hook,
+ svnadmin__use_post_revprop_change_hook} },
+
{"deltify", subcommand_deltify, {0}, N_
("usage: svnadmin deltify [-r LOWER[:UPPER]] REPOS_PATH\n\n"
"Run over the requested revision range, performing predecessor delti-\n"
@@ -345,6 +396,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
("usage: 1. svnadmin freeze REPOS_PATH PROGRAM [ARG...]\n"
" 2. svnadmin freeze -F FILE PROGRAM [ARG...]\n\n"
"1. Run PROGRAM passing ARGS while holding a write-lock on REPOS_PATH.\n"
+ " Allows safe use of third-party backup tools on a live repository.\n"
"\n"
"2. Like 1 except all repositories listed in FILE are locked. The file\n"
" format is repository paths separated by newlines. Repositories are\n"
@@ -361,7 +413,12 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
"Make a hot copy of a repository.\n"
"If --incremental is passed, data which already exists at the destination\n"
"is not copied again. Incremental mode is implemented for FSFS repositories.\n"),
- {svnadmin__clean_logs, svnadmin__incremental} },
+ {svnadmin__clean_logs, svnadmin__incremental, 'q'} },
+
+ {"info", subcommand_info, {0}, N_
+ ("usage: svnadmin info REPOS_PATH\n\n"
+ "Print information about the repository at REPOS_PATH.\n"),
+ {0} },
{"list-dblogs", subcommand_list_dblogs, {0}, N_
("usage: svnadmin list-dblogs REPOS_PATH\n\n"
@@ -384,6 +441,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
"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__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'} },
@@ -409,7 +467,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
("usage: svnadmin pack REPOS_PATH\n\n"
"Possibly compact the repository into a more efficient storage model.\n"
"This may not apply to all repositories, in which case, exit.\n"),
- {'q'} },
+ {'q', 'M'} },
{"recover", subcommand_recover, {0}, N_
("usage: svnadmin recover REPOS_PATH\n\n"
@@ -442,14 +500,16 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
{'r', svnadmin__bypass_hooks} },
{"setrevprop", subcommand_setrevprop, {0}, N_
- ("usage: svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n\n"
- "Set the property NAME on revision REVISION to the contents of FILE. Use\n"
- "--use-pre-revprop-change-hook/--use-post-revprop-change-hook to trigger\n"
- "the revision property-related hooks (for example, if you want an email\n"
- "notification sent from your post-revprop-change hook).\n\n"
+ ("usage: 1. svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n"
+ " 2. svnadmin setrevprop REPOS_PATH -t TXN NAME FILE\n\n"
+ "1. Set the property NAME on revision REVISION to the contents of FILE.\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"
+ "an email notification sent from your post-revprop-change hook).\n\n"
"NOTE: Revision properties are not versioned, so this command will\n"
- "overwrite the previous value of the property.\n"),
- {'r', svnadmin__use_pre_revprop_change_hook,
+ "overwrite the previous value of the property.\n\n"
+ "2. Set the property NAME on transaction TXN to the contents of FILE.\n"),
+ {'r', 't', svnadmin__use_pre_revprop_change_hook,
svnadmin__use_post_revprop_change_hook} },
{"setuuid", subcommand_setuuid, {0}, N_
@@ -482,7 +542,8 @@ static const svn_opt_subcommand_desc2_t cmd_table[] =
{"verify", subcommand_verify, {0}, N_
("usage: svnadmin verify REPOS_PATH\n\n"
"Verify the data stored in the repository.\n"),
- {'t', 'r', 'q', 'M'} },
+ {'t', 'r', 'q', svnadmin__keep_going, 'M',
+ svnadmin__check_normalization, svnadmin__metadata_only} },
{ NULL, NULL, {0}, NULL, {0} }
};
@@ -493,9 +554,6 @@ struct svnadmin_opt_state
{
const char *repository_path;
const char *fs_type; /* --fs-type */
- svn_boolean_t pre_1_4_compatible; /* --pre-1.4-compatible */
- svn_boolean_t pre_1_5_compatible; /* --pre-1.5-compatible */
- svn_boolean_t pre_1_6_compatible; /* --pre-1.6-compatible */
svn_version_t *compatible_version; /* --compatible-version */
svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */
const char *txn_id; /* -t TXN */
@@ -513,11 +571,15 @@ struct svnadmin_opt_state
svn_boolean_t clean_logs; /* --clean-logs */
svn_boolean_t bypass_hooks; /* --bypass-hooks */
svn_boolean_t wait; /* --wait */
+ svn_boolean_t keep_going; /* --keep-going */
+ svn_boolean_t check_normalization; /* --check-normalization */
+ svn_boolean_t metadata_only; /* --metadata-only */
svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */
+ svn_boolean_t ignore_dates; /* --ignore-dates */
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;
+ const char *parent_dir; /* --parent-dir */
svn_stringbuf_t *filedata; /* --file */
const char *config_dir; /* Overriding Configuration Directory */
@@ -564,7 +626,7 @@ target_arg_to_dirent(const char **dirent,
SVN_ERR(svn_utf_cstring_to_utf8(&path, arg, 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);
+ _("Path '%s' is not a local path"), path);
*dirent = svn_dirent_internal_style(path, pool);
return SVN_NO_ERROR;
}
@@ -593,10 +655,10 @@ parse_args(apr_array_header_t **args,
if ((min_expected >= 0) && (num_args < min_expected))
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
- "Not enough arguments");
+ _("Not enough arguments"));
if ((max_expected >= 0) && (num_args > max_expected))
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
- "Too many arguments");
+ _("Too many arguments"));
if (args)
{
*args = apr_array_make(pool, num_args, sizeof(const char *));
@@ -611,6 +673,17 @@ parse_args(apr_array_header_t **args,
}
+/* This implements 'svn_error_malfunction_handler_t. */
+static svn_error_t *
+crashtest_malfunction_handler(svn_boolean_t can_return,
+ const char *file,
+ int line,
+ const char *expr)
+{
+ abort();
+ return SVN_NO_ERROR; /* Not reached. */
+}
+
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool)
@@ -618,7 +691,14 @@ subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool)
struct svnadmin_opt_state *opt_state = baton;
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(svn_cmdline_printf(pool,
+ _("Successfully opened repository '%s'.\n"
+ "Will now crash to simulate a crashing "
+ "server process.\n"),
+ svn_dirent_local_style(opt_state->repository_path,
+ pool)));
SVN_ERR_MALFUNCTION();
/* merely silence a compiler warning (this will never be executed) */
@@ -662,17 +742,6 @@ subcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool)
svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, opt_state->fs_type);
}
- /* Prior to 1.8, we had explicit options to specify compatibility
- with a handful of prior Subversion releases. */
- if (opt_state->pre_1_4_compatible)
- svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1");
- if (opt_state->pre_1_5_compatible)
- svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1");
- if (opt_state->pre_1_6_compatible)
- svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
-
- /* In 1.8, we figured out that we didn't have to keep extending this
- madness indefinitely. */
if (opt_state->compatible_version)
{
if (! svn_version__at_least(opt_state->compatible_version, 1, 4, 0))
@@ -683,18 +752,40 @@ subcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool)
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
if (! svn_version__at_least(opt_state->compatible_version, 1, 8, 0))
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE, "1");
+ /* In 1.9, we figured out that we didn't have to keep extending this
+ madness indefinitely. */
+ svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION,
+ apr_psprintf(pool, "%d.%d.%d%s%s",
+ opt_state->compatible_version->major,
+ opt_state->compatible_version->minor,
+ opt_state->compatible_version->patch,
+ opt_state->compatible_version->tag
+ ? "-" : "",
+ opt_state->compatible_version->tag
+ ? opt_state->compatible_version->tag : ""));
}
- if (opt_state->compatible_version
- && ! svn_version__at_least(opt_state->compatible_version, 1, 1, 0)
- /* ### TODO: this NULL check hard-codes knowledge of the library's
- default fs-type value */
- && (opt_state->fs_type == NULL
- || !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSFS)))
+ if (opt_state->compatible_version)
{
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("Repositories compatible with 1.0.x must use "
- "--fs-type=bdb"));
+ if (! svn_version__at_least(opt_state->compatible_version, 1, 1, 0)
+ /* ### TODO: this NULL check hard-codes knowledge of the library's
+ default fs-type value */
+ && (opt_state->fs_type == NULL
+ || !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSFS)))
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Repositories compatible with 1.0.x must "
+ "use --fs-type=bdb"));
+ }
+
+ if (! svn_version__at_least(opt_state->compatible_version, 1, 9, 0)
+ && opt_state->fs_type && !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSX))
+ {
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Repositories compatible with 1.8.x or "
+ "earlier cannot use --fs-type=%s"),
+ SVN_FS_TYPE_FSX);
+ }
}
SVN_ERR(svn_repos_create(&repos, opt_state->repository_path,
@@ -756,16 +847,87 @@ subcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
return SVN_NO_ERROR;
}
+/* Structure for errors encountered during 'svnadmin verify --keep-going'. */
+struct verification_error
+{
+ svn_revnum_t rev;
+ svn_error_t *err;
+};
+
+/* Pool cleanup function to clear an svn_error_t *. */
+static apr_status_t
+err_cleanup(void *data)
+{
+ svn_error_t *err = data;
+
+ svn_error_clear(err);
+
+ return APR_SUCCESS;
+}
+
+struct repos_verify_callback_baton
+{
+ /* Should we continue after receiving a first verification error? */
+ svn_boolean_t keep_going;
+
+ /* List of errors encountered during 'svnadmin verify --keep-going'. */
+ apr_array_header_t *error_summary;
+
+ /* Pool for data collected during callback invocations. */
+ apr_pool_t *result_pool;
+};
+
+/* Implementation of svn_repos_verify_callback_t to handle errors coming
+ from svn_repos_verify_fs3(). */
+static svn_error_t *
+repos_verify_callback(void *baton,
+ svn_revnum_t revision,
+ svn_error_t *verify_err,
+ apr_pool_t *scratch_pool)
+{
+ struct repos_verify_callback_baton *b = baton;
+
+ if (revision == SVN_INVALID_REVNUM)
+ {
+ SVN_ERR(svn_cmdline_fputs(_("* Error verifying repository metadata.\n"),
+ stderr, scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+ _("* Error verifying revision %ld.\n"),
+ revision));
+ }
+
+ if (b->keep_going)
+ {
+ struct verification_error *verr;
+
+ svn_handle_error2(verify_err, stderr, FALSE, "svnadmin: ");
+
+ /* Remember the error in B->ERROR_SUMMARY. */
+ verr = apr_palloc(b->result_pool, sizeof(*verr));
+ verr->rev = revision;
+ verr->err = svn_error_dup(verify_err);
+ apr_pool_cleanup_register(b->result_pool, verr->err, err_cleanup,
+ apr_pool_cleanup_null);
+ APR_ARRAY_PUSH(b->error_summary, struct verification_error *) = verr;
+
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_trace(svn_error_dup(verify_err));
+}
/* Implementation of svn_repos_notify_func_t to wrap the output to a
- response stream for svn_repos_dump_fs2() and svn_repos_verify_fs() */
+ response stream for svn_repos_dump_fs2(), svn_repos_verify_fs(),
+ svn_repos_hotcopy3() and others. */
static void
repos_notify_handler(void *baton,
const svn_repos_notify_t *notify,
apr_pool_t *scratch_pool)
{
svn_stream_t *feedback_stream = baton;
- apr_size_t len;
switch (notify->action)
{
@@ -789,7 +951,7 @@ repos_notify_handler(void *baton,
case svn_repos_notify_verify_rev_structure:
if (notify->revision == SVN_INVALID_REVNUM)
- svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ svn_error_clear(svn_stream_puts(feedback_stream,
_("* Verifying repository metadata ...\n")));
else
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
@@ -877,13 +1039,11 @@ repos_notify_handler(void *baton,
return;
case svn_repos_notify_load_node_done:
- svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
- "%s", _(" done.\n")));
+ svn_error_clear(svn_stream_puts(feedback_stream, _(" done.\n")));
return;
case svn_repos_notify_load_copied_node:
- len = 9;
- svn_error_clear(svn_stream_write(feedback_stream, "COPIED...", &len));
+ svn_error_clear(svn_stream_puts(feedback_stream, "COPIED..."));
return;
case svn_repos_notify_load_txn_start:
@@ -911,7 +1071,7 @@ repos_notify_handler(void *baton,
return;
case svn_repos_notify_recover_start:
- svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ svn_error_clear(svn_stream_puts(feedback_stream,
_("Repository lock acquired.\n"
"Please wait; recovering the"
" repository may take some time...\n")));
@@ -924,6 +1084,49 @@ repos_notify_handler(void *baton,
" repository may take some time...\n")));
return;
+ case svn_repos_notify_pack_revprops:
+ {
+ const char *shardstr = apr_psprintf(scratch_pool,
+ "%" APR_INT64_T_FMT,
+ notify->shard);
+ svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ _("Packed revision properties in shard %s\n"),
+ shardstr));
+ return;
+ }
+
+ case svn_repos_notify_cleanup_revprops:
+ {
+ const char *shardstr = apr_psprintf(scratch_pool,
+ "%" APR_INT64_T_FMT,
+ notify->shard);
+ svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ _("Removed non-packed revision properties"
+ " in shard %s\n"),
+ shardstr));
+ return;
+ }
+
+ case svn_repos_notify_format_bumped:
+ svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ _("Bumped repository format to %ld\n"),
+ notify->revision));
+ return;
+
+ case svn_repos_notify_hotcopy_rev_range:
+ if (notify->start_revision == notify->end_revision)
+ {
+ svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ _("* Copied revision %ld.\n"),
+ notify->start_revision));
+ }
+ else
+ {
+ svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+ _("* Copied revisions from %ld to %ld.\n"),
+ notify->start_revision, notify->end_revision));
+ }
+
default:
return;
}
@@ -980,7 +1183,7 @@ subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool)
svn_stream_t *stdout_stream;
svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
svn_revnum_t youngest;
- svn_stream_t *progress_stream = NULL;
+ svn_stream_t *feedback_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
@@ -1014,12 +1217,12 @@ subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool)
/* Progress feedback goes to STDERR, unless they asked to suppress it. */
if (! opt_state->quiet)
- progress_stream = recode_stream_create(stderr, pool);
+ feedback_stream = recode_stream_create(stderr, pool);
SVN_ERR(svn_repos_dump_fs3(repos, stdout_stream, lower, upper,
opt_state->incremental, opt_state->use_deltas,
!opt_state->quiet ? repos_notify_handler : NULL,
- progress_stream, check_cancel, NULL, pool));
+ feedback_stream, check_cancel, NULL, pool));
return SVN_NO_ERROR;
}
@@ -1079,8 +1282,10 @@ subcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool)
}
else
{
+ const char *utf8;
/* All repositories in filedata. */
- paths = svn_cstring_split(opt_state->filedata->data, "\n", FALSE, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8, opt_state->filedata->data, pool));
+ paths = svn_cstring_split(utf8, "\r\n", FALSE, pool);
}
b.command = APR_ARRAY_IDX(args, 0, const char *);
@@ -1106,6 +1311,7 @@ subcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
struct svnadmin_opt_state *opt_state = baton;
const char *header =
_("general usage: svnadmin SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n"
+ "Subversion repository administration tool.\n"
"Type 'svnadmin help <subcommand>' for help on a specific subcommand.\n"
"Type 'svnadmin --version' to see the program version and FS modules.\n"
"\n"
@@ -1165,7 +1371,8 @@ subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool)
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, *stdout_stream = NULL;
+ svn_stream_t *stdin_stream;
+ svn_stream_t *feedback_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
@@ -1199,15 +1406,16 @@ subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool)
/* Progress feedback goes to STDOUT, unless they asked to suppress it. */
if (! opt_state->quiet)
- stdout_stream = recode_stream_create(stdout, pool);
+ feedback_stream = recode_stream_create(stdout, pool);
- err = svn_repos_load_fs4(repos, stdin_stream, lower, upper,
+ err = svn_repos_load_fs5(repos, stdin_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->quiet ? NULL : repos_notify_handler,
- stdout_stream, check_cancel, NULL, pool);
+ 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 "
@@ -1254,12 +1462,12 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool)
svn_repos_t *repos;
svn_error_t *err;
struct svnadmin_opt_state *opt_state = baton;
- svn_stream_t *stdout_stream;
+ svn_stream_t *feedback_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
- SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
+ SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool));
/* Restore default signal handlers until after we have acquired the
* exclusive lock so that the user interrupt before we actually
@@ -1267,7 +1475,7 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool)
setup_cancellation_signals(SIG_DFL);
err = svn_repos_recover4(opt_state->repository_path, TRUE,
- repos_notify_handler, stdout_stream,
+ repos_notify_handler, feedback_stream,
check_cancel, NULL, pool);
if (err)
{
@@ -1285,7 +1493,7 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool)
" another process has it open?\n")));
SVN_ERR(svn_cmdline_fflush(stdout));
SVN_ERR(svn_repos_recover4(opt_state->repository_path, FALSE,
- repos_notify_handler, stdout_stream,
+ repos_notify_handler, feedback_stream,
check_cancel, NULL, pool));
}
@@ -1423,30 +1631,47 @@ subcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool)
/* A helper for the 'setrevprop' and 'setlog' commands. Expects
- OPT_STATE->use_pre_revprop_change_hook and
- OPT_STATE->use_post_revprop_change_hook to be set appropriately. */
+ OPT_STATE->txn_id, OPT_STATE->use_pre_revprop_change_hook and
+ OPT_STATE->use_post_revprop_change_hook to be set appropriately.
+ If FILENAME is NULL, delete property PROP_NAME. */
static svn_error_t *
set_revprop(const char *prop_name, const char *filename,
struct svnadmin_opt_state *opt_state, apr_pool_t *pool)
{
svn_repos_t *repos;
- svn_string_t *prop_value = svn_string_create_empty(pool);
- svn_stringbuf_t *file_contents;
+ svn_string_t *prop_value;
+
+ if (filename)
+ {
+ svn_stringbuf_t *file_contents;
- SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool));
+ SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool));
- prop_value->data = file_contents->data;
- prop_value->len = file_contents->len;
+ prop_value = svn_string_create_empty(pool);
+ prop_value->data = file_contents->data;
+ prop_value->len = file_contents->len;
- SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value,
- NULL, FALSE, pool, pool));
+ SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value,
+ NULL, FALSE, pool, pool));
+ }
+ else
+ {
+ prop_value = NULL;
+ }
/* Open the filesystem */
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
- /* If we are bypassing the hooks system, we just hit the filesystem
- directly. */
- SVN_ERR(svn_repos_fs_change_rev_prop4(
+ if (opt_state->txn_id)
+ {
+ svn_fs_t *fs = svn_repos_fs(repos);
+ svn_fs_txn_t *txn;
+
+ SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool));
+ SVN_ERR(svn_fs_change_txn_prop(txn, prop_name, prop_value, pool));
+ }
+ else
+ SVN_ERR(svn_repos_fs_change_rev_prop4(
repos, opt_state->start_revision.value.number,
NULL, prop_name, NULL, prop_value,
opt_state->use_pre_revprop_change_hook,
@@ -1471,7 +1696,21 @@ subcommand_setrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool)
filename = APR_ARRAY_IDX(args, 1, const char *);
SVN_ERR(target_arg_to_dirent(&filename, filename, pool));
- if (opt_state->start_revision.kind != svn_opt_revision_number)
+ if (opt_state->txn_id)
+ {
+ if (opt_state->start_revision.kind != svn_opt_revision_unspecified
+ || opt_state->end_revision.kind != svn_opt_revision_unspecified)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--revision (-r) and --transaction (-t) "
+ "are mutually exclusive"));
+
+ if (opt_state->use_pre_revprop_change_hook
+ || opt_state->use_post_revprop_change_hook)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Calling hooks is incompatible with "
+ "--transaction (-t)"));
+ }
+ else if (opt_state->start_revision.kind != svn_opt_revision_number)
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Missing revision"));
else if (opt_state->end_revision.kind != svn_opt_revision_unspecified)
@@ -1540,7 +1779,7 @@ subcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
- svn_stream_t *progress_stream = NULL;
+ svn_stream_t *feedback_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
@@ -1549,11 +1788,11 @@ subcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool)
/* Progress feedback goes to STDOUT, unless they asked to suppress it. */
if (! opt_state->quiet)
- progress_stream = recode_stream_create(stdout, pool);
+ feedback_stream = recode_stream_create(stdout, pool);
return svn_error_trace(
svn_repos_fs_pack2(repos, !opt_state->quiet ? repos_notify_handler : NULL,
- progress_stream, check_cancel, NULL, pool));
+ feedback_stream, check_cancel, NULL, pool));
}
@@ -1565,7 +1804,8 @@ subcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
svn_repos_t *repos;
svn_fs_t *fs;
svn_revnum_t youngest, lower, upper;
- svn_stream_t *progress_stream = NULL;
+ svn_stream_t *feedback_stream = NULL;
+ struct repos_verify_callback_baton verify_baton = { 0 };
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
@@ -1609,13 +1849,93 @@ subcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
upper = lower;
}
- if (! opt_state->quiet)
- progress_stream = recode_stream_create(stderr, pool);
+ if (!opt_state->quiet)
+ feedback_stream = recode_stream_create(stdout, pool);
+
+ verify_baton.keep_going = opt_state->keep_going;
+ verify_baton.error_summary =
+ apr_array_make(pool, 0, sizeof(struct verification_error *));
+ verify_baton.result_pool = pool;
+
+ SVN_ERR(svn_repos_verify_fs3(repos, lower, upper,
+ opt_state->check_normalization,
+ opt_state->metadata_only,
+ !opt_state->quiet
+ ? repos_notify_handler : NULL,
+ feedback_stream,
+ repos_verify_callback, &verify_baton,
+ check_cancel, NULL, pool));
+
+ /* Show the --keep-going error summary. */
+ if (!opt_state->quiet
+ && opt_state->keep_going
+ && verify_baton.error_summary->nelts > 0)
+ {
+ int rev_maxlength;
+ svn_revnum_t end_revnum;
+ apr_pool_t *iterpool;
+ int i;
+
+ svn_error_clear(
+ svn_stream_puts(feedback_stream,
+ _("\n-----Summary of corrupt revisions-----\n")));
+
+ /* The standard column width for the revision number is 6 characters.
+ If the revision number can potentially be larger (i.e. if end_revnum
+ is larger than 1000000), we increase the column width as needed. */
+ rev_maxlength = 6;
+ end_revnum = APR_ARRAY_IDX(verify_baton.error_summary,
+ verify_baton.error_summary->nelts - 1,
+ struct verification_error *)->rev;
+ while (end_revnum >= 1000000)
+ {
+ rev_maxlength++;
+ end_revnum = end_revnum / 10;
+ }
+
+ iterpool = svn_pool_create(pool);
+ for (i = 0; i < verify_baton.error_summary->nelts; i++)
+ {
+ struct verification_error *verr;
+ svn_error_t *err;
+ const char *rev_str;
+
+ svn_pool_clear(iterpool);
+
+ verr = APR_ARRAY_IDX(verify_baton.error_summary, i,
+ struct verification_error *);
+
+ if (verr->rev != SVN_INVALID_REVNUM)
+ {
+ rev_str = apr_psprintf(iterpool, "r%ld", verr->rev);
+ rev_str = apr_psprintf(iterpool, "%*s", rev_maxlength, rev_str);
+ for (err = svn_error_purge_tracing(verr->err);
+ err != SVN_NO_ERROR; err = err->child)
+ {
+ char buf[512];
+ const char *message;
+
+ message = svn_err_best_message(err, buf, sizeof(buf));
+ svn_error_clear(svn_stream_printf(feedback_stream, iterpool,
+ "%s: E%06d: %s\n",
+ rev_str, err->apr_err,
+ message));
+ }
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+ }
+
+ if (verify_baton.error_summary->nelts > 0)
+ {
+ return svn_error_createf(SVN_ERR_CL_REPOS_VERIFY_FAILED, NULL,
+ _("Failed to verify repository '%s'"),
+ svn_dirent_local_style(
+ opt_state->repository_path, pool));
+ }
- return svn_repos_verify_fs2(repos, lower, upper,
- !opt_state->quiet
- ? repos_notify_handler : NULL,
- progress_stream, check_cancel, NULL, pool);
+ return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
@@ -1623,6 +1943,7 @@ svn_error_t *
subcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
+ svn_stream_t *feedback_stream = NULL;
apr_array_header_t *targets;
const char *new_repos_path;
@@ -1631,9 +1952,136 @@ subcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool)
new_repos_path = APR_ARRAY_IDX(targets, 0, const char *);
SVN_ERR(target_arg_to_dirent(&new_repos_path, new_repos_path, pool));
- return svn_repos_hotcopy2(opt_state->repository_path, new_repos_path,
+ /* Progress feedback goes to STDOUT, unless they asked to suppress it. */
+ if (! opt_state->quiet)
+ feedback_stream = recode_stream_create(stdout, pool);
+
+ return svn_repos_hotcopy3(opt_state->repository_path, new_repos_path,
opt_state->clean_logs, opt_state->incremental,
- check_cancel, NULL, pool);
+ !opt_state->quiet ? repos_notify_handler : NULL,
+ feedback_stream, check_cancel, NULL, pool);
+}
+
+svn_error_t *
+subcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ struct svnadmin_opt_state *opt_state = baton;
+ svn_repos_t *repos;
+ svn_fs_t *fs;
+ int fs_format;
+ const char *uuid;
+
+ /* Expect no more arguments. */
+ SVN_ERR(parse_args(NULL, os, 0, 0, pool));
+
+ SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
+ fs = svn_repos_fs(repos);
+ SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"),
+ svn_dirent_local_style(svn_repos_path(repos, pool),
+ pool)));
+
+ SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool));
+ SVN_ERR(svn_cmdline_printf(pool, _("UUID: %s\n"), uuid));
+ {
+ int repos_format, minor;
+ svn_version_t *repos_version, *fs_version;
+ SVN_ERR(svn_repos_info_format(&repos_format, &repos_version,
+ repos, pool, pool));
+ SVN_ERR(svn_cmdline_printf(pool, _("Repository Format: %d\n"),
+ repos_format));
+
+ SVN_ERR(svn_fs_info_format(&fs_format, &fs_version,
+ fs, pool, pool));
+ /* fs_format will be printed later. */
+
+ SVN_ERR_ASSERT(repos_version->major == SVN_VER_MAJOR);
+ SVN_ERR_ASSERT(fs_version->major == SVN_VER_MAJOR);
+ SVN_ERR_ASSERT(repos_version->patch == 0);
+ SVN_ERR_ASSERT(fs_version->patch == 0);
+
+ minor = (repos_version->minor > fs_version->minor)
+ ? repos_version->minor : fs_version->minor;
+ SVN_ERR(svn_cmdline_printf(pool, _("Compatible With Version: %d.%d.0\n"),
+ SVN_VER_MAJOR, minor));
+ }
+
+ {
+ apr_hash_t *capabilities_set;
+ apr_array_header_t *capabilities;
+ int i;
+
+ SVN_ERR(svn_repos_capabilities(&capabilities_set, repos, pool, pool));
+ capabilities = svn_sort__hash(capabilities_set,
+ svn_sort_compare_items_lexically,
+ pool);
+
+ for (i = 0; i < capabilities->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(capabilities, i,
+ svn_sort__item_t);
+ const char *capability = item->key;
+ SVN_ERR(svn_cmdline_printf(pool, _("Repository Capability: %s\n"),
+ capability));
+ }
+ }
+
+ {
+ const svn_fs_info_placeholder_t *info;
+
+ SVN_ERR(svn_fs_info(&info, fs, pool, pool));
+ SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Type: %s\n"),
+ info->fs_type));
+ SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Format: %d\n"),
+ fs_format));
+ 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")));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: no\n")));
+
+ if (fsfs_info->shard_size)
+ SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shard Size: %d\n"),
+ fsfs_info->shard_size));
+
+ /* Print packing statistics, if enabled on the FS. */
+ if (fsfs_info->shard_size)
+ {
+ 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;
+ SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shards Packed: %ld/%ld\n"),
+ shards_packed, shards_full));
+ }
+
+ if (fsfs_info->log_addressing)
+ SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: yes\n")));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: no\n")));
+ }
+ }
+
+ {
+ apr_array_header_t *files;
+ int i;
+
+ SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool));
+ for (i = 0; i < files->nelts; i++)
+ SVN_ERR(svn_cmdline_printf(pool, _("Configuration File: %s\n"),
+ svn_dirent_local_style(
+ APR_ARRAY_IDX(files, i, const char *),
+ pool)));
+ }
+
+ /* 'svn info' prints an extra newline here, to support multiple targets.
+ We'll do the same. */
+ SVN_ERR(svn_cmdline_printf(pool, "\n"));
+
+ return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
@@ -1709,6 +2157,7 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
const char *fs_path = "/";
apr_hash_t *locks;
apr_hash_index_t *hi;
+ apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_opt__args_to_target_array(&targets, os,
apr_array_make(pool, 0,
@@ -1729,24 +2178,28 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
const char *cr_date, *exp_date = "";
- const char *path = svn__apr_hash_index_key(hi);
- svn_lock_t *lock = svn__apr_hash_index_val(hi);
+ const char *path = apr_hash_this_key(hi);
+ svn_lock_t *lock = apr_hash_this_val(hi);
int comment_lines = 0;
- cr_date = svn_time_to_human_cstring(lock->creation_date, pool);
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(check_cancel(NULL));
+
+ cr_date = svn_time_to_human_cstring(lock->creation_date, iterpool);
if (lock->expiration_date)
- exp_date = svn_time_to_human_cstring(lock->expiration_date, pool);
+ exp_date = svn_time_to_human_cstring(lock->expiration_date, iterpool);
if (lock->comment)
comment_lines = svn_cstring_count_newlines(lock->comment) + 1;
- SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), path));
- SVN_ERR(svn_cmdline_printf(pool, _("UUID Token: %s\n"), lock->token));
- SVN_ERR(svn_cmdline_printf(pool, _("Owner: %s\n"), lock->owner));
- SVN_ERR(svn_cmdline_printf(pool, _("Created: %s\n"), cr_date));
- SVN_ERR(svn_cmdline_printf(pool, _("Expires: %s\n"), exp_date));
- SVN_ERR(svn_cmdline_printf(pool,
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Path: %s\n"), path));
+ SVN_ERR(svn_cmdline_printf(iterpool, _("UUID Token: %s\n"), lock->token));
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Owner: %s\n"), lock->owner));
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Created: %s\n"), cr_date));
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Expires: %s\n"), exp_date));
+ SVN_ERR(svn_cmdline_printf(iterpool,
Q_("Comment (%i line):\n%s\n\n",
"Comment (%i lines):\n%s\n\n",
comment_lines),
@@ -1754,6 +2207,8 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
lock->comment ? lock->comment : ""));
}
+ svn_pool_destroy(iterpool);
+
return SVN_NO_ERROR;
}
@@ -1888,18 +2343,18 @@ subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
svn_error_t *err;
struct svnadmin_opt_state *opt_state = baton;
- svn_stream_t *stdout_stream;
+ svn_stream_t *feedback_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
- SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
+ SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool));
/* Restore default signal handlers. */
setup_cancellation_signals(SIG_DFL);
err = svn_repos_upgrade2(opt_state->repository_path, TRUE,
- repos_notify_handler, stdout_stream, pool);
+ repos_notify_handler, feedback_stream, pool);
if (err)
{
if (APR_STATUS_IS_EAGAIN(err->apr_err))
@@ -1917,7 +2372,7 @@ subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool)
" another process has it open?\n")));
SVN_ERR(svn_cmdline_fflush(stdout));
SVN_ERR(svn_repos_upgrade2(opt_state->repository_path, FALSE,
- repos_notify_handler, stdout_stream,
+ repos_notify_handler, feedback_stream,
pool));
}
else if (err->apr_err == SVN_ERR_FS_UNSUPPORTED_UPGRADE)
@@ -1941,26 +2396,53 @@ subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool)
}
+/* This implements `svn_opt_subcommand_t'. */
+static svn_error_t *
+subcommand_delrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ struct svnadmin_opt_state *opt_state = baton;
+ apr_array_header_t *args;
+ const char *prop_name;
+
+ /* Expect one more argument: NAME */
+ SVN_ERR(parse_args(&args, os, 1, 1, pool));
+ prop_name = APR_ARRAY_IDX(args, 0, const char *);
+
+ if (opt_state->txn_id)
+ {
+ if (opt_state->start_revision.kind != svn_opt_revision_unspecified
+ || opt_state->end_revision.kind != svn_opt_revision_unspecified)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--revision (-r) and --transaction (-t) "
+ "are mutually exclusive"));
+
+ if (opt_state->use_pre_revprop_change_hook
+ || opt_state->use_post_revprop_change_hook)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Calling hooks is incompatible with "
+ "--transaction (-t)"));
+ }
+ else if (opt_state->start_revision.kind != svn_opt_revision_number)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Missing revision"));
+ else if (opt_state->end_revision.kind != svn_opt_revision_unspecified)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Only one revision allowed"));
+
+ return set_revprop(prop_name, NULL, opt_state, pool);
+}
+
+
/** Main. **/
-/* Report and clear the error ERR, and return EXIT_FAILURE. */
-#define EXIT_ERROR(err) \
- svn_cmdline_handle_exit_error(err, NULL, "svnadmin: ")
-
-/* A redefinition of the public SVN_INT_ERR macro, that suppresses the
- * error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR, amd with the
- * program name 'svnadmin' instead of 'svn'. */
-#undef SVN_INT_ERR
-#define SVN_INT_ERR(expr) \
- do { \
- svn_error_t *svn_err__temp = (expr); \
- if (svn_err__temp) \
- return EXIT_ERROR(svn_err__temp); \
- } while (0)
-
-static int
-sub_main(int argc, const char *argv[], apr_pool_t *pool)
+/*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ */
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
svn_error_t *err;
apr_status_t apr_err;
@@ -1976,15 +2458,16 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
/* Check library versions */
- SVN_INT_ERR(check_lib_versions());
+ SVN_ERR(check_lib_versions());
/* Initialize the FS library. */
- SVN_INT_ERR(svn_fs_initialize(pool));
+ SVN_ERR(svn_fs_initialize(pool));
if (argc <= 1)
{
- SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
- return EXIT_FAILURE;
+ SVN_ERR(subcommand_help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
/* Initialize opt_state. */
@@ -1993,7 +2476,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
opt_state.memory_cache_size = svn_cache_config_get()->cache_size;
/* Parse options. */
- SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
+ SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
os->interleave = 1;
@@ -2008,8 +2491,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
break;
else if (apr_err)
{
- SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
- return EXIT_FAILURE;
+ SVN_ERR(subcommand_help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
/* Stash the option code in an array before parsing it. */
@@ -2020,23 +2504,19 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
{
if (opt_state.start_revision.kind != svn_opt_revision_unspecified)
{
- err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("Multiple revision arguments encountered; "
- "try '-r N:M' instead of '-r N -r M'"));
- return EXIT_ERROR(err);
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Multiple revision arguments encountered; "
+ "try '-r N:M' instead of '-r N -r M'"));
}
if (svn_opt_parse_revision(&(opt_state.start_revision),
&(opt_state.end_revision),
opt_arg, pool) != 0)
{
- err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg,
- pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
- if (! err)
- err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Syntax error in revision argument '%s'"),
utf8_opt_arg);
- return EXIT_ERROR(err);
}
}
break;
@@ -2056,8 +2536,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
= 0x100000 * apr_strtoi64(opt_arg, NULL, 0);
break;
case 'F':
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
- SVN_INT_ERR(svn_stringbuf_from_file2(&(opt_state.filedata),
+ 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));
dash_F_arg = TRUE;
case svnadmin__version:
@@ -2076,13 +2556,19 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
opt_state.uuid_action = svn_repos_load_uuid_force;
break;
case svnadmin__pre_1_4_compatible:
- opt_state.pre_1_4_compatible = TRUE;
+ opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t));
+ opt_state.compatible_version->major = 1;
+ opt_state.compatible_version->minor = 3;
break;
case svnadmin__pre_1_5_compatible:
- opt_state.pre_1_5_compatible = TRUE;
+ opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t));
+ opt_state.compatible_version->major = 1;
+ opt_state.compatible_version->minor = 4;
break;
case svnadmin__pre_1_6_compatible:
- opt_state.pre_1_6_compatible = TRUE;
+ opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t));
+ opt_state.compatible_version->major = 1;
+ opt_state.compatible_version->minor = 5;
break;
case svnadmin__compatible_version:
{
@@ -2092,16 +2578,15 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
/* Parse the version string which carries our target
compatibility. */
- SVN_INT_ERR(svn_version__parse_version_string(&compatible_version,
+ SVN_ERR(svn_version__parse_version_string(&compatible_version,
opt_arg, pool));
/* We can't create repository with a version older than 1.0.0. */
if (! svn_version__at_least(compatible_version, 1, 0, 0))
{
- err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Cannot create pre-1.0-compatible "
- "repositories"));
- return EXIT_ERROR(err);
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Cannot create pre-1.0-compatible "
+ "repositories"));
}
/* We can't create repository with a version newer than what
@@ -2111,22 +2596,30 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
compatible_version->minor,
compatible_version->patch))
{
- err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Cannot guarantee compatibility "
- "beyond the current running version "
- "(%s)"),
- SVN_VER_NUM );
- return EXIT_ERROR(err);
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Cannot guarantee compatibility "
+ "beyond the current running version "
+ "(%s)"),
+ SVN_VER_NUM);
}
opt_state.compatible_version = compatible_version;
}
break;
+ case svnadmin__keep_going:
+ opt_state.keep_going = TRUE;
+ break;
+ case svnadmin__check_normalization:
+ opt_state.check_normalization = TRUE;
+ break;
+ case svnadmin__metadata_only:
+ opt_state.metadata_only = TRUE;
+ break;
case svnadmin__fs_type:
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool));
break;
case svnadmin__parent_dir:
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.parent_dir, opt_arg,
+ SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.parent_dir, opt_arg,
pool));
opt_state.parent_dir
= svn_dirent_internal_style(opt_state.parent_dir, pool);
@@ -2155,11 +2648,14 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
case svnadmin__bypass_prop_validation:
opt_state.bypass_prop_validation = TRUE;
break;
+ case svnadmin__ignore_dates:
+ opt_state.ignore_dates = TRUE;
+ break;
case svnadmin__clean_logs:
opt_state.clean_logs = TRUE;
break;
case svnadmin__config_dir:
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
opt_state.config_dir =
apr_pstrdup(pool, svn_dirent_canonicalize(utf8_opt_arg, pool));
break;
@@ -2168,8 +2664,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
break;
default:
{
- SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
- return EXIT_FAILURE;
+ SVN_ERR(subcommand_help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
} /* close `switch' */
} /* close `while' */
@@ -2202,8 +2699,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
{
svn_error_clear(svn_cmdline_fprintf(stderr, pool,
_("subcommand argument required\n")));
- SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
- return EXIT_FAILURE;
+ SVN_ERR(subcommand_help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
}
else
@@ -2213,14 +2711,15 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
if (subcommand == NULL)
{
const char *first_arg_utf8;
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&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));
- SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
- return EXIT_FAILURE;
+ SVN_ERR(subcommand_help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
}
}
@@ -2235,23 +2734,17 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
if (os->ind >= os->argc)
{
- err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("Repository argument required"));
- return EXIT_ERROR(err);
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Repository argument required"));
}
- if ((err = svn_utf_cstring_to_utf8(&repos_path,
- os->argv[os->ind++], pool)))
- {
- return EXIT_ERROR(err);
- }
+ SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool));
if (svn_path_is_url(repos_path))
{
- err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("'%s' is a URL when it should be a "
- "local path"), repos_path);
- return EXIT_ERROR(err);
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is a URL when it should be a "
+ "local path"), repos_path);
}
opt_state.repository_path = svn_dirent_internal_style(repos_path, pool);
@@ -2277,13 +2770,14 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
pool);
svn_opt_format_option(&optstr, badopt, FALSE, pool);
if (subcommand->name[0] == '-')
- SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
+ SVN_ERR(subcommand_help(NULL, NULL, pool));
else
svn_error_clear(svn_cmdline_fprintf(stderr, pool
, _("Subcommand '%s' doesn't accept option '%s'\n"
"Type 'svnadmin help %s' for usage.\n"),
subcommand->name, optstr, subcommand->name));
- return EXIT_FAILURE;
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
}
@@ -2325,26 +2819,18 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool)
err = svn_error_quick_wrap(err,
_("Try 'svnadmin help' for more info"));
}
- return EXIT_ERROR(err);
- }
- else
- {
- /* Ensure that everything is written to stdout, so the user will
- see any print errors. */
- err = svn_cmdline_fflush(stdout);
- if (err)
- {
- return EXIT_ERROR(err);
- }
- return EXIT_SUCCESS;
+ return err;
}
+
+ return SVN_NO_ERROR;
}
int
main(int argc, const char *argv[])
{
apr_pool_t *pool;
- int exit_code;
+ int exit_code = EXIT_SUCCESS;
+ svn_error_t *err;
/* Initialize the app. */
if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS)
@@ -2355,7 +2841,17 @@ main(int argc, const char *argv[])
*/
pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
- exit_code = sub_main(argc, argv, pool);
+ err = sub_main(&exit_code, argc, argv, pool);
+
+ /* Flush stdout and report if it fails. It would be flushed on exit anyway
+ but this makes sure that output is not silently lost if it fails. */
+ err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+ if (err)
+ {
+ exit_code = EXIT_FAILURE;
+ svn_cmdline_handle_exit_error(err, NULL, "svnadmin: ");
+ }
svn_pool_destroy(pool);
return exit_code;