svnadmin.c revision 362181
1251881Speter/* 2251881Speter * svnadmin.c: Subversion server administration tool main file. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter#include <apr_file_io.h> 26251881Speter 27251881Speter#include "svn_hash.h" 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_cmdline.h" 30251881Speter#include "svn_error.h" 31251881Speter#include "svn_opt.h" 32251881Speter#include "svn_utf.h" 33251881Speter#include "svn_subst.h" 34251881Speter#include "svn_dirent_uri.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_config.h" 37251881Speter#include "svn_repos.h" 38251881Speter#include "svn_cache_config.h" 39251881Speter#include "svn_version.h" 40251881Speter#include "svn_props.h" 41289180Speter#include "svn_sorts.h" 42251881Speter#include "svn_time.h" 43251881Speter#include "svn_user.h" 44251881Speter#include "svn_xml.h" 45362181Sdim#include "svn_fs.h" 46251881Speter 47289180Speter#include "private/svn_cmdline_private.h" 48251881Speter#include "private/svn_opt_private.h" 49289180Speter#include "private/svn_sorts_private.h" 50251881Speter#include "private/svn_subr_private.h" 51362181Sdim#include "private/svn_cmdline_private.h" 52362181Sdim#include "private/svn_fspath.h" 53362181Sdim#include "private/svn_fs_fs_private.h" 54251881Speter 55251881Speter#include "svn_private_config.h" 56251881Speter 57251881Speter 58251881Speter/*** Code. ***/ 59251881Speter 60289180Speter/* FSFS format 7's "block-read" feature performs poorly with small caches. 61289180Speter * Enable it only if caches above this threshold have been configured. 62289180Speter * The current threshold is 64MB. */ 63289180Speter#define BLOCK_READ_CACHE_THRESHOLD (0x40 * 0x100000) 64289180Speter 65362181Sdimstatic svn_cancel_func_t check_cancel = NULL; 66251881Speter 67251881Speter/* Custom filesystem warning function. */ 68251881Speterstatic void 69251881Speterwarning_func(void *baton, 70251881Speter svn_error_t *err) 71251881Speter{ 72251881Speter if (! err) 73251881Speter return; 74289180Speter svn_handle_warning2(stderr, err, "svnadmin: "); 75251881Speter} 76251881Speter 77251881Speter 78251881Speter/* Version compatibility check */ 79251881Speterstatic svn_error_t * 80251881Spetercheck_lib_versions(void) 81251881Speter{ 82251881Speter static const svn_version_checklist_t checklist[] = 83251881Speter { 84251881Speter { "svn_subr", svn_subr_version }, 85251881Speter { "svn_repos", svn_repos_version }, 86251881Speter { "svn_fs", svn_fs_version }, 87251881Speter { "svn_delta", svn_delta_version }, 88251881Speter { NULL, NULL } 89251881Speter }; 90251881Speter SVN_VERSION_DEFINE(my_version); 91251881Speter 92257936Speter return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); 93251881Speter} 94251881Speter 95251881Speter 96251881Speter 97251881Speter/** Subcommands. **/ 98251881Speter 99251881Speterstatic svn_opt_subcommand_t 100362181Sdim subcommand_build_repcache, 101251881Speter subcommand_crashtest, 102251881Speter subcommand_create, 103289180Speter subcommand_delrevprop, 104251881Speter subcommand_deltify, 105251881Speter subcommand_dump, 106362181Sdim subcommand_dump_revprops, 107251881Speter subcommand_freeze, 108251881Speter subcommand_help, 109251881Speter subcommand_hotcopy, 110289180Speter subcommand_info, 111251881Speter subcommand_load, 112362181Sdim subcommand_load_revprops, 113251881Speter subcommand_list_dblogs, 114251881Speter subcommand_list_unused_dblogs, 115251881Speter subcommand_lock, 116251881Speter subcommand_lslocks, 117251881Speter subcommand_lstxns, 118251881Speter subcommand_pack, 119251881Speter subcommand_recover, 120362181Sdim subcommand_rev_size, 121251881Speter subcommand_rmlocks, 122251881Speter subcommand_rmtxns, 123251881Speter subcommand_setlog, 124251881Speter subcommand_setrevprop, 125251881Speter subcommand_setuuid, 126251881Speter subcommand_unlock, 127251881Speter subcommand_upgrade, 128251881Speter subcommand_verify; 129251881Speter 130251881Speterenum svnadmin__cmdline_options_t 131251881Speter { 132251881Speter svnadmin__version = SVN_OPT_FIRST_LONGOPT_ID, 133251881Speter svnadmin__incremental, 134289180Speter svnadmin__keep_going, 135251881Speter svnadmin__deltas, 136251881Speter svnadmin__ignore_uuid, 137251881Speter svnadmin__force_uuid, 138251881Speter svnadmin__fs_type, 139251881Speter svnadmin__parent_dir, 140251881Speter svnadmin__bdb_txn_nosync, 141251881Speter svnadmin__bdb_log_keep, 142251881Speter svnadmin__config_dir, 143251881Speter svnadmin__bypass_hooks, 144251881Speter svnadmin__bypass_prop_validation, 145289180Speter svnadmin__ignore_dates, 146251881Speter svnadmin__use_pre_commit_hook, 147251881Speter svnadmin__use_post_commit_hook, 148251881Speter svnadmin__use_pre_revprop_change_hook, 149251881Speter svnadmin__use_post_revprop_change_hook, 150251881Speter svnadmin__clean_logs, 151251881Speter svnadmin__wait, 152251881Speter svnadmin__pre_1_4_compatible, 153251881Speter svnadmin__pre_1_5_compatible, 154251881Speter svnadmin__pre_1_6_compatible, 155289180Speter svnadmin__compatible_version, 156289180Speter svnadmin__check_normalization, 157362181Sdim svnadmin__metadata_only, 158362181Sdim svnadmin__no_flush_to_disk, 159362181Sdim svnadmin__normalize_props, 160362181Sdim svnadmin__exclude, 161362181Sdim svnadmin__include, 162362181Sdim svnadmin__glob 163251881Speter }; 164251881Speter 165251881Speter/* Option codes and descriptions. 166251881Speter * 167251881Speter * The entire list must be terminated with an entry of nulls. 168251881Speter */ 169251881Speterstatic const apr_getopt_option_t options_table[] = 170251881Speter { 171251881Speter {"help", 'h', 0, 172251881Speter N_("show help on a subcommand")}, 173251881Speter 174251881Speter {NULL, '?', 0, 175251881Speter N_("show help on a subcommand")}, 176251881Speter 177251881Speter {"version", svnadmin__version, 0, 178251881Speter N_("show program version information")}, 179251881Speter 180251881Speter {"revision", 'r', 1, 181251881Speter N_("specify revision number ARG (or X:Y range)")}, 182251881Speter 183251881Speter {"transaction", 't', 1, 184251881Speter N_("specify transaction name ARG")}, 185251881Speter 186251881Speter {"incremental", svnadmin__incremental, 0, 187251881Speter N_("dump or hotcopy incrementally")}, 188251881Speter 189251881Speter {"deltas", svnadmin__deltas, 0, 190251881Speter N_("use deltas in dump output")}, 191251881Speter 192251881Speter {"bypass-hooks", svnadmin__bypass_hooks, 0, 193251881Speter N_("bypass the repository hook system")}, 194251881Speter 195251881Speter {"bypass-prop-validation", svnadmin__bypass_prop_validation, 0, 196251881Speter N_("bypass property validation logic")}, 197251881Speter 198289180Speter {"ignore-dates", svnadmin__ignore_dates, 0, 199289180Speter N_("ignore revision datestamps found in the stream")}, 200289180Speter 201251881Speter {"quiet", 'q', 0, 202289180Speter N_("no progress (only errors to stderr)")}, 203251881Speter 204251881Speter {"ignore-uuid", svnadmin__ignore_uuid, 0, 205251881Speter N_("ignore any repos UUID found in the stream")}, 206251881Speter 207251881Speter {"force-uuid", svnadmin__force_uuid, 0, 208251881Speter N_("set repos UUID to that found in stream, if any")}, 209251881Speter 210251881Speter {"fs-type", svnadmin__fs_type, 1, 211289180Speter N_("type of repository:\n" 212289180Speter " 'fsfs' (default), 'bdb' or 'fsx'\n" 213289180Speter " CAUTION: FSX is for EXPERIMENTAL use only!")}, 214251881Speter 215251881Speter {"parent-dir", svnadmin__parent_dir, 1, 216251881Speter N_("load at specified directory in repository")}, 217251881Speter 218251881Speter {"bdb-txn-nosync", svnadmin__bdb_txn_nosync, 0, 219251881Speter N_("disable fsync at transaction commit [Berkeley DB]")}, 220251881Speter 221251881Speter {"bdb-log-keep", svnadmin__bdb_log_keep, 0, 222251881Speter N_("disable automatic log file removal [Berkeley DB]")}, 223251881Speter 224251881Speter {"config-dir", svnadmin__config_dir, 1, 225251881Speter N_("read user configuration files from directory ARG")}, 226251881Speter 227251881Speter {"clean-logs", svnadmin__clean_logs, 0, 228251881Speter N_("remove redundant Berkeley DB log files\n" 229251881Speter " from source repository [Berkeley DB]")}, 230251881Speter 231251881Speter {"use-pre-commit-hook", svnadmin__use_pre_commit_hook, 0, 232251881Speter N_("call pre-commit hook before committing revisions")}, 233251881Speter 234251881Speter {"use-post-commit-hook", svnadmin__use_post_commit_hook, 0, 235251881Speter N_("call post-commit hook after committing revisions")}, 236251881Speter 237251881Speter {"use-pre-revprop-change-hook", svnadmin__use_pre_revprop_change_hook, 0, 238251881Speter N_("call hook before changing revision property")}, 239251881Speter 240251881Speter {"use-post-revprop-change-hook", svnadmin__use_post_revprop_change_hook, 0, 241251881Speter N_("call hook after changing revision property")}, 242251881Speter 243251881Speter {"wait", svnadmin__wait, 0, 244251881Speter N_("wait instead of exit if the repository is in\n" 245251881Speter " use by another process")}, 246251881Speter 247251881Speter {"pre-1.4-compatible", svnadmin__pre_1_4_compatible, 0, 248251881Speter N_("deprecated; see --compatible-version")}, 249251881Speter 250251881Speter {"pre-1.5-compatible", svnadmin__pre_1_5_compatible, 0, 251251881Speter N_("deprecated; see --compatible-version")}, 252251881Speter 253251881Speter {"pre-1.6-compatible", svnadmin__pre_1_6_compatible, 0, 254251881Speter N_("deprecated; see --compatible-version")}, 255251881Speter 256289180Speter {"keep-going", svnadmin__keep_going, 0, 257289180Speter N_("continue verification after detecting a corruption")}, 258289180Speter 259251881Speter {"memory-cache-size", 'M', 1, 260251881Speter N_("size of the extra in-memory cache in MB used to\n" 261251881Speter " minimize redundant operations. Default: 16.\n" 262251881Speter " [used for FSFS repositories only]")}, 263251881Speter 264251881Speter {"compatible-version", svnadmin__compatible_version, 1, 265251881Speter N_("use repository format compatible with Subversion\n" 266251881Speter " version ARG (\"1.5.5\", \"1.7\", etc.)")}, 267251881Speter 268251881Speter {"file", 'F', 1, N_("read repository paths from file ARG")}, 269251881Speter 270289180Speter {"check-normalization", svnadmin__check_normalization, 0, 271289180Speter N_("report any names within the same directory or\n" 272289180Speter " svn:mergeinfo property value that differ only\n" 273289180Speter " in character representation, but are otherwise\n" 274289180Speter " identical")}, 275289180Speter 276289180Speter {"metadata-only", svnadmin__metadata_only, 0, 277289180Speter N_("verify metadata only (ignored for BDB),\n" 278289180Speter " checking against external corruption in\n" 279289180Speter " Subversion 1.9+ format repositories.\n")}, 280289180Speter 281362181Sdim {"no-flush-to-disk", svnadmin__no_flush_to_disk, 0, 282362181Sdim N_("disable flushing to disk during the operation\n" 283362181Sdim " (faster, but unsafe on power off)")}, 284362181Sdim 285362181Sdim {"normalize-props", svnadmin__normalize_props, 0, 286362181Sdim N_("normalize property values found in the dumpstream\n" 287362181Sdim " (currently, only translates non-LF line endings)")}, 288362181Sdim 289362181Sdim {"exclude", svnadmin__exclude, 1, 290362181Sdim N_("filter out nodes with given prefix(es) from dump")}, 291362181Sdim 292362181Sdim {"include", svnadmin__include, 1, 293362181Sdim N_("filter out nodes without given prefix(es) from dump")}, 294362181Sdim 295362181Sdim {"pattern", svnadmin__glob, 0, 296362181Sdim N_("treat the path prefixes as file glob patterns.\n" 297362181Sdim " Glob special characters are '*' '?' '[]' and '\\'.\n" 298362181Sdim " Character '/' is not treated specially, so\n" 299362181Sdim " pattern /*/foo matches paths /a/foo and /a/b/foo.") }, 300362181Sdim 301251881Speter {NULL} 302251881Speter }; 303251881Speter 304251881Speter 305251881Speter/* Array of available subcommands. 306251881Speter * The entire list must be terminated with an entry of nulls. 307251881Speter */ 308362181Sdimstatic const svn_opt_subcommand_desc3_t cmd_table[] = 309251881Speter{ 310362181Sdim {"build-repcache", subcommand_build_repcache, {0}, {N_( 311362181Sdim "usage: svnadmin build-repcache REPOS_PATH [-r LOWER[:UPPER]]\n" 312362181Sdim "\n"), N_( 313362181Sdim "Add missing entries to the representation cache for the repository\n" 314362181Sdim "at REPOS_PATH. Process data in revisions LOWER through UPPER.\n" 315362181Sdim "If no revision arguments are given, process all revisions. If only\n" 316362181Sdim "LOWER revision argument is given, process only that single revision.\n" 317362181Sdim )}, 318362181Sdim {'r', 'q', 'M'} }, 319362181Sdim 320362181Sdim {"crashtest", subcommand_crashtest, {0}, {N_( 321362181Sdim "usage: svnadmin crashtest REPOS_PATH\n" 322362181Sdim "\n"), N_( 323251881Speter "Open the repository at REPOS_PATH, then abort, thus simulating\n" 324362181Sdim "a process that crashes while holding an open repository handle.\n" 325362181Sdim )}, 326251881Speter {0} }, 327251881Speter 328362181Sdim {"create", subcommand_create, {0}, {N_( 329362181Sdim "usage: svnadmin create REPOS_PATH\n" 330362181Sdim "\n"), N_( 331362181Sdim "Create a new, empty repository at REPOS_PATH.\n" 332362181Sdim )}, 333251881Speter {svnadmin__bdb_txn_nosync, svnadmin__bdb_log_keep, 334251881Speter svnadmin__config_dir, svnadmin__fs_type, svnadmin__compatible_version, 335251881Speter svnadmin__pre_1_4_compatible, svnadmin__pre_1_5_compatible, 336251881Speter svnadmin__pre_1_6_compatible 337251881Speter } }, 338251881Speter 339362181Sdim {"delrevprop", subcommand_delrevprop, {0}, {N_( 340362181Sdim "usage: 1. svnadmin delrevprop REPOS_PATH -r REVISION NAME\n" 341362181Sdim " 2. svnadmin delrevprop REPOS_PATH -t TXN NAME\n" 342362181Sdim "\n"), N_( 343362181Sdim "1. Delete the property NAME on revision REVISION.\n" 344362181Sdim "\n"), N_( 345289180Speter "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" 346289180Speter "trigger the revision property-related hooks (for example, if you want\n" 347362181Sdim "an email notification sent from your post-revprop-change hook).\n" 348362181Sdim "\n"), N_( 349289180Speter "NOTE: Revision properties are not versioned, so this command will\n" 350362181Sdim "irreversibly destroy the previous value of the property.\n" 351362181Sdim "\n"), N_( 352362181Sdim "2. Delete the property NAME on transaction TXN.\n" 353362181Sdim )}, 354289180Speter {'r', 't', svnadmin__use_pre_revprop_change_hook, 355289180Speter svnadmin__use_post_revprop_change_hook} }, 356289180Speter 357362181Sdim {"deltify", subcommand_deltify, {0}, {N_( 358362181Sdim "usage: svnadmin deltify [-r LOWER[:UPPER]] REPOS_PATH\n" 359362181Sdim "\n"), N_( 360251881Speter "Run over the requested revision range, performing predecessor delti-\n" 361251881Speter "fication on the paths changed in those revisions. Deltification in\n" 362251881Speter "essence compresses the repository by only storing the differences or\n" 363251881Speter "delta from the preceding revision. If no revisions are specified,\n" 364362181Sdim "this will simply deltify the HEAD revision.\n" 365362181Sdim )}, 366251881Speter {'r', 'q', 'M'} }, 367251881Speter 368362181Sdim {"dump", subcommand_dump, {0}, {N_( 369362181Sdim "usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER] [--incremental]]\n" 370362181Sdim "\n"), N_( 371251881Speter "Dump the contents of filesystem to stdout in a 'dumpfile'\n" 372251881Speter "portable format, sending feedback to stderr. Dump revisions\n" 373251881Speter "LOWER rev through UPPER rev. If no revisions are given, dump all\n" 374251881Speter "revision trees. If only LOWER is given, dump that one revision tree.\n" 375251881Speter "If --incremental is passed, the first revision dumped will describe\n" 376251881Speter "only the paths changed in that revision; otherwise it will describe\n" 377251881Speter "every path present in the repository as of that revision. (In either\n" 378251881Speter "case, the second and subsequent revisions, if any, describe only paths\n" 379362181Sdim "changed in those revisions.)\n" 380362181Sdim "\n"), N_( 381362181Sdim "Using --exclude or --include gives results equivalent to authz-based\n" 382362181Sdim "path exclusions. In particular, when the source of a copy is\n" 383362181Sdim "excluded, the copy is transformed into an add (unlike in 'svndumpfilter').\n" 384362181Sdim )}, 385362181Sdim {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M', 'F', 386362181Sdim svnadmin__exclude, svnadmin__include, svnadmin__glob }, 387362181Sdim {{'F', N_("write to file ARG instead of stdout")}} }, 388251881Speter 389362181Sdim {"dump-revprops", subcommand_dump_revprops, {0}, {N_( 390362181Sdim "usage: svnadmin dump-revprops REPOS_PATH [-r LOWER[:UPPER]]\n" 391362181Sdim "\n"), N_( 392362181Sdim "Dump the revision properties of filesystem to stdout in a 'dumpfile'\n" 393362181Sdim "portable format, sending feedback to stderr. Dump revisions\n" 394362181Sdim "LOWER rev through UPPER rev. If no revisions are given, dump the\n" 395362181Sdim "properties for all revisions. If only LOWER is given, dump the\n" 396362181Sdim "properties for that one revision.\n" 397362181Sdim )}, 398362181Sdim {'r', 'q', 'F'}, 399362181Sdim {{'F', N_("write to file ARG instead of stdout")}} }, 400362181Sdim 401362181Sdim {"freeze", subcommand_freeze, {0}, {N_( 402362181Sdim "usage: 1. svnadmin freeze REPOS_PATH -- PROGRAM [ARG...]\n" 403362181Sdim " 2. svnadmin freeze -F FILE -- PROGRAM [ARG...]\n" 404362181Sdim "\n"), N_( 405251881Speter "1. Run PROGRAM passing ARGS while holding a write-lock on REPOS_PATH.\n" 406322442Speter " Allows safe use of third-party backup tools on a live repository.\n" 407362181Sdim "\n"), N_( 408251881Speter "2. Like 1 except all repositories listed in FILE are locked. The file\n" 409251881Speter " format is repository paths separated by newlines. Repositories are\n" 410362181Sdim " locked in the same order as they are listed in the file.\n" 411362181Sdim "\n" 412362181Sdim "The '--' tells svnadmin to stop looking for svnadmin options and pass\n" 413362181Sdim "all later arguments to PROGRAM even if they begin with '-'.\n" 414362181Sdim )}, 415362181Sdim {'F'}, 416362181Sdim {{'F', N_("read repository paths from file ARG")}} }, 417251881Speter 418362181Sdim {"help", subcommand_help, {"?", "h"}, {N_( 419362181Sdim "usage: svnadmin help [SUBCOMMAND...]\n" 420362181Sdim "\n"), N_( 421362181Sdim "Describe the usage of this program or its subcommands.\n" 422362181Sdim )}, 423251881Speter {0} }, 424251881Speter 425362181Sdim {"hotcopy", subcommand_hotcopy, {0}, {N_( 426362181Sdim "usage: svnadmin hotcopy REPOS_PATH NEW_REPOS_PATH\n" 427362181Sdim "\n"), N_( 428251881Speter "Make a hot copy of a repository.\n" 429251881Speter "If --incremental is passed, data which already exists at the destination\n" 430362181Sdim "is not copied again. Incremental mode is implemented for FSFS repositories.\n" 431362181Sdim )}, 432289180Speter {svnadmin__clean_logs, svnadmin__incremental, 'q'} }, 433251881Speter 434362181Sdim {"info", subcommand_info, {0}, {N_( 435362181Sdim "usage: svnadmin info REPOS_PATH\n" 436362181Sdim "\n"), N_( 437362181Sdim "Print information about the repository at REPOS_PATH.\n" 438362181Sdim )}, 439289180Speter {0} }, 440289180Speter 441362181Sdim {"list-dblogs", subcommand_list_dblogs, {0}, {N_( 442362181Sdim "usage: svnadmin list-dblogs REPOS_PATH\n" 443362181Sdim "\n"), N_( 444362181Sdim "List all Berkeley DB log files.\n" 445362181Sdim "\n"), N_( 446251881Speter "WARNING: Modifying or deleting logfiles which are still in use\n" 447362181Sdim "will cause your repository to be corrupted.\n" 448362181Sdim )}, 449251881Speter {0} }, 450251881Speter 451362181Sdim {"list-unused-dblogs", subcommand_list_unused_dblogs, {0}, {N_( 452362181Sdim "usage: svnadmin list-unused-dblogs REPOS_PATH\n" 453362181Sdim "\n"), N_( 454362181Sdim "List unused Berkeley DB log files.\n" 455362181Sdim )}, 456251881Speter {0} }, 457251881Speter 458362181Sdim {"load", subcommand_load, {0}, {N_( 459362181Sdim "usage: svnadmin load REPOS_PATH\n" 460362181Sdim "\n"), N_( 461251881Speter "Read a 'dumpfile'-formatted stream from stdin, committing\n" 462251881Speter "new revisions into the repository's filesystem. If the repository\n" 463251881Speter "was previously empty, its UUID will, by default, be changed to the\n" 464251881Speter "one specified in the stream. Progress feedback is sent to stdout.\n" 465251881Speter "If --revision is specified, limit the loaded revisions to only those\n" 466362181Sdim "in the dump stream whose revision numbers match the specified range.\n" 467362181Sdim )}, 468251881Speter {'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid, 469289180Speter svnadmin__ignore_dates, 470251881Speter svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook, 471362181Sdim svnadmin__parent_dir, svnadmin__normalize_props, 472362181Sdim svnadmin__bypass_prop_validation, 'M', 473362181Sdim svnadmin__no_flush_to_disk, 'F'}, 474362181Sdim {{'F', N_("read from file ARG instead of stdin")}} }, 475251881Speter 476362181Sdim {"load-revprops", subcommand_load_revprops, {0}, {N_( 477362181Sdim "usage: svnadmin load-revprops REPOS_PATH\n" 478362181Sdim "\n"), N_( 479362181Sdim "Read a 'dumpfile'-formatted stream from stdin, setting the revision\n" 480362181Sdim "properties in the repository's filesystem. Revisions not found in the\n" 481362181Sdim "repository will cause an error. Progress feedback is sent to stdout.\n" 482362181Sdim "If --revision is specified, limit the loaded revisions to only those\n" 483362181Sdim "in the dump stream whose revision numbers match the specified range.\n" 484362181Sdim )}, 485362181Sdim {'q', 'r', svnadmin__force_uuid, svnadmin__normalize_props, 486362181Sdim svnadmin__bypass_prop_validation, svnadmin__no_flush_to_disk, 'F'}, 487362181Sdim {{'F', N_("read from file ARG instead of stdin")}} }, 488362181Sdim 489362181Sdim {"lock", subcommand_lock, {0}, {N_( 490362181Sdim "usage: svnadmin lock REPOS_PATH PATH USERNAME COMMENT-FILE [TOKEN]\n" 491362181Sdim "\n"), N_( 492251881Speter "Lock PATH by USERNAME setting comments from COMMENT-FILE.\n" 493251881Speter "If provided, use TOKEN as lock token. Use --bypass-hooks to avoid\n" 494362181Sdim "triggering the pre-lock and post-lock hook scripts.\n" 495362181Sdim )}, 496362181Sdim {svnadmin__bypass_hooks, 'q'} }, 497251881Speter 498362181Sdim {"lslocks", subcommand_lslocks, {0}, {N_( 499362181Sdim "usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n" 500362181Sdim "\n"), N_( 501251881Speter "Print descriptions of all locks on or under PATH-IN-REPOS (which,\n" 502362181Sdim "if not provided, is the root of the repository).\n" 503362181Sdim )}, 504251881Speter {0} }, 505251881Speter 506362181Sdim {"lstxns", subcommand_lstxns, {0}, {N_( 507362181Sdim "usage: svnadmin lstxns REPOS_PATH\n" 508362181Sdim "\n"), N_( 509362181Sdim "Print the names of uncommitted transactions. With -rN skip the output\n" 510362181Sdim "of those that have a base revision more recent than rN. Transactions\n" 511362181Sdim "with base revisions much older than HEAD are likely to have been\n" 512362181Sdim "abandoned and are candidates to be removed.\n" 513362181Sdim )}, 514362181Sdim {'r'}, 515362181Sdim { {'r', "transaction base revision ARG"} } }, 516251881Speter 517362181Sdim {"pack", subcommand_pack, {0}, {N_( 518362181Sdim "usage: svnadmin pack REPOS_PATH\n" 519362181Sdim "\n"), N_( 520251881Speter "Possibly compact the repository into a more efficient storage model.\n" 521362181Sdim "This may not apply to all repositories, in which case, exit.\n" 522362181Sdim )}, 523289180Speter {'q', 'M'} }, 524251881Speter 525362181Sdim {"recover", subcommand_recover, {0}, {N_( 526362181Sdim "usage: svnadmin recover REPOS_PATH\n" 527362181Sdim "\n"), N_( 528251881Speter "Run the recovery procedure on a repository. Do this if you've\n" 529251881Speter "been getting errors indicating that recovery ought to be run.\n" 530251881Speter "Berkeley DB recovery requires exclusive access and will\n" 531362181Sdim "exit if the repository is in use by another process.\n" 532362181Sdim )}, 533251881Speter {svnadmin__wait} }, 534251881Speter 535362181Sdim {"rev-size", subcommand_rev_size, {0}, {N_( 536362181Sdim "usage: svnadmin rev-size REPOS_PATH -r REVISION\n" 537362181Sdim "\n"), N_( 538362181Sdim "Print the total size in bytes of the representation on disk of\n" 539362181Sdim "revision REVISION.\n" 540362181Sdim "\n"), N_( 541362181Sdim "The size includes revision properties and excludes FSFS indexes.\n" 542362181Sdim )}, 543362181Sdim {'r', 'q', 'M'}, 544362181Sdim { {'q', "print only the size and a newline"} } }, 545251881Speter 546362181Sdim {"rmlocks", subcommand_rmlocks, {0}, {N_( 547362181Sdim "usage: svnadmin rmlocks REPOS_PATH LOCKED_PATH...\n" 548362181Sdim "\n"), N_( 549362181Sdim "Unconditionally remove lock from each LOCKED_PATH.\n" 550362181Sdim )}, 551251881Speter {'q'} }, 552251881Speter 553362181Sdim {"rmtxns", subcommand_rmtxns, {0}, {N_( 554362181Sdim "usage: svnadmin rmtxns REPOS_PATH TXN_NAME...\n" 555362181Sdim "\n"), N_( 556362181Sdim "Delete the named transaction(s).\n" 557362181Sdim )}, 558362181Sdim {'q'} }, 559362181Sdim 560362181Sdim {"setlog", subcommand_setlog, {0}, {N_( 561362181Sdim "usage: svnadmin setlog REPOS_PATH -r REVISION FILE\n" 562362181Sdim "\n"), N_( 563251881Speter "Set the log-message on revision REVISION to the contents of FILE. Use\n" 564251881Speter "--bypass-hooks to avoid triggering the revision-property-related hooks\n" 565251881Speter "(for example, if you do not want an email notification sent\n" 566251881Speter "from your post-revprop-change hook, or because the modification of\n" 567251881Speter "revision properties has not been enabled in the pre-revprop-change\n" 568362181Sdim "hook).\n" 569362181Sdim "\n"), N_( 570251881Speter "NOTE: Revision properties are not versioned, so this command will\n" 571362181Sdim "overwrite the previous log message.\n" 572362181Sdim )}, 573251881Speter {'r', svnadmin__bypass_hooks} }, 574251881Speter 575362181Sdim {"setrevprop", subcommand_setrevprop, {0}, {N_( 576362181Sdim "usage: 1. svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n" 577362181Sdim " 2. svnadmin setrevprop REPOS_PATH -t TXN NAME FILE\n" 578362181Sdim "\n"), N_( 579362181Sdim "1. Set the property NAME on revision REVISION to the contents of FILE.\n" 580362181Sdim "\n"), N_( 581289180Speter "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" 582289180Speter "trigger the revision property-related hooks (for example, if you want\n" 583362181Sdim "an email notification sent from your post-revprop-change hook).\n" 584362181Sdim "\n"), N_( 585251881Speter "NOTE: Revision properties are not versioned, so this command will\n" 586362181Sdim "overwrite the previous value of the property.\n" 587362181Sdim "\n"), N_( 588362181Sdim "2. Set the property NAME on transaction TXN to the contents of FILE.\n" 589362181Sdim )}, 590289180Speter {'r', 't', svnadmin__use_pre_revprop_change_hook, 591251881Speter svnadmin__use_post_revprop_change_hook} }, 592251881Speter 593362181Sdim {"setuuid", subcommand_setuuid, {0}, {N_( 594362181Sdim "usage: svnadmin setuuid REPOS_PATH [NEW_UUID]\n" 595362181Sdim "\n"), N_( 596251881Speter "Reset the repository UUID for the repository located at REPOS_PATH. If\n" 597251881Speter "NEW_UUID is provided, use that as the new repository UUID; otherwise,\n" 598362181Sdim "generate a brand new UUID for the repository.\n" 599362181Sdim )}, 600251881Speter {0} }, 601251881Speter 602362181Sdim {"unlock", subcommand_unlock, {0}, {N_( 603362181Sdim "usage: svnadmin unlock REPOS_PATH LOCKED_PATH USERNAME TOKEN\n" 604362181Sdim "\n"), N_( 605251881Speter "Unlock LOCKED_PATH (as USERNAME) after verifying that the token\n" 606251881Speter "associated with the lock matches TOKEN. Use --bypass-hooks to avoid\n" 607362181Sdim "triggering the pre-unlock and post-unlock hook scripts.\n" 608362181Sdim )}, 609362181Sdim {svnadmin__bypass_hooks, 'q'} }, 610251881Speter 611362181Sdim {"upgrade", subcommand_upgrade, {0}, {N_( 612362181Sdim "usage: svnadmin upgrade REPOS_PATH\n" 613362181Sdim "\n"), N_( 614251881Speter "Upgrade the repository located at REPOS_PATH to the latest supported\n" 615362181Sdim "schema version.\n" 616362181Sdim "\n"), N_( 617251881Speter "This functionality is provided as a convenience for repository\n" 618251881Speter "administrators who wish to make use of new Subversion functionality\n" 619251881Speter "without having to undertake a potentially costly full repository dump\n" 620251881Speter "and load operation. As such, the upgrade performs only the minimum\n" 621251881Speter "amount of work needed to accomplish this while still maintaining the\n" 622251881Speter "integrity of the repository. It does not guarantee the most optimized\n" 623362181Sdim "repository state as a dump and subsequent load would.\n" 624362181Sdim )}, 625251881Speter {0} }, 626251881Speter 627362181Sdim {"verify", subcommand_verify, {0}, {N_( 628362181Sdim "usage: svnadmin verify REPOS_PATH\n" 629362181Sdim "\n"), N_( 630362181Sdim "Verify the data stored in the repository.\n" 631362181Sdim )}, 632289180Speter {'t', 'r', 'q', svnadmin__keep_going, 'M', 633289180Speter svnadmin__check_normalization, svnadmin__metadata_only} }, 634251881Speter 635362181Sdim { NULL, NULL, {0}, {NULL}, {0} } 636251881Speter}; 637251881Speter 638251881Speter 639251881Speter/* Baton for passing option/argument state to a subcommand function. */ 640251881Speterstruct svnadmin_opt_state 641251881Speter{ 642251881Speter const char *repository_path; 643251881Speter const char *fs_type; /* --fs-type */ 644251881Speter svn_version_t *compatible_version; /* --compatible-version */ 645251881Speter svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */ 646251881Speter const char *txn_id; /* -t TXN */ 647251881Speter svn_boolean_t help; /* --help or -? */ 648251881Speter svn_boolean_t version; /* --version */ 649251881Speter svn_boolean_t incremental; /* --incremental */ 650251881Speter svn_boolean_t use_deltas; /* --deltas */ 651251881Speter svn_boolean_t use_pre_commit_hook; /* --use-pre-commit-hook */ 652251881Speter svn_boolean_t use_post_commit_hook; /* --use-post-commit-hook */ 653251881Speter svn_boolean_t use_pre_revprop_change_hook; /* --use-pre-revprop-change-hook */ 654251881Speter svn_boolean_t use_post_revprop_change_hook; /* --use-post-revprop-change-hook */ 655251881Speter svn_boolean_t quiet; /* --quiet */ 656251881Speter svn_boolean_t bdb_txn_nosync; /* --bdb-txn-nosync */ 657251881Speter svn_boolean_t bdb_log_keep; /* --bdb-log-keep */ 658251881Speter svn_boolean_t clean_logs; /* --clean-logs */ 659251881Speter svn_boolean_t bypass_hooks; /* --bypass-hooks */ 660251881Speter svn_boolean_t wait; /* --wait */ 661289180Speter svn_boolean_t keep_going; /* --keep-going */ 662289180Speter svn_boolean_t check_normalization; /* --check-normalization */ 663289180Speter svn_boolean_t metadata_only; /* --metadata-only */ 664251881Speter svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */ 665289180Speter svn_boolean_t ignore_dates; /* --ignore-dates */ 666362181Sdim svn_boolean_t no_flush_to_disk; /* --no-flush-to-disk */ 667362181Sdim svn_boolean_t normalize_props; /* --normalize_props */ 668251881Speter enum svn_repos_load_uuid uuid_action; /* --ignore-uuid, 669251881Speter --force-uuid */ 670251881Speter apr_uint64_t memory_cache_size; /* --memory-cache-size M */ 671289180Speter const char *parent_dir; /* --parent-dir */ 672362181Sdim const char *file; /* --file */ 673362181Sdim apr_array_header_t *exclude; /* --exclude */ 674362181Sdim apr_array_header_t *include; /* --include */ 675362181Sdim svn_boolean_t glob; /* --pattern */ 676251881Speter 677251881Speter const char *config_dir; /* Overriding Configuration Directory */ 678251881Speter}; 679251881Speter 680251881Speter 681362181Sdim/* Helper to open a repository and set a warning func (so we don't 682362181Sdim * SEGFAULT when libsvn_fs's default handler gets run). */ 683362181Sdimstatic svn_error_t * 684362181Sdimopen_repos(svn_repos_t **repos, 685362181Sdim const char *path, 686362181Sdim struct svnadmin_opt_state *opt_state, 687362181Sdim apr_pool_t *pool) 688362181Sdim{ 689362181Sdim /* Enable the "block-read" feature (where it applies)? */ 690362181Sdim svn_boolean_t use_block_read 691362181Sdim = svn_cache_config_get()->cache_size > BLOCK_READ_CACHE_THRESHOLD; 692362181Sdim 693362181Sdim /* construct FS configuration parameters: enable caches for r/o data */ 694362181Sdim apr_hash_t *fs_config = apr_hash_make(pool); 695362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1"); 696362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1"); 697362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS, "1"); 698362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2"); 699362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS, 700362181Sdim svn_uuid_generate(pool)); 701362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, 702362181Sdim use_block_read ? "1" : "0"); 703362181Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_NO_FLUSH_TO_DISK, 704362181Sdim opt_state->no_flush_to_disk ? "1" : "0"); 705362181Sdim 706362181Sdim /* now, open the requested repository */ 707362181Sdim SVN_ERR(svn_repos_open3(repos, path, fs_config, pool, pool)); 708362181Sdim svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL); 709362181Sdim return SVN_NO_ERROR; 710362181Sdim} 711362181Sdim 712362181Sdim 713251881Speter/* Set *REVNUM to the revision specified by REVISION (or to 714251881Speter SVN_INVALID_REVNUM if that has the type 'unspecified'), 715251881Speter possibly making use of the YOUNGEST revision number in REPOS. */ 716251881Speterstatic svn_error_t * 717251881Speterget_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision, 718251881Speter svn_revnum_t youngest, svn_repos_t *repos, apr_pool_t *pool) 719251881Speter{ 720251881Speter if (revision->kind == svn_opt_revision_number) 721251881Speter *revnum = revision->value.number; 722251881Speter else if (revision->kind == svn_opt_revision_head) 723251881Speter *revnum = youngest; 724251881Speter else if (revision->kind == svn_opt_revision_date) 725251881Speter SVN_ERR(svn_repos_dated_revision(revnum, repos, revision->value.date, 726251881Speter pool)); 727251881Speter else if (revision->kind == svn_opt_revision_unspecified) 728251881Speter *revnum = SVN_INVALID_REVNUM; 729251881Speter else 730251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 731251881Speter _("Invalid revision specifier")); 732251881Speter 733251881Speter if (*revnum > youngest) 734251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 735251881Speter _("Revisions must not be greater than the youngest revision (%ld)"), 736251881Speter youngest); 737251881Speter 738251881Speter return SVN_NO_ERROR; 739251881Speter} 740251881Speter 741362181Sdim/* Set *FSPATH to an internal-style fspath parsed from ARG. */ 742251881Speterstatic svn_error_t * 743362181Sdimtarget_arg_to_fspath(const char **fspath, 744362181Sdim const char *arg, 745362181Sdim apr_pool_t *result_pool, 746362181Sdim apr_pool_t *scratch_pool) 747362181Sdim{ 748362181Sdim /* ### Using a private API. This really shouldn't be needed. */ 749362181Sdim *fspath = svn_fspath__canonicalize(arg, result_pool); 750362181Sdim return SVN_NO_ERROR; 751362181Sdim} 752362181Sdim 753362181Sdim/* Set *DIRENT to an internal-style, local dirent path 754362181Sdim allocated from POOL and parsed from PATH. */ 755362181Sdimstatic svn_error_t * 756251881Spetertarget_arg_to_dirent(const char **dirent, 757362181Sdim const char *path, 758251881Speter apr_pool_t *pool) 759251881Speter{ 760251881Speter if (svn_path_is_url(path)) 761251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 762289180Speter _("Path '%s' is not a local path"), path); 763251881Speter *dirent = svn_dirent_internal_style(path, pool); 764251881Speter return SVN_NO_ERROR; 765251881Speter} 766251881Speter 767251881Speter/* Parse the remaining command-line arguments from OS, returning them 768251881Speter in a new array *ARGS (allocated from POOL) and optionally verifying 769251881Speter that we got the expected number thereof. If MIN_EXPECTED is not 770251881Speter negative, return an error if the function would return fewer than 771251881Speter MIN_EXPECTED arguments. If MAX_EXPECTED is not negative, return an 772251881Speter error if the function would return more than MAX_EXPECTED 773251881Speter arguments. 774251881Speter 775251881Speter As a special case, when MIN_EXPECTED and MAX_EXPECTED are both 0, 776251881Speter allow ARGS to be NULL. */ 777251881Speterstatic svn_error_t * 778251881Speterparse_args(apr_array_header_t **args, 779251881Speter apr_getopt_t *os, 780251881Speter int min_expected, 781251881Speter int max_expected, 782251881Speter apr_pool_t *pool) 783251881Speter{ 784251881Speter int num_args = os ? (os->argc - os->ind) : 0; 785251881Speter 786251881Speter if (min_expected || max_expected) 787251881Speter SVN_ERR_ASSERT(args); 788251881Speter 789251881Speter if ((min_expected >= 0) && (num_args < min_expected)) 790251881Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, 791289180Speter _("Not enough arguments")); 792251881Speter if ((max_expected >= 0) && (num_args > max_expected)) 793251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 794289180Speter _("Too many arguments")); 795251881Speter if (args) 796251881Speter { 797251881Speter *args = apr_array_make(pool, num_args, sizeof(const char *)); 798251881Speter 799251881Speter if (num_args) 800251881Speter while (os->ind < os->argc) 801362181Sdim { 802362181Sdim const char *arg; 803362181Sdim 804362181Sdim SVN_ERR(svn_utf_cstring_to_utf8(&arg, os->argv[os->ind++], pool)); 805362181Sdim APR_ARRAY_PUSH(*args, const char *) = arg; 806362181Sdim } 807251881Speter } 808251881Speter 809251881Speter return SVN_NO_ERROR; 810251881Speter} 811251881Speter 812251881Speter 813289180Speter/* This implements 'svn_error_malfunction_handler_t. */ 814289180Speterstatic svn_error_t * 815289180Spetercrashtest_malfunction_handler(svn_boolean_t can_return, 816289180Speter const char *file, 817289180Speter int line, 818289180Speter const char *expr) 819289180Speter{ 820289180Speter abort(); 821289180Speter return SVN_NO_ERROR; /* Not reached. */ 822289180Speter} 823289180Speter 824251881Speter/* This implements `svn_opt_subcommand_t'. */ 825251881Speterstatic svn_error_t * 826251881Spetersubcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool) 827251881Speter{ 828251881Speter struct svnadmin_opt_state *opt_state = baton; 829251881Speter svn_repos_t *repos; 830251881Speter 831289180Speter (void)svn_error_set_malfunction_handler(crashtest_malfunction_handler); 832362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 833289180Speter SVN_ERR(svn_cmdline_printf(pool, 834289180Speter _("Successfully opened repository '%s'.\n" 835289180Speter "Will now crash to simulate a crashing " 836289180Speter "server process.\n"), 837289180Speter svn_dirent_local_style(opt_state->repository_path, 838289180Speter pool))); 839251881Speter SVN_ERR_MALFUNCTION(); 840251881Speter 841251881Speter /* merely silence a compiler warning (this will never be executed) */ 842251881Speter return SVN_NO_ERROR; 843251881Speter} 844251881Speter 845251881Speter/* This implements `svn_opt_subcommand_t'. */ 846251881Speterstatic svn_error_t * 847251881Spetersubcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool) 848251881Speter{ 849251881Speter struct svnadmin_opt_state *opt_state = baton; 850251881Speter svn_repos_t *repos; 851251881Speter apr_hash_t *fs_config = apr_hash_make(pool); 852251881Speter 853251881Speter /* Expect no more arguments. */ 854251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 855251881Speter 856251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC, 857251881Speter (opt_state->bdb_txn_nosync ? "1" :"0")); 858251881Speter 859251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE, 860251881Speter (opt_state->bdb_log_keep ? "0" :"1")); 861251881Speter 862251881Speter if (opt_state->fs_type) 863251881Speter { 864251881Speter /* With 1.8 we are announcing that BDB is deprecated. No support 865251881Speter * has been removed and it will continue to work until some future 866251881Speter * date. The purpose here is to discourage people from creating 867251881Speter * new BDB repositories which they will need to dump/load into 868251881Speter * FSFS or some new FS type in the future. */ 869251881Speter if (0 == strcmp(opt_state->fs_type, SVN_FS_TYPE_BDB)) 870251881Speter { 871251881Speter SVN_ERR(svn_cmdline_fprintf( 872251881Speter stderr, pool, 873251881Speter _("%swarning:" 874251881Speter " The \"%s\" repository back-end is deprecated," 875251881Speter " consider using \"%s\" instead.\n"), 876251881Speter "svnadmin: ", SVN_FS_TYPE_BDB, SVN_FS_TYPE_FSFS)); 877251881Speter fflush(stderr); 878251881Speter } 879251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, opt_state->fs_type); 880251881Speter } 881251881Speter 882251881Speter if (opt_state->compatible_version) 883251881Speter { 884251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 4, 0)) 885251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1"); 886251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 5, 0)) 887251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1"); 888251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 6, 0)) 889251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1"); 890251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 8, 0)) 891251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE, "1"); 892289180Speter /* In 1.9, we figured out that we didn't have to keep extending this 893289180Speter madness indefinitely. */ 894289180Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION, 895289180Speter apr_psprintf(pool, "%d.%d.%d%s%s", 896289180Speter opt_state->compatible_version->major, 897289180Speter opt_state->compatible_version->minor, 898289180Speter opt_state->compatible_version->patch, 899289180Speter opt_state->compatible_version->tag 900289180Speter ? "-" : "", 901289180Speter opt_state->compatible_version->tag 902289180Speter ? opt_state->compatible_version->tag : "")); 903251881Speter } 904251881Speter 905289180Speter if (opt_state->compatible_version) 906253734Speter { 907289180Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 1, 0) 908289180Speter /* ### TODO: this NULL check hard-codes knowledge of the library's 909289180Speter default fs-type value */ 910289180Speter && (opt_state->fs_type == NULL 911289180Speter || !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSFS))) 912289180Speter { 913289180Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 914289180Speter _("Repositories compatible with 1.0.x must " 915289180Speter "use --fs-type=bdb")); 916289180Speter } 917289180Speter 918289180Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 9, 0) 919289180Speter && opt_state->fs_type && !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSX)) 920289180Speter { 921289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 922289180Speter _("Repositories compatible with 1.8.x or " 923289180Speter "earlier cannot use --fs-type=%s"), 924289180Speter SVN_FS_TYPE_FSX); 925289180Speter } 926253734Speter } 927253734Speter 928251881Speter SVN_ERR(svn_repos_create(&repos, opt_state->repository_path, 929251881Speter NULL, NULL, NULL, fs_config, pool)); 930251881Speter svn_fs_set_warning_func(svn_repos_fs(repos), warning_func, NULL); 931251881Speter return SVN_NO_ERROR; 932251881Speter} 933251881Speter 934251881Speter 935251881Speter/* This implements `svn_opt_subcommand_t'. */ 936251881Speterstatic svn_error_t * 937251881Spetersubcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool) 938251881Speter{ 939251881Speter struct svnadmin_opt_state *opt_state = baton; 940251881Speter svn_repos_t *repos; 941251881Speter svn_fs_t *fs; 942251881Speter svn_revnum_t start = SVN_INVALID_REVNUM, end = SVN_INVALID_REVNUM; 943251881Speter svn_revnum_t youngest, revision; 944251881Speter apr_pool_t *subpool = svn_pool_create(pool); 945251881Speter 946251881Speter /* Expect no more arguments. */ 947251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 948251881Speter 949362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 950251881Speter fs = svn_repos_fs(repos); 951251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 952251881Speter 953251881Speter /* Find the revision numbers at which to start and end. */ 954251881Speter SVN_ERR(get_revnum(&start, &opt_state->start_revision, 955251881Speter youngest, repos, pool)); 956251881Speter SVN_ERR(get_revnum(&end, &opt_state->end_revision, 957251881Speter youngest, repos, pool)); 958251881Speter 959251881Speter /* Fill in implied revisions if necessary. */ 960251881Speter if (start == SVN_INVALID_REVNUM) 961251881Speter start = youngest; 962251881Speter if (end == SVN_INVALID_REVNUM) 963251881Speter end = start; 964251881Speter 965251881Speter if (start > end) 966251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 967251881Speter _("First revision cannot be higher than second")); 968251881Speter 969251881Speter /* Loop over the requested revision range, performing the 970251881Speter predecessor deltification on paths changed in each. */ 971251881Speter for (revision = start; revision <= end; revision++) 972251881Speter { 973251881Speter svn_pool_clear(subpool); 974251881Speter SVN_ERR(check_cancel(NULL)); 975251881Speter if (! opt_state->quiet) 976251881Speter SVN_ERR(svn_cmdline_printf(subpool, _("Deltifying revision %ld..."), 977251881Speter revision)); 978251881Speter SVN_ERR(svn_fs_deltify_revision(fs, revision, subpool)); 979251881Speter if (! opt_state->quiet) 980251881Speter SVN_ERR(svn_cmdline_printf(subpool, _("done.\n"))); 981251881Speter } 982251881Speter svn_pool_destroy(subpool); 983251881Speter 984251881Speter return SVN_NO_ERROR; 985251881Speter} 986251881Speter 987289180Speter/* Structure for errors encountered during 'svnadmin verify --keep-going'. */ 988289180Speterstruct verification_error 989289180Speter{ 990289180Speter svn_revnum_t rev; 991289180Speter svn_error_t *err; 992289180Speter}; 993251881Speter 994289180Speter/* Pool cleanup function to clear an svn_error_t *. */ 995289180Speterstatic apr_status_t 996289180Spetererr_cleanup(void *data) 997289180Speter{ 998289180Speter svn_error_t *err = data; 999289180Speter 1000289180Speter svn_error_clear(err); 1001289180Speter 1002289180Speter return APR_SUCCESS; 1003289180Speter} 1004289180Speter 1005289180Speterstruct repos_verify_callback_baton 1006289180Speter{ 1007289180Speter /* Should we continue after receiving a first verification error? */ 1008289180Speter svn_boolean_t keep_going; 1009289180Speter 1010289180Speter /* List of errors encountered during 'svnadmin verify --keep-going'. */ 1011289180Speter apr_array_header_t *error_summary; 1012289180Speter 1013289180Speter /* Pool for data collected during callback invocations. */ 1014289180Speter apr_pool_t *result_pool; 1015289180Speter}; 1016289180Speter 1017289180Speter/* Implementation of svn_repos_verify_callback_t to handle errors coming 1018289180Speter from svn_repos_verify_fs3(). */ 1019289180Speterstatic svn_error_t * 1020289180Speterrepos_verify_callback(void *baton, 1021289180Speter svn_revnum_t revision, 1022289180Speter svn_error_t *verify_err, 1023289180Speter apr_pool_t *scratch_pool) 1024289180Speter{ 1025289180Speter struct repos_verify_callback_baton *b = baton; 1026289180Speter 1027289180Speter if (revision == SVN_INVALID_REVNUM) 1028289180Speter { 1029289180Speter SVN_ERR(svn_cmdline_fputs(_("* Error verifying repository metadata.\n"), 1030289180Speter stderr, scratch_pool)); 1031289180Speter } 1032289180Speter else 1033289180Speter { 1034289180Speter SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, 1035289180Speter _("* Error verifying revision %ld.\n"), 1036289180Speter revision)); 1037289180Speter } 1038289180Speter 1039289180Speter if (b->keep_going) 1040289180Speter { 1041289180Speter struct verification_error *verr; 1042289180Speter 1043289180Speter svn_handle_error2(verify_err, stderr, FALSE, "svnadmin: "); 1044289180Speter 1045289180Speter /* Remember the error in B->ERROR_SUMMARY. */ 1046289180Speter verr = apr_palloc(b->result_pool, sizeof(*verr)); 1047289180Speter verr->rev = revision; 1048289180Speter verr->err = svn_error_dup(verify_err); 1049289180Speter apr_pool_cleanup_register(b->result_pool, verr->err, err_cleanup, 1050289180Speter apr_pool_cleanup_null); 1051289180Speter APR_ARRAY_PUSH(b->error_summary, struct verification_error *) = verr; 1052289180Speter 1053289180Speter return SVN_NO_ERROR; 1054289180Speter } 1055289180Speter else 1056289180Speter return svn_error_trace(svn_error_dup(verify_err)); 1057289180Speter} 1058289180Speter 1059251881Speter/* Implementation of svn_repos_notify_func_t to wrap the output to a 1060289180Speter response stream for svn_repos_dump_fs2(), svn_repos_verify_fs(), 1061289180Speter svn_repos_hotcopy3() and others. */ 1062251881Speterstatic void 1063251881Speterrepos_notify_handler(void *baton, 1064251881Speter const svn_repos_notify_t *notify, 1065251881Speter apr_pool_t *scratch_pool) 1066251881Speter{ 1067251881Speter svn_stream_t *feedback_stream = baton; 1068251881Speter 1069251881Speter switch (notify->action) 1070251881Speter { 1071251881Speter case svn_repos_notify_warning: 1072257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1073257936Speter "WARNING 0x%04x: %s\n", notify->warning, 1074257936Speter notify->warning_str)); 1075251881Speter return; 1076251881Speter 1077251881Speter case svn_repos_notify_dump_rev_end: 1078257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1079257936Speter _("* Dumped revision %ld.\n"), 1080257936Speter notify->revision)); 1081251881Speter return; 1082251881Speter 1083251881Speter case svn_repos_notify_verify_rev_end: 1084257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1085257936Speter _("* Verified revision %ld.\n"), 1086257936Speter notify->revision)); 1087251881Speter return; 1088251881Speter 1089251881Speter case svn_repos_notify_verify_rev_structure: 1090251881Speter if (notify->revision == SVN_INVALID_REVNUM) 1091289180Speter svn_error_clear(svn_stream_puts(feedback_stream, 1092257936Speter _("* Verifying repository metadata ...\n"))); 1093251881Speter else 1094257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1095257936Speter _("* Verifying metadata at revision %ld ...\n"), 1096257936Speter notify->revision)); 1097251881Speter return; 1098251881Speter 1099251881Speter case svn_repos_notify_pack_shard_start: 1100251881Speter { 1101251881Speter const char *shardstr = apr_psprintf(scratch_pool, 1102251881Speter "%" APR_INT64_T_FMT, 1103251881Speter notify->shard); 1104257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1105257936Speter _("Packing revisions in shard %s..."), 1106257936Speter shardstr)); 1107251881Speter } 1108251881Speter return; 1109251881Speter 1110251881Speter case svn_repos_notify_pack_shard_end: 1111257936Speter svn_error_clear(svn_stream_puts(feedback_stream, _("done.\n"))); 1112251881Speter return; 1113251881Speter 1114251881Speter case svn_repos_notify_pack_shard_start_revprop: 1115251881Speter { 1116251881Speter const char *shardstr = apr_psprintf(scratch_pool, 1117251881Speter "%" APR_INT64_T_FMT, 1118251881Speter notify->shard); 1119257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1120257936Speter _("Packing revprops in shard %s..."), 1121257936Speter shardstr)); 1122251881Speter } 1123251881Speter return; 1124251881Speter 1125251881Speter case svn_repos_notify_pack_shard_end_revprop: 1126257936Speter svn_error_clear(svn_stream_puts(feedback_stream, _("done.\n"))); 1127251881Speter return; 1128251881Speter 1129251881Speter case svn_repos_notify_load_txn_committed: 1130251881Speter if (notify->old_revision == SVN_INVALID_REVNUM) 1131251881Speter { 1132257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1133257936Speter _("\n------- Committed revision %ld >>>\n\n"), 1134257936Speter notify->new_revision)); 1135251881Speter } 1136251881Speter else 1137251881Speter { 1138257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1139257936Speter _("\n------- Committed new rev %ld" 1140257936Speter " (loaded from original rev %ld" 1141257936Speter ") >>>\n\n"), notify->new_revision, 1142257936Speter notify->old_revision)); 1143251881Speter } 1144251881Speter return; 1145251881Speter 1146251881Speter case svn_repos_notify_load_node_start: 1147251881Speter { 1148251881Speter switch (notify->node_action) 1149251881Speter { 1150251881Speter case svn_node_action_change: 1151257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1152251881Speter _(" * editing path : %s ..."), 1153257936Speter notify->path)); 1154251881Speter break; 1155251881Speter 1156251881Speter case svn_node_action_delete: 1157257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1158251881Speter _(" * deleting path : %s ..."), 1159257936Speter notify->path)); 1160251881Speter break; 1161251881Speter 1162251881Speter case svn_node_action_add: 1163257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1164251881Speter _(" * adding path : %s ..."), 1165257936Speter notify->path)); 1166251881Speter break; 1167251881Speter 1168251881Speter case svn_node_action_replace: 1169257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1170251881Speter _(" * replacing path : %s ..."), 1171257936Speter notify->path)); 1172251881Speter break; 1173251881Speter 1174251881Speter } 1175251881Speter } 1176251881Speter return; 1177251881Speter 1178251881Speter case svn_repos_notify_load_node_done: 1179289180Speter svn_error_clear(svn_stream_puts(feedback_stream, _(" done.\n"))); 1180251881Speter return; 1181251881Speter 1182251881Speter case svn_repos_notify_load_copied_node: 1183289180Speter svn_error_clear(svn_stream_puts(feedback_stream, "COPIED...")); 1184251881Speter return; 1185251881Speter 1186251881Speter case svn_repos_notify_load_txn_start: 1187257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1188257936Speter _("<<< Started new transaction, based on " 1189257936Speter "original revision %ld\n"), 1190257936Speter notify->old_revision)); 1191251881Speter return; 1192251881Speter 1193251881Speter case svn_repos_notify_load_skipped_rev: 1194257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1195257936Speter _("<<< Skipped original revision %ld\n"), 1196257936Speter notify->old_revision)); 1197251881Speter return; 1198251881Speter 1199251881Speter case svn_repos_notify_load_normalized_mergeinfo: 1200257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1201257936Speter _(" removing '\\r' from %s ..."), 1202257936Speter SVN_PROP_MERGEINFO)); 1203251881Speter return; 1204251881Speter 1205251881Speter case svn_repos_notify_mutex_acquired: 1206362181Sdim svn_cmdline__setup_cancellation_handler(); 1207251881Speter return; 1208251881Speter 1209251881Speter case svn_repos_notify_recover_start: 1210289180Speter svn_error_clear(svn_stream_puts(feedback_stream, 1211257936Speter _("Repository lock acquired.\n" 1212257936Speter "Please wait; recovering the" 1213257936Speter " repository may take some time...\n"))); 1214251881Speter return; 1215251881Speter 1216251881Speter case svn_repos_notify_upgrade_start: 1217257936Speter svn_error_clear(svn_stream_puts(feedback_stream, 1218257936Speter _("Repository lock acquired.\n" 1219257936Speter "Please wait; upgrading the" 1220257936Speter " repository may take some time...\n"))); 1221251881Speter return; 1222251881Speter 1223289180Speter case svn_repos_notify_pack_revprops: 1224289180Speter { 1225289180Speter const char *shardstr = apr_psprintf(scratch_pool, 1226289180Speter "%" APR_INT64_T_FMT, 1227289180Speter notify->shard); 1228289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1229289180Speter _("Packed revision properties in shard %s\n"), 1230289180Speter shardstr)); 1231289180Speter return; 1232289180Speter } 1233289180Speter 1234289180Speter case svn_repos_notify_cleanup_revprops: 1235289180Speter { 1236289180Speter const char *shardstr = apr_psprintf(scratch_pool, 1237289180Speter "%" APR_INT64_T_FMT, 1238289180Speter notify->shard); 1239289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1240289180Speter _("Removed non-packed revision properties" 1241289180Speter " in shard %s\n"), 1242289180Speter shardstr)); 1243289180Speter return; 1244289180Speter } 1245289180Speter 1246289180Speter case svn_repos_notify_format_bumped: 1247289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1248289180Speter _("Bumped repository format to %ld\n"), 1249289180Speter notify->revision)); 1250289180Speter return; 1251289180Speter 1252289180Speter case svn_repos_notify_hotcopy_rev_range: 1253289180Speter if (notify->start_revision == notify->end_revision) 1254289180Speter { 1255289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1256289180Speter _("* Copied revision %ld.\n"), 1257289180Speter notify->start_revision)); 1258289180Speter } 1259289180Speter else 1260289180Speter { 1261289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1262289180Speter _("* Copied revisions from %ld to %ld.\n"), 1263289180Speter notify->start_revision, notify->end_revision)); 1264289180Speter } 1265362181Sdim return; 1266289180Speter 1267362181Sdim case svn_repos_notify_pack_noop: 1268362181Sdim /* For best backward compatibility, we keep silent if there were just 1269362181Sdim no more shards to pack. */ 1270362181Sdim if (notify->shard == -1) 1271362181Sdim { 1272362181Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1273362181Sdim _("svnadmin: Warning - this repository is not sharded." 1274362181Sdim " Packing has no effect.\n"))); 1275362181Sdim } 1276362181Sdim return; 1277362181Sdim 1278362181Sdim case svn_repos_notify_load_revprop_set: 1279362181Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1280362181Sdim _("Properties set on revision %ld.\n"), 1281362181Sdim notify->new_revision)); 1282362181Sdim return; 1283362181Sdim 1284251881Speter default: 1285251881Speter return; 1286251881Speter } 1287251881Speter} 1288251881Speter 1289251881Speter 1290251881Speter/* Baton for recode_write(). */ 1291251881Speterstruct recode_write_baton 1292251881Speter{ 1293251881Speter apr_pool_t *pool; 1294251881Speter FILE *out; 1295251881Speter}; 1296251881Speter 1297251881Speter/* This implements the 'svn_write_fn_t' interface. 1298251881Speter 1299251881Speter Write DATA to ((struct recode_write_baton *) BATON)->out, in the 1300251881Speter console encoding, using svn_cmdline_fprintf(). DATA is a 1301251881Speter UTF8-encoded C string, therefore ignore LEN. 1302251881Speter 1303251881Speter ### This recoding mechanism might want to be abstracted into 1304251881Speter ### svn_io.h or svn_cmdline.h, if it proves useful elsewhere. */ 1305251881Speterstatic svn_error_t *recode_write(void *baton, 1306251881Speter const char *data, 1307251881Speter apr_size_t *len) 1308251881Speter{ 1309251881Speter struct recode_write_baton *rwb = baton; 1310251881Speter svn_pool_clear(rwb->pool); 1311251881Speter return svn_cmdline_fputs(data, rwb->out, rwb->pool); 1312251881Speter} 1313251881Speter 1314251881Speter/* Create a stream, to write to STD_STREAM, that uses recode_write() 1315251881Speter to perform UTF-8 to console encoding translation. */ 1316251881Speterstatic svn_stream_t * 1317251881Speterrecode_stream_create(FILE *std_stream, apr_pool_t *pool) 1318251881Speter{ 1319251881Speter struct recode_write_baton *std_stream_rwb = 1320251881Speter apr_palloc(pool, sizeof(struct recode_write_baton)); 1321251881Speter 1322251881Speter svn_stream_t *rw_stream = svn_stream_create(std_stream_rwb, pool); 1323251881Speter std_stream_rwb->pool = svn_pool_create(pool); 1324251881Speter std_stream_rwb->out = std_stream; 1325251881Speter svn_stream_set_write(rw_stream, recode_write); 1326251881Speter return rw_stream; 1327251881Speter} 1328251881Speter 1329362181Sdim/* Read the min / max revision from the OPT_STATE, verify them against REPOS 1330362181Sdim and return them in *LOWER and *UPPER, respectively. Use SCRATCH_POOL 1331362181Sdim for temporary allocations. */ 1332251881Speterstatic svn_error_t * 1333362181Sdimget_dump_range(svn_revnum_t *lower, 1334362181Sdim svn_revnum_t *upper, 1335362181Sdim svn_repos_t *repos, 1336362181Sdim struct svnadmin_opt_state *opt_state, 1337362181Sdim apr_pool_t *scratch_pool) 1338251881Speter{ 1339251881Speter svn_fs_t *fs; 1340251881Speter svn_revnum_t youngest; 1341251881Speter 1342362181Sdim *lower = SVN_INVALID_REVNUM; 1343362181Sdim *upper = SVN_INVALID_REVNUM; 1344251881Speter 1345251881Speter fs = svn_repos_fs(repos); 1346362181Sdim SVN_ERR(svn_fs_youngest_rev(&youngest, fs, scratch_pool)); 1347251881Speter 1348251881Speter /* Find the revision numbers at which to start and end. */ 1349362181Sdim SVN_ERR(get_revnum(lower, &opt_state->start_revision, 1350362181Sdim youngest, repos, scratch_pool)); 1351362181Sdim SVN_ERR(get_revnum(upper, &opt_state->end_revision, 1352362181Sdim youngest, repos, scratch_pool)); 1353251881Speter 1354251881Speter /* Fill in implied revisions if necessary. */ 1355362181Sdim if (*lower == SVN_INVALID_REVNUM) 1356251881Speter { 1357362181Sdim *lower = 0; 1358362181Sdim *upper = youngest; 1359251881Speter } 1360362181Sdim else if (*upper == SVN_INVALID_REVNUM) 1361251881Speter { 1362362181Sdim *upper = *lower; 1363251881Speter } 1364251881Speter 1365362181Sdim if (*lower > *upper) 1366251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1367251881Speter _("First revision cannot be higher than second")); 1368251881Speter 1369362181Sdim return SVN_NO_ERROR; 1370362181Sdim} 1371251881Speter 1372362181Sdim/* Compare the node-path PATH with the (const char *) prefixes in PFXLIST. 1373362181Sdim * Return TRUE if any prefix is a prefix of PATH (matching whole path 1374362181Sdim * components); FALSE otherwise. 1375362181Sdim * PATH starts with a '/', as do the (const char *) paths in PREFIXES. */ 1376362181Sdim/* This function is a duplicate of svndumpfilter.c:ary_prefix_match(). */ 1377362181Sdimstatic svn_boolean_t 1378362181Sdimary_prefix_match(const apr_array_header_t *pfxlist, const char *path) 1379362181Sdim{ 1380362181Sdim int i; 1381362181Sdim size_t path_len = strlen(path); 1382362181Sdim 1383362181Sdim for (i = 0; i < pfxlist->nelts; i++) 1384362181Sdim { 1385362181Sdim const char *pfx = APR_ARRAY_IDX(pfxlist, i, const char *); 1386362181Sdim size_t pfx_len = strlen(pfx); 1387362181Sdim 1388362181Sdim if (path_len < pfx_len) 1389362181Sdim continue; 1390362181Sdim if (strncmp(path, pfx, pfx_len) == 0 1391362181Sdim && (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] == '/')) 1392362181Sdim return TRUE; 1393362181Sdim } 1394362181Sdim 1395362181Sdim return FALSE; 1396362181Sdim} 1397362181Sdim 1398362181Sdim/* Baton for dump_filter_func(). */ 1399362181Sdimstruct dump_filter_baton_t 1400362181Sdim{ 1401362181Sdim apr_array_header_t *prefixes; 1402362181Sdim svn_boolean_t glob; 1403362181Sdim svn_boolean_t do_exclude; 1404362181Sdim}; 1405362181Sdim 1406362181Sdim/* Implements svn_repos_dump_filter_func_t. */ 1407362181Sdimstatic svn_error_t * 1408362181Sdimdump_filter_func(svn_boolean_t *include, 1409362181Sdim svn_fs_root_t *root, 1410362181Sdim const char *path, 1411362181Sdim void *baton, 1412362181Sdim apr_pool_t *scratch_pool) 1413362181Sdim{ 1414362181Sdim struct dump_filter_baton_t *b = baton; 1415362181Sdim const svn_boolean_t matches = 1416362181Sdim (b->glob 1417362181Sdim ? svn_cstring_match_glob_list(path, b->prefixes) 1418362181Sdim : ary_prefix_match(b->prefixes, path)); 1419362181Sdim 1420362181Sdim *include = b->do_exclude ? !matches : matches; 1421362181Sdim return SVN_NO_ERROR; 1422362181Sdim} 1423362181Sdim 1424362181Sdim/* This implements `svn_opt_subcommand_t'. */ 1425362181Sdimstatic svn_error_t * 1426362181Sdimsubcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1427362181Sdim{ 1428362181Sdim struct svnadmin_opt_state *opt_state = baton; 1429362181Sdim svn_repos_t *repos; 1430362181Sdim svn_stream_t *out_stream; 1431362181Sdim svn_revnum_t lower, upper; 1432362181Sdim svn_stream_t *feedback_stream = NULL; 1433362181Sdim struct dump_filter_baton_t filter_baton = {0}; 1434362181Sdim 1435362181Sdim /* Expect no more arguments. */ 1436362181Sdim SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1437362181Sdim 1438362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 1439362181Sdim SVN_ERR(get_dump_range(&lower, &upper, repos, opt_state, pool)); 1440362181Sdim 1441362181Sdim /* Open the file or STDOUT, depending on whether -F was specified. */ 1442362181Sdim if (opt_state->file) 1443362181Sdim { 1444362181Sdim apr_file_t *file; 1445362181Sdim 1446362181Sdim /* Overwrite existing files, same as with > redirection. */ 1447362181Sdim SVN_ERR(svn_io_file_open(&file, opt_state->file, 1448362181Sdim APR_WRITE | APR_CREATE | APR_TRUNCATE 1449362181Sdim | APR_BUFFERED, APR_OS_DEFAULT, pool)); 1450362181Sdim out_stream = svn_stream_from_aprfile2(file, FALSE, pool); 1451362181Sdim } 1452362181Sdim else 1453362181Sdim SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); 1454362181Sdim 1455251881Speter /* Progress feedback goes to STDERR, unless they asked to suppress it. */ 1456251881Speter if (! opt_state->quiet) 1457289180Speter feedback_stream = recode_stream_create(stderr, pool); 1458251881Speter 1459362181Sdim /* Initialize the filter baton. */ 1460362181Sdim filter_baton.glob = opt_state->glob; 1461362181Sdim 1462362181Sdim if (opt_state->exclude && !opt_state->include) 1463362181Sdim { 1464362181Sdim filter_baton.prefixes = opt_state->exclude; 1465362181Sdim filter_baton.do_exclude = TRUE; 1466362181Sdim } 1467362181Sdim else if (opt_state->include && !opt_state->exclude) 1468362181Sdim { 1469362181Sdim filter_baton.prefixes = opt_state->include; 1470362181Sdim filter_baton.do_exclude = FALSE; 1471362181Sdim } 1472362181Sdim else if (opt_state->include && opt_state->exclude) 1473362181Sdim { 1474362181Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1475362181Sdim _("'--exclude' and '--include' options " 1476362181Sdim "cannot be used simultaneously")); 1477362181Sdim } 1478362181Sdim 1479362181Sdim SVN_ERR(svn_repos_dump_fs4(repos, out_stream, lower, upper, 1480251881Speter opt_state->incremental, opt_state->use_deltas, 1481362181Sdim TRUE, TRUE, 1482251881Speter !opt_state->quiet ? repos_notify_handler : NULL, 1483362181Sdim feedback_stream, 1484362181Sdim filter_baton.prefixes ? dump_filter_func : NULL, 1485362181Sdim &filter_baton, 1486362181Sdim check_cancel, NULL, pool)); 1487251881Speter 1488251881Speter return SVN_NO_ERROR; 1489251881Speter} 1490251881Speter 1491362181Sdim/* This implements `svn_opt_subcommand_t'. */ 1492362181Sdimstatic svn_error_t * 1493362181Sdimsubcommand_dump_revprops(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1494362181Sdim{ 1495362181Sdim struct svnadmin_opt_state *opt_state = baton; 1496362181Sdim svn_repos_t *repos; 1497362181Sdim svn_stream_t *out_stream; 1498362181Sdim svn_revnum_t lower, upper; 1499362181Sdim svn_stream_t *feedback_stream = NULL; 1500362181Sdim 1501362181Sdim /* Expect no more arguments. */ 1502362181Sdim SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1503362181Sdim 1504362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 1505362181Sdim SVN_ERR(get_dump_range(&lower, &upper, repos, opt_state, pool)); 1506362181Sdim 1507362181Sdim /* Open the file or STDOUT, depending on whether -F was specified. */ 1508362181Sdim if (opt_state->file) 1509362181Sdim { 1510362181Sdim apr_file_t *file; 1511362181Sdim 1512362181Sdim /* Overwrite existing files, same as with > redirection. */ 1513362181Sdim SVN_ERR(svn_io_file_open(&file, opt_state->file, 1514362181Sdim APR_WRITE | APR_CREATE | APR_TRUNCATE 1515362181Sdim | APR_BUFFERED, APR_OS_DEFAULT, pool)); 1516362181Sdim out_stream = svn_stream_from_aprfile2(file, FALSE, pool); 1517362181Sdim } 1518362181Sdim else 1519362181Sdim SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); 1520362181Sdim 1521362181Sdim /* Progress feedback goes to STDERR, unless they asked to suppress it. */ 1522362181Sdim if (! opt_state->quiet) 1523362181Sdim feedback_stream = recode_stream_create(stderr, pool); 1524362181Sdim 1525362181Sdim SVN_ERR(svn_repos_dump_fs4(repos, out_stream, lower, upper, 1526362181Sdim FALSE, FALSE, TRUE, FALSE, 1527362181Sdim !opt_state->quiet ? repos_notify_handler : NULL, 1528362181Sdim feedback_stream, NULL, NULL, 1529362181Sdim check_cancel, NULL, pool)); 1530362181Sdim 1531362181Sdim return SVN_NO_ERROR; 1532362181Sdim} 1533362181Sdim 1534251881Speterstruct freeze_baton_t { 1535251881Speter const char *command; 1536251881Speter const char **args; 1537251881Speter int status; 1538251881Speter}; 1539251881Speter 1540251881Speter/* Implements svn_repos_freeze_func_t */ 1541251881Speterstatic svn_error_t * 1542251881Speterfreeze_body(void *baton, 1543251881Speter apr_pool_t *pool) 1544251881Speter{ 1545251881Speter struct freeze_baton_t *b = baton; 1546251881Speter apr_status_t apr_err; 1547251881Speter apr_file_t *infile, *outfile, *errfile; 1548251881Speter 1549251881Speter apr_err = apr_file_open_stdin(&infile, pool); 1550251881Speter if (apr_err) 1551251881Speter return svn_error_wrap_apr(apr_err, "Can't open stdin"); 1552251881Speter apr_err = apr_file_open_stdout(&outfile, pool); 1553251881Speter if (apr_err) 1554251881Speter return svn_error_wrap_apr(apr_err, "Can't open stdout"); 1555251881Speter apr_err = apr_file_open_stderr(&errfile, pool); 1556251881Speter if (apr_err) 1557251881Speter return svn_error_wrap_apr(apr_err, "Can't open stderr"); 1558251881Speter 1559251881Speter SVN_ERR(svn_io_run_cmd(NULL, b->command, b->args, &b->status, 1560251881Speter NULL, TRUE, 1561251881Speter infile, outfile, errfile, pool)); 1562251881Speter 1563251881Speter return SVN_NO_ERROR; 1564251881Speter} 1565251881Speter 1566251881Speterstatic svn_error_t * 1567251881Spetersubcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1568251881Speter{ 1569251881Speter struct svnadmin_opt_state *opt_state = baton; 1570251881Speter apr_array_header_t *paths; 1571251881Speter apr_array_header_t *args; 1572251881Speter int i; 1573251881Speter struct freeze_baton_t b; 1574251881Speter 1575362181Sdim SVN_ERR(parse_args(&args, os, -1, -1, pool)); 1576251881Speter 1577251881Speter if (!args->nelts) 1578251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 1579251881Speter _("No program provided")); 1580251881Speter 1581362181Sdim if (!opt_state->file) 1582251881Speter { 1583251881Speter /* One repository on the command line. */ 1584251881Speter paths = apr_array_make(pool, 1, sizeof(const char *)); 1585251881Speter APR_ARRAY_PUSH(paths, const char *) = opt_state->repository_path; 1586251881Speter } 1587251881Speter else 1588251881Speter { 1589362181Sdim svn_stringbuf_t *buf; 1590289180Speter const char *utf8; 1591362181Sdim /* Read repository paths from the -F file. */ 1592362181Sdim SVN_ERR(svn_stringbuf_from_file2(&buf, opt_state->file, pool)); 1593362181Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8, buf->data, pool)); 1594289180Speter paths = svn_cstring_split(utf8, "\r\n", FALSE, pool); 1595251881Speter } 1596251881Speter 1597251881Speter b.command = APR_ARRAY_IDX(args, 0, const char *); 1598286506Speter b.args = apr_palloc(pool, sizeof(char *) * (args->nelts + 1)); 1599251881Speter for (i = 0; i < args->nelts; ++i) 1600251881Speter b.args[i] = APR_ARRAY_IDX(args, i, const char *); 1601251881Speter b.args[args->nelts] = NULL; 1602251881Speter 1603251881Speter SVN_ERR(svn_repos_freeze(paths, freeze_body, &b, pool)); 1604251881Speter 1605251881Speter /* Make any non-zero status visible to the user. */ 1606251881Speter if (b.status) 1607251881Speter exit(b.status); 1608251881Speter 1609251881Speter return SVN_NO_ERROR; 1610251881Speter} 1611251881Speter 1612251881Speter 1613251881Speter/* This implements `svn_opt_subcommand_t'. */ 1614251881Speterstatic svn_error_t * 1615251881Spetersubcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1616251881Speter{ 1617251881Speter struct svnadmin_opt_state *opt_state = baton; 1618251881Speter const char *header = 1619251881Speter _("general usage: svnadmin SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n" 1620289180Speter "Subversion repository administration tool.\n" 1621251881Speter "Type 'svnadmin help <subcommand>' for help on a specific subcommand.\n" 1622251881Speter "Type 'svnadmin --version' to see the program version and FS modules.\n" 1623251881Speter "\n" 1624251881Speter "Available subcommands:\n"); 1625251881Speter 1626251881Speter const char *fs_desc_start 1627251881Speter = _("The following repository back-end (FS) modules are available:\n\n"); 1628251881Speter 1629251881Speter svn_stringbuf_t *version_footer; 1630251881Speter 1631251881Speter version_footer = svn_stringbuf_create(fs_desc_start, pool); 1632251881Speter SVN_ERR(svn_fs_print_modules(version_footer, pool)); 1633251881Speter 1634362181Sdim SVN_ERR(svn_opt_print_help5(os, "svnadmin", 1635251881Speter opt_state ? opt_state->version : FALSE, 1636251881Speter opt_state ? opt_state->quiet : FALSE, 1637251881Speter /*###opt_state ? opt_state->verbose :*/ FALSE, 1638251881Speter version_footer->data, 1639251881Speter header, cmd_table, options_table, NULL, NULL, 1640251881Speter pool)); 1641251881Speter 1642251881Speter return SVN_NO_ERROR; 1643251881Speter} 1644251881Speter 1645251881Speter 1646251881Speter/* Set *REVNUM to the revision number of a numeric REV, or to 1647251881Speter SVN_INVALID_REVNUM if REV is unspecified. */ 1648251881Speterstatic svn_error_t * 1649251881Speteroptrev_to_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *opt_rev) 1650251881Speter{ 1651251881Speter if (opt_rev->kind == svn_opt_revision_number) 1652251881Speter { 1653251881Speter *revnum = opt_rev->value.number; 1654251881Speter if (! SVN_IS_VALID_REVNUM(*revnum)) 1655251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1656251881Speter _("Invalid revision number (%ld) specified"), 1657251881Speter *revnum); 1658251881Speter } 1659251881Speter else if (opt_rev->kind == svn_opt_revision_unspecified) 1660251881Speter { 1661251881Speter *revnum = SVN_INVALID_REVNUM; 1662251881Speter } 1663251881Speter else 1664251881Speter { 1665251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1666251881Speter _("Non-numeric revision specified")); 1667251881Speter } 1668251881Speter return SVN_NO_ERROR; 1669251881Speter} 1670251881Speter 1671362181Sdim/* Read the min / max revision from the OPT_STATE, verify them and return 1672362181Sdim them in *LOWER and *UPPER, respectively. */ 1673251881Speterstatic svn_error_t * 1674362181Sdimget_load_range(svn_revnum_t *lower, 1675362181Sdim svn_revnum_t *upper, 1676362181Sdim struct svnadmin_opt_state *opt_state) 1677251881Speter{ 1678251881Speter /* Find the revision numbers at which to start and end. We only 1679251881Speter support a limited set of revision kinds: number and unspecified. */ 1680362181Sdim SVN_ERR(optrev_to_revnum(lower, &opt_state->start_revision)); 1681362181Sdim SVN_ERR(optrev_to_revnum(upper, &opt_state->end_revision)); 1682251881Speter 1683251881Speter /* Fill in implied revisions if necessary. */ 1684362181Sdim if ((*upper == SVN_INVALID_REVNUM) && (*lower != SVN_INVALID_REVNUM)) 1685251881Speter { 1686362181Sdim *upper = *lower; 1687251881Speter } 1688362181Sdim else if ((*upper != SVN_INVALID_REVNUM) && (*lower == SVN_INVALID_REVNUM)) 1689251881Speter { 1690362181Sdim *lower = *upper; 1691251881Speter } 1692251881Speter 1693251881Speter /* Ensure correct range ordering. */ 1694362181Sdim if (*lower > *upper) 1695251881Speter { 1696251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1697251881Speter _("First revision cannot be higher than second")); 1698251881Speter } 1699251881Speter 1700362181Sdim return SVN_NO_ERROR; 1701362181Sdim} 1702251881Speter 1703251881Speter 1704362181Sdim/* This implements `svn_opt_subcommand_t'. */ 1705362181Sdimstatic svn_error_t * 1706362181Sdimsubcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1707362181Sdim{ 1708362181Sdim svn_error_t *err; 1709362181Sdim struct svnadmin_opt_state *opt_state = baton; 1710362181Sdim svn_repos_t *repos; 1711362181Sdim svn_revnum_t lower, upper; 1712362181Sdim svn_stream_t *in_stream; 1713362181Sdim svn_stream_t *feedback_stream = NULL; 1714362181Sdim 1715362181Sdim /* Expect no more arguments. */ 1716362181Sdim SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1717362181Sdim 1718362181Sdim /* Find the revision numbers at which to start and end. We only 1719362181Sdim support a limited set of revision kinds: number and unspecified. */ 1720362181Sdim SVN_ERR(get_load_range(&lower, &upper, opt_state)); 1721362181Sdim 1722362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 1723362181Sdim 1724362181Sdim /* Open the file or STDIN, depending on whether -F was specified. */ 1725362181Sdim if (opt_state->file) 1726362181Sdim SVN_ERR(svn_stream_open_readonly(&in_stream, opt_state->file, 1727362181Sdim pool, pool)); 1728362181Sdim else 1729362181Sdim SVN_ERR(svn_stream_for_stdin2(&in_stream, TRUE, pool)); 1730362181Sdim 1731251881Speter /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 1732251881Speter if (! opt_state->quiet) 1733289180Speter feedback_stream = recode_stream_create(stdout, pool); 1734251881Speter 1735362181Sdim err = svn_repos_load_fs6(repos, in_stream, lower, upper, 1736251881Speter opt_state->uuid_action, opt_state->parent_dir, 1737251881Speter opt_state->use_pre_commit_hook, 1738251881Speter opt_state->use_post_commit_hook, 1739251881Speter !opt_state->bypass_prop_validation, 1740289180Speter opt_state->ignore_dates, 1741362181Sdim opt_state->normalize_props, 1742251881Speter opt_state->quiet ? NULL : repos_notify_handler, 1743289180Speter feedback_stream, check_cancel, NULL, pool); 1744362181Sdim 1745362181Sdim if (svn_error_find_cause(err, SVN_ERR_BAD_PROPERTY_VALUE_EOL)) 1746362181Sdim { 1747362181Sdim return svn_error_quick_wrap(err, 1748362181Sdim _("A property with invalid line ending " 1749362181Sdim "found in dumpstream; consider using " 1750362181Sdim "--normalize-props while loading.")); 1751362181Sdim } 1752362181Sdim else if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE) 1753362181Sdim { 1754362181Sdim return svn_error_quick_wrap(err, 1755362181Sdim _("Invalid property value found in " 1756362181Sdim "dumpstream; consider repairing the " 1757362181Sdim "source or using --bypass-prop-validation " 1758362181Sdim "while loading.")); 1759362181Sdim } 1760362181Sdim 1761251881Speter return err; 1762251881Speter} 1763251881Speter 1764362181Sdimstatic svn_error_t * 1765362181Sdimsubcommand_load_revprops(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1766362181Sdim{ 1767362181Sdim svn_error_t *err; 1768362181Sdim struct svnadmin_opt_state *opt_state = baton; 1769362181Sdim svn_repos_t *repos; 1770362181Sdim svn_revnum_t lower, upper; 1771362181Sdim svn_stream_t *in_stream; 1772251881Speter 1773362181Sdim svn_stream_t *feedback_stream = NULL; 1774362181Sdim 1775362181Sdim /* Expect no more arguments. */ 1776362181Sdim SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1777362181Sdim 1778362181Sdim /* Find the revision numbers at which to start and end. We only 1779362181Sdim support a limited set of revision kinds: number and unspecified. */ 1780362181Sdim SVN_ERR(get_load_range(&lower, &upper, opt_state)); 1781362181Sdim 1782362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 1783362181Sdim 1784362181Sdim /* Open the file or STDIN, depending on whether -F was specified. */ 1785362181Sdim if (opt_state->file) 1786362181Sdim SVN_ERR(svn_stream_open_readonly(&in_stream, opt_state->file, 1787362181Sdim pool, pool)); 1788362181Sdim else 1789362181Sdim SVN_ERR(svn_stream_for_stdin2(&in_stream, TRUE, pool)); 1790362181Sdim 1791362181Sdim /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 1792362181Sdim if (! opt_state->quiet) 1793362181Sdim feedback_stream = recode_stream_create(stdout, pool); 1794362181Sdim 1795362181Sdim err = svn_repos_load_fs_revprops(repos, in_stream, lower, upper, 1796362181Sdim !opt_state->bypass_prop_validation, 1797362181Sdim opt_state->ignore_dates, 1798362181Sdim opt_state->normalize_props, 1799362181Sdim opt_state->quiet ? NULL 1800362181Sdim : repos_notify_handler, 1801362181Sdim feedback_stream, check_cancel, NULL, pool); 1802362181Sdim 1803362181Sdim if (svn_error_find_cause(err, SVN_ERR_BAD_PROPERTY_VALUE_EOL)) 1804362181Sdim { 1805362181Sdim return svn_error_quick_wrap(err, 1806362181Sdim _("A property with invalid line ending " 1807362181Sdim "found in dumpstream; consider using " 1808362181Sdim "--normalize-props while loading.")); 1809362181Sdim } 1810362181Sdim else if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE) 1811362181Sdim { 1812362181Sdim return svn_error_quick_wrap(err, 1813362181Sdim _("Invalid property value found in " 1814362181Sdim "dumpstream; consider repairing the " 1815362181Sdim "source or using --bypass-prop-validation " 1816362181Sdim "while loading.")); 1817362181Sdim } 1818362181Sdim 1819362181Sdim return err; 1820362181Sdim} 1821362181Sdim 1822251881Speter/* This implements `svn_opt_subcommand_t'. */ 1823251881Speterstatic svn_error_t * 1824251881Spetersubcommand_lstxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1825251881Speter{ 1826251881Speter struct svnadmin_opt_state *opt_state = baton; 1827251881Speter svn_repos_t *repos; 1828251881Speter svn_fs_t *fs; 1829251881Speter apr_array_header_t *txns; 1830362181Sdim apr_pool_t *iterpool; 1831362181Sdim svn_revnum_t youngest, limit; 1832251881Speter int i; 1833251881Speter 1834251881Speter /* Expect no more arguments. */ 1835251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1836362181Sdim if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 1837362181Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1838362181Sdim _("Revision range is not allowed")); 1839251881Speter 1840362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 1841251881Speter fs = svn_repos_fs(repos); 1842251881Speter SVN_ERR(svn_fs_list_transactions(&txns, fs, pool)); 1843251881Speter 1844362181Sdim SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 1845362181Sdim SVN_ERR(get_revnum(&limit, &opt_state->start_revision, youngest, repos, 1846362181Sdim pool)); 1847362181Sdim 1848362181Sdim iterpool = svn_pool_create(pool); 1849251881Speter for (i = 0; i < txns->nelts; i++) 1850251881Speter { 1851362181Sdim const char *name = APR_ARRAY_IDX(txns, i, const char *); 1852362181Sdim svn_boolean_t show = TRUE; 1853362181Sdim 1854362181Sdim svn_pool_clear(iterpool); 1855362181Sdim if (limit != SVN_INVALID_REVNUM) 1856362181Sdim { 1857362181Sdim svn_fs_txn_t *txn; 1858362181Sdim svn_revnum_t base; 1859362181Sdim 1860362181Sdim SVN_ERR(svn_fs_open_txn(&txn, fs, name, iterpool)); 1861362181Sdim base = svn_fs_txn_base_revision(txn); 1862362181Sdim 1863362181Sdim if (base > limit) 1864362181Sdim show = FALSE; 1865362181Sdim } 1866362181Sdim if (show) 1867362181Sdim SVN_ERR(svn_cmdline_printf(pool, "%s\n", name)); 1868251881Speter } 1869362181Sdim svn_pool_destroy(iterpool); 1870251881Speter 1871251881Speter return SVN_NO_ERROR; 1872251881Speter} 1873251881Speter 1874251881Speter 1875251881Speter/* This implements `svn_opt_subcommand_t'. */ 1876251881Speterstatic svn_error_t * 1877251881Spetersubcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1878251881Speter{ 1879251881Speter svn_revnum_t youngest_rev; 1880251881Speter svn_repos_t *repos; 1881251881Speter svn_error_t *err; 1882251881Speter struct svnadmin_opt_state *opt_state = baton; 1883289180Speter svn_stream_t *feedback_stream = NULL; 1884251881Speter 1885251881Speter /* Expect no more arguments. */ 1886251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1887251881Speter 1888289180Speter SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool)); 1889251881Speter 1890251881Speter /* Restore default signal handlers until after we have acquired the 1891251881Speter * exclusive lock so that the user interrupt before we actually 1892251881Speter * touch the repository. */ 1893362181Sdim svn_cmdline__disable_cancellation_handler(); 1894251881Speter 1895251881Speter err = svn_repos_recover4(opt_state->repository_path, TRUE, 1896289180Speter repos_notify_handler, feedback_stream, 1897251881Speter check_cancel, NULL, pool); 1898251881Speter if (err) 1899251881Speter { 1900251881Speter if (! APR_STATUS_IS_EAGAIN(err->apr_err)) 1901251881Speter return err; 1902251881Speter svn_error_clear(err); 1903251881Speter if (! opt_state->wait) 1904251881Speter return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL, 1905251881Speter _("Failed to get exclusive repository " 1906251881Speter "access; perhaps another process\n" 1907251881Speter "such as httpd, svnserve or svn " 1908251881Speter "has it open?")); 1909251881Speter SVN_ERR(svn_cmdline_printf(pool, 1910251881Speter _("Waiting on repository lock; perhaps" 1911251881Speter " another process has it open?\n"))); 1912251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 1913251881Speter SVN_ERR(svn_repos_recover4(opt_state->repository_path, FALSE, 1914289180Speter repos_notify_handler, feedback_stream, 1915251881Speter check_cancel, NULL, pool)); 1916251881Speter } 1917251881Speter 1918251881Speter SVN_ERR(svn_cmdline_printf(pool, _("\nRecovery completed.\n"))); 1919251881Speter 1920251881Speter /* Since db transactions may have been replayed, it's nice to tell 1921251881Speter people what the latest revision is. It also proves that the 1922251881Speter recovery actually worked. */ 1923362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 1924251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos), pool)); 1925251881Speter SVN_ERR(svn_cmdline_printf(pool, _("The latest repos revision is %ld.\n"), 1926251881Speter youngest_rev)); 1927251881Speter 1928251881Speter return SVN_NO_ERROR; 1929251881Speter} 1930251881Speter 1931251881Speter 1932251881Speter/* This implements `svn_opt_subcommand_t'. */ 1933251881Speterstatic svn_error_t * 1934251881Speterlist_dblogs(apr_getopt_t *os, void *baton, svn_boolean_t only_unused, 1935251881Speter apr_pool_t *pool) 1936251881Speter{ 1937251881Speter struct svnadmin_opt_state *opt_state = baton; 1938251881Speter apr_array_header_t *logfiles; 1939251881Speter int i; 1940251881Speter 1941251881Speter /* Expect no more arguments. */ 1942251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1943251881Speter 1944251881Speter SVN_ERR(svn_repos_db_logfiles(&logfiles, 1945251881Speter opt_state->repository_path, 1946251881Speter only_unused, 1947251881Speter pool)); 1948251881Speter 1949251881Speter /* Loop, printing log files. We append the log paths to the 1950251881Speter repository path, making sure to return everything to the native 1951251881Speter style before printing. */ 1952251881Speter for (i = 0; i < logfiles->nelts; i++) 1953251881Speter { 1954362181Sdim const char *log_path; 1955362181Sdim log_path = svn_dirent_join(opt_state->repository_path, 1956251881Speter APR_ARRAY_IDX(logfiles, i, const char *), 1957251881Speter pool); 1958362181Sdim log_path = svn_dirent_local_style(log_path, pool); 1959362181Sdim SVN_ERR(svn_cmdline_printf(pool, "%s\n", log_path)); 1960251881Speter } 1961251881Speter 1962251881Speter return SVN_NO_ERROR; 1963251881Speter} 1964251881Speter 1965251881Speter 1966251881Speter/* This implements `svn_opt_subcommand_t'. */ 1967251881Speterstatic svn_error_t * 1968251881Spetersubcommand_list_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1969251881Speter{ 1970251881Speter SVN_ERR(list_dblogs(os, baton, FALSE, pool)); 1971251881Speter return SVN_NO_ERROR; 1972251881Speter} 1973251881Speter 1974251881Speter 1975251881Speter/* This implements `svn_opt_subcommand_t'. */ 1976251881Speterstatic svn_error_t * 1977251881Spetersubcommand_list_unused_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1978251881Speter{ 1979251881Speter /* Expect no more arguments. */ 1980251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1981251881Speter 1982251881Speter SVN_ERR(list_dblogs(os, baton, TRUE, pool)); 1983251881Speter return SVN_NO_ERROR; 1984251881Speter} 1985251881Speter 1986251881Speter 1987251881Speter/* This implements `svn_opt_subcommand_t'. */ 1988251881Speterstatic svn_error_t * 1989251881Spetersubcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1990251881Speter{ 1991251881Speter struct svnadmin_opt_state *opt_state = baton; 1992251881Speter svn_repos_t *repos; 1993251881Speter svn_fs_t *fs; 1994251881Speter svn_fs_txn_t *txn; 1995251881Speter apr_array_header_t *args; 1996251881Speter int i; 1997251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1998251881Speter 1999362181Sdim SVN_ERR(parse_args(&args, os, -1, -1, pool)); 2000251881Speter 2001362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2002251881Speter fs = svn_repos_fs(repos); 2003251881Speter 2004251881Speter /* All the rest of the arguments are transaction names. */ 2005251881Speter for (i = 0; i < args->nelts; i++) 2006251881Speter { 2007251881Speter const char *txn_name = APR_ARRAY_IDX(args, i, const char *); 2008251881Speter svn_error_t *err; 2009251881Speter 2010251881Speter svn_pool_clear(subpool); 2011251881Speter 2012251881Speter /* Try to open the txn. If that succeeds, try to abort it. */ 2013362181Sdim err = svn_fs_open_txn(&txn, fs, txn_name, subpool); 2014251881Speter if (! err) 2015251881Speter err = svn_fs_abort_txn(txn, subpool); 2016251881Speter 2017251881Speter /* If either the open or the abort of the txn fails because that 2018251881Speter transaction is dead, just try to purge the thing. Else, 2019251881Speter there was either an error worth reporting, or not error at 2020251881Speter all. */ 2021251881Speter if (err && (err->apr_err == SVN_ERR_FS_TRANSACTION_DEAD)) 2022251881Speter { 2023251881Speter svn_error_clear(err); 2024362181Sdim err = svn_fs_purge_txn(fs, txn_name, subpool); 2025251881Speter } 2026251881Speter 2027251881Speter /* If we had a real from the txn open, abort, or purge, we clear 2028251881Speter that error and just report to the user that we had an issue 2029251881Speter with this particular txn. */ 2030251881Speter if (err) 2031251881Speter { 2032251881Speter svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: "); 2033251881Speter svn_error_clear(err); 2034251881Speter } 2035251881Speter else if (! opt_state->quiet) 2036251881Speter { 2037251881Speter SVN_ERR(svn_cmdline_printf(subpool, _("Transaction '%s' removed.\n"), 2038251881Speter txn_name)); 2039251881Speter } 2040251881Speter } 2041251881Speter 2042251881Speter svn_pool_destroy(subpool); 2043251881Speter 2044251881Speter return SVN_NO_ERROR; 2045251881Speter} 2046251881Speter 2047251881Speter 2048251881Speter/* A helper for the 'setrevprop' and 'setlog' commands. Expects 2049289180Speter OPT_STATE->txn_id, OPT_STATE->use_pre_revprop_change_hook and 2050289180Speter OPT_STATE->use_post_revprop_change_hook to be set appropriately. 2051289180Speter If FILENAME is NULL, delete property PROP_NAME. */ 2052251881Speterstatic svn_error_t * 2053251881Speterset_revprop(const char *prop_name, const char *filename, 2054251881Speter struct svnadmin_opt_state *opt_state, apr_pool_t *pool) 2055251881Speter{ 2056251881Speter svn_repos_t *repos; 2057289180Speter svn_string_t *prop_value; 2058251881Speter 2059289180Speter if (filename) 2060289180Speter { 2061289180Speter svn_stringbuf_t *file_contents; 2062251881Speter 2063289180Speter SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool)); 2064251881Speter 2065289180Speter prop_value = svn_string_create_empty(pool); 2066289180Speter prop_value->data = file_contents->data; 2067289180Speter prop_value->len = file_contents->len; 2068251881Speter 2069289180Speter SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value, 2070289180Speter NULL, FALSE, pool, pool)); 2071289180Speter } 2072289180Speter else 2073289180Speter { 2074289180Speter prop_value = NULL; 2075289180Speter } 2076289180Speter 2077251881Speter /* Open the filesystem */ 2078362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2079251881Speter 2080289180Speter if (opt_state->txn_id) 2081289180Speter { 2082289180Speter svn_fs_t *fs = svn_repos_fs(repos); 2083289180Speter svn_fs_txn_t *txn; 2084289180Speter 2085289180Speter SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool)); 2086289180Speter SVN_ERR(svn_fs_change_txn_prop(txn, prop_name, prop_value, pool)); 2087289180Speter } 2088289180Speter else 2089289180Speter SVN_ERR(svn_repos_fs_change_rev_prop4( 2090251881Speter repos, opt_state->start_revision.value.number, 2091251881Speter NULL, prop_name, NULL, prop_value, 2092251881Speter opt_state->use_pre_revprop_change_hook, 2093251881Speter opt_state->use_post_revprop_change_hook, 2094251881Speter NULL, NULL, pool)); 2095251881Speter 2096251881Speter return SVN_NO_ERROR; 2097251881Speter} 2098251881Speter 2099251881Speter 2100251881Speter/* This implements `svn_opt_subcommand_t'. */ 2101251881Speterstatic svn_error_t * 2102251881Spetersubcommand_setrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2103251881Speter{ 2104251881Speter struct svnadmin_opt_state *opt_state = baton; 2105251881Speter apr_array_header_t *args; 2106251881Speter const char *prop_name, *filename; 2107251881Speter 2108251881Speter /* Expect two more arguments: NAME FILE */ 2109251881Speter SVN_ERR(parse_args(&args, os, 2, 2, pool)); 2110251881Speter prop_name = APR_ARRAY_IDX(args, 0, const char *); 2111251881Speter filename = APR_ARRAY_IDX(args, 1, const char *); 2112251881Speter SVN_ERR(target_arg_to_dirent(&filename, filename, pool)); 2113251881Speter 2114289180Speter if (opt_state->txn_id) 2115289180Speter { 2116289180Speter if (opt_state->start_revision.kind != svn_opt_revision_unspecified 2117289180Speter || opt_state->end_revision.kind != svn_opt_revision_unspecified) 2118289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2119289180Speter _("--revision (-r) and --transaction (-t) " 2120289180Speter "are mutually exclusive")); 2121289180Speter 2122289180Speter if (opt_state->use_pre_revprop_change_hook 2123289180Speter || opt_state->use_post_revprop_change_hook) 2124289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2125289180Speter _("Calling hooks is incompatible with " 2126289180Speter "--transaction (-t)")); 2127289180Speter } 2128289180Speter else if (opt_state->start_revision.kind != svn_opt_revision_number) 2129251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2130251881Speter _("Missing revision")); 2131251881Speter else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 2132251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2133251881Speter _("Only one revision allowed")); 2134251881Speter 2135251881Speter return set_revprop(prop_name, filename, opt_state, pool); 2136251881Speter} 2137251881Speter 2138251881Speter 2139251881Speter/* This implements `svn_opt_subcommand_t'. */ 2140251881Speterstatic svn_error_t * 2141251881Spetersubcommand_setuuid(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2142251881Speter{ 2143251881Speter struct svnadmin_opt_state *opt_state = baton; 2144251881Speter apr_array_header_t *args; 2145251881Speter svn_repos_t *repos; 2146251881Speter svn_fs_t *fs; 2147251881Speter const char *uuid = NULL; 2148251881Speter 2149251881Speter /* Expect zero or one more arguments: [UUID] */ 2150251881Speter SVN_ERR(parse_args(&args, os, 0, 1, pool)); 2151251881Speter if (args->nelts == 1) 2152251881Speter uuid = APR_ARRAY_IDX(args, 0, const char *); 2153251881Speter 2154362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2155251881Speter fs = svn_repos_fs(repos); 2156251881Speter return svn_fs_set_uuid(fs, uuid, pool); 2157251881Speter} 2158251881Speter 2159251881Speter 2160251881Speter/* This implements `svn_opt_subcommand_t'. */ 2161251881Speterstatic svn_error_t * 2162251881Spetersubcommand_setlog(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2163251881Speter{ 2164251881Speter struct svnadmin_opt_state *opt_state = baton; 2165251881Speter apr_array_header_t *args; 2166251881Speter const char *filename; 2167251881Speter 2168251881Speter /* Expect one more argument: FILE */ 2169251881Speter SVN_ERR(parse_args(&args, os, 1, 1, pool)); 2170251881Speter filename = APR_ARRAY_IDX(args, 0, const char *); 2171251881Speter SVN_ERR(target_arg_to_dirent(&filename, filename, pool)); 2172251881Speter 2173251881Speter if (opt_state->start_revision.kind != svn_opt_revision_number) 2174251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2175251881Speter _("Missing revision")); 2176251881Speter else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 2177251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2178251881Speter _("Only one revision allowed")); 2179251881Speter 2180251881Speter /* set_revprop() responds only to pre-/post-revprop-change opts. */ 2181251881Speter if (!opt_state->bypass_hooks) 2182251881Speter { 2183251881Speter opt_state->use_pre_revprop_change_hook = TRUE; 2184251881Speter opt_state->use_post_revprop_change_hook = TRUE; 2185251881Speter } 2186251881Speter 2187251881Speter return set_revprop(SVN_PROP_REVISION_LOG, filename, opt_state, pool); 2188251881Speter} 2189251881Speter 2190251881Speter 2191251881Speter/* This implements 'svn_opt_subcommand_t'. */ 2192251881Speterstatic svn_error_t * 2193251881Spetersubcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2194251881Speter{ 2195251881Speter struct svnadmin_opt_state *opt_state = baton; 2196251881Speter svn_repos_t *repos; 2197289180Speter svn_stream_t *feedback_stream = NULL; 2198251881Speter 2199251881Speter /* Expect no more arguments. */ 2200251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 2201251881Speter 2202362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2203251881Speter 2204251881Speter /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 2205251881Speter if (! opt_state->quiet) 2206289180Speter feedback_stream = recode_stream_create(stdout, pool); 2207251881Speter 2208251881Speter return svn_error_trace( 2209251881Speter svn_repos_fs_pack2(repos, !opt_state->quiet ? repos_notify_handler : NULL, 2210289180Speter feedback_stream, check_cancel, NULL, pool)); 2211251881Speter} 2212251881Speter 2213251881Speter 2214251881Speter/* This implements `svn_opt_subcommand_t'. */ 2215251881Speterstatic svn_error_t * 2216251881Spetersubcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2217251881Speter{ 2218251881Speter struct svnadmin_opt_state *opt_state = baton; 2219251881Speter svn_repos_t *repos; 2220251881Speter svn_fs_t *fs; 2221251881Speter svn_revnum_t youngest, lower, upper; 2222289180Speter svn_stream_t *feedback_stream = NULL; 2223289180Speter struct repos_verify_callback_baton verify_baton = { 0 }; 2224251881Speter 2225251881Speter /* Expect no more arguments. */ 2226251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 2227251881Speter 2228251881Speter if (opt_state->txn_id 2229251881Speter && (opt_state->start_revision.kind != svn_opt_revision_unspecified 2230251881Speter || opt_state->end_revision.kind != svn_opt_revision_unspecified)) 2231251881Speter { 2232251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2233251881Speter _("--revision (-r) and --transaction (-t) " 2234251881Speter "are mutually exclusive")); 2235251881Speter } 2236251881Speter 2237362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2238251881Speter fs = svn_repos_fs(repos); 2239251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 2240251881Speter 2241251881Speter /* Usage 2. */ 2242251881Speter if (opt_state->txn_id) 2243251881Speter { 2244251881Speter svn_fs_txn_t *txn; 2245251881Speter svn_fs_root_t *root; 2246251881Speter 2247251881Speter SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool)); 2248251881Speter SVN_ERR(svn_fs_txn_root(&root, txn, pool)); 2249251881Speter SVN_ERR(svn_fs_verify_root(root, pool)); 2250251881Speter return SVN_NO_ERROR; 2251251881Speter } 2252251881Speter else 2253251881Speter /* Usage 1. */ 2254251881Speter ; 2255251881Speter 2256251881Speter /* Find the revision numbers at which to start and end. */ 2257251881Speter SVN_ERR(get_revnum(&lower, &opt_state->start_revision, 2258251881Speter youngest, repos, pool)); 2259251881Speter SVN_ERR(get_revnum(&upper, &opt_state->end_revision, 2260251881Speter youngest, repos, pool)); 2261251881Speter 2262251881Speter if (upper == SVN_INVALID_REVNUM) 2263251881Speter { 2264251881Speter upper = lower; 2265251881Speter } 2266251881Speter 2267289180Speter if (!opt_state->quiet) 2268289180Speter feedback_stream = recode_stream_create(stdout, pool); 2269251881Speter 2270289180Speter verify_baton.keep_going = opt_state->keep_going; 2271289180Speter verify_baton.error_summary = 2272289180Speter apr_array_make(pool, 0, sizeof(struct verification_error *)); 2273289180Speter verify_baton.result_pool = pool; 2274289180Speter 2275289180Speter SVN_ERR(svn_repos_verify_fs3(repos, lower, upper, 2276289180Speter opt_state->check_normalization, 2277289180Speter opt_state->metadata_only, 2278289180Speter !opt_state->quiet 2279289180Speter ? repos_notify_handler : NULL, 2280289180Speter feedback_stream, 2281289180Speter repos_verify_callback, &verify_baton, 2282289180Speter check_cancel, NULL, pool)); 2283289180Speter 2284289180Speter /* Show the --keep-going error summary. */ 2285362181Sdim if (opt_state->keep_going && verify_baton.error_summary->nelts > 0) 2286289180Speter { 2287289180Speter int rev_maxlength; 2288289180Speter svn_revnum_t end_revnum; 2289289180Speter apr_pool_t *iterpool; 2290289180Speter int i; 2291289180Speter 2292362181Sdim if (feedback_stream == NULL) /* happens when we are in --quiet mode */ 2293362181Sdim feedback_stream = recode_stream_create(stdout, pool); 2294362181Sdim 2295289180Speter svn_error_clear( 2296289180Speter svn_stream_puts(feedback_stream, 2297289180Speter _("\n-----Summary of corrupt revisions-----\n"))); 2298289180Speter 2299289180Speter /* The standard column width for the revision number is 6 characters. 2300289180Speter If the revision number can potentially be larger (i.e. if end_revnum 2301289180Speter is larger than 1000000), we increase the column width as needed. */ 2302289180Speter rev_maxlength = 6; 2303289180Speter end_revnum = APR_ARRAY_IDX(verify_baton.error_summary, 2304289180Speter verify_baton.error_summary->nelts - 1, 2305289180Speter struct verification_error *)->rev; 2306289180Speter while (end_revnum >= 1000000) 2307289180Speter { 2308289180Speter rev_maxlength++; 2309289180Speter end_revnum = end_revnum / 10; 2310289180Speter } 2311289180Speter 2312289180Speter iterpool = svn_pool_create(pool); 2313289180Speter for (i = 0; i < verify_baton.error_summary->nelts; i++) 2314289180Speter { 2315289180Speter struct verification_error *verr; 2316289180Speter svn_error_t *err; 2317289180Speter const char *rev_str; 2318289180Speter 2319289180Speter svn_pool_clear(iterpool); 2320289180Speter 2321289180Speter verr = APR_ARRAY_IDX(verify_baton.error_summary, i, 2322289180Speter struct verification_error *); 2323289180Speter 2324289180Speter if (verr->rev != SVN_INVALID_REVNUM) 2325289180Speter { 2326289180Speter rev_str = apr_psprintf(iterpool, "r%ld", verr->rev); 2327289180Speter rev_str = apr_psprintf(iterpool, "%*s", rev_maxlength, rev_str); 2328289180Speter for (err = svn_error_purge_tracing(verr->err); 2329289180Speter err != SVN_NO_ERROR; err = err->child) 2330289180Speter { 2331289180Speter char buf[512]; 2332289180Speter const char *message; 2333289180Speter 2334289180Speter message = svn_err_best_message(err, buf, sizeof(buf)); 2335289180Speter svn_error_clear(svn_stream_printf(feedback_stream, iterpool, 2336289180Speter "%s: E%06d: %s\n", 2337289180Speter rev_str, err->apr_err, 2338289180Speter message)); 2339289180Speter } 2340289180Speter } 2341289180Speter } 2342289180Speter 2343289180Speter svn_pool_destroy(iterpool); 2344289180Speter } 2345289180Speter 2346289180Speter if (verify_baton.error_summary->nelts > 0) 2347289180Speter { 2348289180Speter return svn_error_createf(SVN_ERR_CL_REPOS_VERIFY_FAILED, NULL, 2349289180Speter _("Failed to verify repository '%s'"), 2350289180Speter svn_dirent_local_style( 2351289180Speter opt_state->repository_path, pool)); 2352289180Speter } 2353289180Speter 2354289180Speter return SVN_NO_ERROR; 2355251881Speter} 2356251881Speter 2357251881Speter/* This implements `svn_opt_subcommand_t'. */ 2358251881Spetersvn_error_t * 2359251881Spetersubcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2360251881Speter{ 2361251881Speter struct svnadmin_opt_state *opt_state = baton; 2362289180Speter svn_stream_t *feedback_stream = NULL; 2363251881Speter apr_array_header_t *targets; 2364251881Speter const char *new_repos_path; 2365251881Speter 2366251881Speter /* Expect one more argument: NEW_REPOS_PATH */ 2367251881Speter SVN_ERR(parse_args(&targets, os, 1, 1, pool)); 2368251881Speter new_repos_path = APR_ARRAY_IDX(targets, 0, const char *); 2369251881Speter SVN_ERR(target_arg_to_dirent(&new_repos_path, new_repos_path, pool)); 2370251881Speter 2371289180Speter /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 2372289180Speter if (! opt_state->quiet) 2373289180Speter feedback_stream = recode_stream_create(stdout, pool); 2374289180Speter 2375289180Speter return svn_repos_hotcopy3(opt_state->repository_path, new_repos_path, 2376251881Speter opt_state->clean_logs, opt_state->incremental, 2377289180Speter !opt_state->quiet ? repos_notify_handler : NULL, 2378289180Speter feedback_stream, check_cancel, NULL, pool); 2379251881Speter} 2380251881Speter 2381289180Spetersvn_error_t * 2382289180Spetersubcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2383289180Speter{ 2384289180Speter struct svnadmin_opt_state *opt_state = baton; 2385289180Speter svn_repos_t *repos; 2386289180Speter svn_fs_t *fs; 2387289180Speter int fs_format; 2388289180Speter const char *uuid; 2389362181Sdim svn_revnum_t head_rev; 2390289180Speter 2391289180Speter /* Expect no more arguments. */ 2392289180Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 2393289180Speter 2394362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2395289180Speter fs = svn_repos_fs(repos); 2396289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), 2397289180Speter svn_dirent_local_style(svn_repos_path(repos, pool), 2398289180Speter pool))); 2399289180Speter 2400289180Speter SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); 2401289180Speter SVN_ERR(svn_cmdline_printf(pool, _("UUID: %s\n"), uuid)); 2402362181Sdim 2403362181Sdim SVN_ERR(svn_fs_youngest_rev(&head_rev, fs, pool)); 2404362181Sdim SVN_ERR(svn_cmdline_printf(pool, _("Revisions: %ld\n"), head_rev)); 2405289180Speter { 2406289180Speter int repos_format, minor; 2407289180Speter svn_version_t *repos_version, *fs_version; 2408289180Speter SVN_ERR(svn_repos_info_format(&repos_format, &repos_version, 2409289180Speter repos, pool, pool)); 2410289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Repository Format: %d\n"), 2411289180Speter repos_format)); 2412289180Speter 2413289180Speter SVN_ERR(svn_fs_info_format(&fs_format, &fs_version, 2414289180Speter fs, pool, pool)); 2415289180Speter /* fs_format will be printed later. */ 2416289180Speter 2417289180Speter SVN_ERR_ASSERT(repos_version->major == SVN_VER_MAJOR); 2418289180Speter SVN_ERR_ASSERT(fs_version->major == SVN_VER_MAJOR); 2419289180Speter SVN_ERR_ASSERT(repos_version->patch == 0); 2420289180Speter SVN_ERR_ASSERT(fs_version->patch == 0); 2421289180Speter 2422289180Speter minor = (repos_version->minor > fs_version->minor) 2423289180Speter ? repos_version->minor : fs_version->minor; 2424289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Compatible With Version: %d.%d.0\n"), 2425289180Speter SVN_VER_MAJOR, minor)); 2426289180Speter } 2427289180Speter 2428289180Speter { 2429289180Speter apr_hash_t *capabilities_set; 2430289180Speter apr_array_header_t *capabilities; 2431289180Speter int i; 2432289180Speter 2433289180Speter SVN_ERR(svn_repos_capabilities(&capabilities_set, repos, pool, pool)); 2434289180Speter capabilities = svn_sort__hash(capabilities_set, 2435289180Speter svn_sort_compare_items_lexically, 2436289180Speter pool); 2437289180Speter 2438289180Speter for (i = 0; i < capabilities->nelts; i++) 2439289180Speter { 2440289180Speter svn_sort__item_t *item = &APR_ARRAY_IDX(capabilities, i, 2441289180Speter svn_sort__item_t); 2442289180Speter const char *capability = item->key; 2443289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Repository Capability: %s\n"), 2444289180Speter capability)); 2445289180Speter } 2446289180Speter } 2447289180Speter 2448289180Speter { 2449289180Speter const svn_fs_info_placeholder_t *info; 2450289180Speter 2451289180Speter SVN_ERR(svn_fs_info(&info, fs, pool, pool)); 2452289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Type: %s\n"), 2453289180Speter info->fs_type)); 2454289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Format: %d\n"), 2455289180Speter fs_format)); 2456289180Speter if (!strcmp(info->fs_type, SVN_FS_TYPE_FSFS)) 2457289180Speter { 2458289180Speter const svn_fs_fsfs_info_t *fsfs_info = (const void *)info; 2459289180Speter 2460289180Speter if (fsfs_info->shard_size) 2461289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: yes\n"))); 2462289180Speter else 2463289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: no\n"))); 2464289180Speter 2465289180Speter if (fsfs_info->shard_size) 2466289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shard Size: %d\n"), 2467289180Speter fsfs_info->shard_size)); 2468289180Speter 2469289180Speter /* Print packing statistics, if enabled on the FS. */ 2470289180Speter if (fsfs_info->shard_size) 2471289180Speter { 2472289180Speter const int shard_size = fsfs_info->shard_size; 2473289180Speter const long shards_packed = fsfs_info->min_unpacked_rev / shard_size; 2474362181Sdim const long shards_full = (head_rev + 1) / shard_size; 2475289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shards Packed: %ld/%ld\n"), 2476289180Speter shards_packed, shards_full)); 2477289180Speter } 2478289180Speter 2479289180Speter if (fsfs_info->log_addressing) 2480289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: yes\n"))); 2481289180Speter else 2482289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: no\n"))); 2483289180Speter } 2484362181Sdim else if (!strcmp(info->fs_type, SVN_FS_TYPE_FSX)) 2485362181Sdim { 2486362181Sdim const svn_fs_fsx_info_t *fsx_info = (const void *)info; 2487362181Sdim 2488362181Sdim const int shard_size = fsx_info->shard_size; 2489362181Sdim const long shards_packed = fsx_info->min_unpacked_rev / shard_size; 2490362181Sdim long shards_full = (head_rev + 1) / shard_size; 2491362181Sdim 2492362181Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSX Shard Size: %d\n"), 2493362181Sdim shard_size)); 2494362181Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSX Shards Packed: %ld/%ld\n"), 2495362181Sdim shards_packed, shards_full)); 2496362181Sdim } 2497289180Speter } 2498289180Speter 2499289180Speter { 2500289180Speter apr_array_header_t *files; 2501289180Speter int i; 2502289180Speter 2503289180Speter SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool)); 2504289180Speter for (i = 0; i < files->nelts; i++) 2505289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Configuration File: %s\n"), 2506289180Speter svn_dirent_local_style( 2507289180Speter APR_ARRAY_IDX(files, i, const char *), 2508289180Speter pool))); 2509289180Speter } 2510289180Speter 2511289180Speter /* 'svn info' prints an extra newline here, to support multiple targets. 2512289180Speter We'll do the same. */ 2513289180Speter SVN_ERR(svn_cmdline_printf(pool, "\n")); 2514289180Speter 2515289180Speter return SVN_NO_ERROR; 2516289180Speter} 2517289180Speter 2518251881Speter/* This implements `svn_opt_subcommand_t'. */ 2519251881Speterstatic svn_error_t * 2520251881Spetersubcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2521251881Speter{ 2522251881Speter struct svnadmin_opt_state *opt_state = baton; 2523251881Speter svn_repos_t *repos; 2524251881Speter svn_fs_t *fs; 2525251881Speter svn_fs_access_t *access; 2526251881Speter apr_array_header_t *args; 2527251881Speter const char *username; 2528251881Speter const char *lock_path; 2529251881Speter const char *comment_file_name; 2530251881Speter svn_stringbuf_t *file_contents; 2531251881Speter svn_lock_t *lock; 2532251881Speter const char *lock_token = NULL; 2533251881Speter 2534251881Speter /* Expect three more arguments: PATH USERNAME COMMENT-FILE */ 2535251881Speter SVN_ERR(parse_args(&args, os, 3, 4, pool)); 2536251881Speter lock_path = APR_ARRAY_IDX(args, 0, const char *); 2537251881Speter username = APR_ARRAY_IDX(args, 1, const char *); 2538251881Speter comment_file_name = APR_ARRAY_IDX(args, 2, const char *); 2539251881Speter 2540251881Speter /* Expect one more optional argument: TOKEN */ 2541251881Speter if (args->nelts == 4) 2542251881Speter lock_token = APR_ARRAY_IDX(args, 3, const char *); 2543251881Speter 2544251881Speter SVN_ERR(target_arg_to_dirent(&comment_file_name, comment_file_name, pool)); 2545251881Speter 2546362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2547251881Speter fs = svn_repos_fs(repos); 2548251881Speter 2549251881Speter /* Create an access context describing the user. */ 2550251881Speter SVN_ERR(svn_fs_create_access(&access, username, pool)); 2551251881Speter 2552251881Speter /* Attach the access context to the filesystem. */ 2553251881Speter SVN_ERR(svn_fs_set_access(fs, access)); 2554251881Speter 2555251881Speter SVN_ERR(svn_stringbuf_from_file2(&file_contents, comment_file_name, pool)); 2556251881Speter 2557362181Sdim SVN_ERR(target_arg_to_fspath(&lock_path, lock_path, pool, pool)); 2558251881Speter 2559251881Speter if (opt_state->bypass_hooks) 2560362181Sdim SVN_ERR(svn_fs_lock(&lock, fs, lock_path, 2561251881Speter lock_token, 2562251881Speter file_contents->data, /* comment */ 2563251881Speter 0, /* is_dav_comment */ 2564251881Speter 0, /* no expiration time. */ 2565251881Speter SVN_INVALID_REVNUM, 2566251881Speter FALSE, pool)); 2567251881Speter else 2568362181Sdim SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path, 2569251881Speter lock_token, 2570251881Speter file_contents->data, /* comment */ 2571251881Speter 0, /* is_dav_comment */ 2572251881Speter 0, /* no expiration time. */ 2573251881Speter SVN_INVALID_REVNUM, 2574251881Speter FALSE, pool)); 2575251881Speter 2576362181Sdim if (! opt_state->quiet) 2577362181Sdim SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), 2578362181Sdim lock_path, username)); 2579362181Sdim 2580251881Speter return SVN_NO_ERROR; 2581251881Speter} 2582251881Speter 2583251881Speterstatic svn_error_t * 2584251881Spetersubcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2585251881Speter{ 2586251881Speter struct svnadmin_opt_state *opt_state = baton; 2587251881Speter apr_array_header_t *targets; 2588251881Speter svn_repos_t *repos; 2589362181Sdim const char *fs_path; 2590251881Speter apr_hash_t *locks; 2591251881Speter apr_hash_index_t *hi; 2592289180Speter apr_pool_t *iterpool = svn_pool_create(pool); 2593251881Speter 2594251881Speter SVN_ERR(svn_opt__args_to_target_array(&targets, os, 2595251881Speter apr_array_make(pool, 0, 2596251881Speter sizeof(const char *)), 2597251881Speter pool)); 2598251881Speter if (targets->nelts > 1) 2599251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 2600251881Speter _("Too many arguments given")); 2601251881Speter if (targets->nelts) 2602251881Speter fs_path = APR_ARRAY_IDX(targets, 0, const char *); 2603362181Sdim else 2604362181Sdim fs_path = "/"; 2605362181Sdim SVN_ERR(target_arg_to_fspath(&fs_path, fs_path, pool, pool)); 2606251881Speter 2607362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2608251881Speter 2609251881Speter /* Fetch all locks on or below the root directory. */ 2610251881Speter SVN_ERR(svn_repos_fs_get_locks2(&locks, repos, fs_path, svn_depth_infinity, 2611251881Speter NULL, NULL, pool)); 2612251881Speter 2613251881Speter for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) 2614251881Speter { 2615251881Speter const char *cr_date, *exp_date = ""; 2616289180Speter const char *path = apr_hash_this_key(hi); 2617289180Speter svn_lock_t *lock = apr_hash_this_val(hi); 2618251881Speter int comment_lines = 0; 2619251881Speter 2620289180Speter svn_pool_clear(iterpool); 2621251881Speter 2622289180Speter SVN_ERR(check_cancel(NULL)); 2623289180Speter 2624289180Speter cr_date = svn_time_to_human_cstring(lock->creation_date, iterpool); 2625289180Speter 2626251881Speter if (lock->expiration_date) 2627289180Speter exp_date = svn_time_to_human_cstring(lock->expiration_date, iterpool); 2628251881Speter 2629251881Speter if (lock->comment) 2630251881Speter comment_lines = svn_cstring_count_newlines(lock->comment) + 1; 2631251881Speter 2632289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Path: %s\n"), path)); 2633289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("UUID Token: %s\n"), lock->token)); 2634289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Owner: %s\n"), lock->owner)); 2635289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Created: %s\n"), cr_date)); 2636289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Expires: %s\n"), exp_date)); 2637289180Speter SVN_ERR(svn_cmdline_printf(iterpool, 2638251881Speter Q_("Comment (%i line):\n%s\n\n", 2639251881Speter "Comment (%i lines):\n%s\n\n", 2640251881Speter comment_lines), 2641251881Speter comment_lines, 2642251881Speter lock->comment ? lock->comment : "")); 2643251881Speter } 2644251881Speter 2645289180Speter svn_pool_destroy(iterpool); 2646289180Speter 2647251881Speter return SVN_NO_ERROR; 2648251881Speter} 2649251881Speter 2650251881Speter 2651251881Speter 2652251881Speterstatic svn_error_t * 2653251881Spetersubcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2654251881Speter{ 2655251881Speter struct svnadmin_opt_state *opt_state = baton; 2656251881Speter svn_repos_t *repos; 2657251881Speter svn_fs_t *fs; 2658251881Speter svn_fs_access_t *access; 2659251881Speter svn_error_t *err; 2660251881Speter apr_array_header_t *args; 2661251881Speter int i; 2662251881Speter const char *username; 2663251881Speter apr_pool_t *subpool = svn_pool_create(pool); 2664251881Speter 2665362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2666251881Speter fs = svn_repos_fs(repos); 2667251881Speter 2668251881Speter /* svn_fs_unlock() demands that some username be associated with the 2669251881Speter filesystem, so just use the UID of the person running 'svnadmin'.*/ 2670251881Speter username = svn_user_get_name(pool); 2671251881Speter if (! username) 2672251881Speter username = "administrator"; 2673251881Speter 2674251881Speter /* Create an access context describing the current user. */ 2675251881Speter SVN_ERR(svn_fs_create_access(&access, username, pool)); 2676251881Speter 2677251881Speter /* Attach the access context to the filesystem. */ 2678251881Speter SVN_ERR(svn_fs_set_access(fs, access)); 2679251881Speter 2680251881Speter /* Parse out any options. */ 2681362181Sdim SVN_ERR(parse_args(&args, os, -1, -1, pool)); 2682251881Speter 2683251881Speter /* Our usage requires at least one FS path. */ 2684251881Speter if (args->nelts == 0) 2685251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 2686251881Speter _("No paths to unlock provided")); 2687251881Speter 2688251881Speter /* All the rest of the arguments are paths from which to remove locks. */ 2689251881Speter for (i = 0; i < args->nelts; i++) 2690251881Speter { 2691251881Speter const char *lock_path = APR_ARRAY_IDX(args, i, const char *); 2692251881Speter svn_lock_t *lock; 2693251881Speter 2694362181Sdim SVN_ERR(target_arg_to_fspath(&lock_path, lock_path, subpool, subpool)); 2695251881Speter 2696251881Speter /* Fetch the path's svn_lock_t. */ 2697362181Sdim err = svn_fs_get_lock(&lock, fs, lock_path, subpool); 2698251881Speter if (err) 2699251881Speter goto move_on; 2700251881Speter if (! lock) 2701251881Speter { 2702362181Sdim if (! opt_state->quiet) 2703362181Sdim SVN_ERR(svn_cmdline_printf(subpool, 2704362181Sdim _("Path '%s' isn't locked.\n"), 2705362181Sdim lock_path)); 2706251881Speter continue; 2707251881Speter } 2708362181Sdim lock = NULL; /* Don't access LOCK after this point. */ 2709251881Speter 2710251881Speter /* Now forcibly destroy the lock. */ 2711362181Sdim err = svn_fs_unlock(fs, lock_path, 2712362181Sdim NULL, 1 /* force */, subpool); 2713251881Speter if (err) 2714251881Speter goto move_on; 2715251881Speter 2716362181Sdim if (! opt_state->quiet) 2717362181Sdim SVN_ERR(svn_cmdline_printf(subpool, 2718362181Sdim _("Removed lock on '%s'.\n"), 2719362181Sdim lock_path)); 2720251881Speter 2721251881Speter move_on: 2722251881Speter if (err) 2723251881Speter { 2724251881Speter /* Print the error, but move on to the next lock. */ 2725251881Speter svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: "); 2726251881Speter svn_error_clear(err); 2727251881Speter } 2728251881Speter 2729251881Speter svn_pool_clear(subpool); 2730251881Speter } 2731251881Speter 2732251881Speter svn_pool_destroy(subpool); 2733251881Speter return SVN_NO_ERROR; 2734251881Speter} 2735251881Speter 2736251881Speter 2737251881Speter/* This implements `svn_opt_subcommand_t'. */ 2738251881Speterstatic svn_error_t * 2739251881Spetersubcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2740251881Speter{ 2741251881Speter struct svnadmin_opt_state *opt_state = baton; 2742251881Speter svn_repos_t *repos; 2743251881Speter svn_fs_t *fs; 2744251881Speter svn_fs_access_t *access; 2745251881Speter apr_array_header_t *args; 2746251881Speter const char *username; 2747251881Speter const char *lock_path; 2748251881Speter const char *lock_token = NULL; 2749251881Speter 2750251881Speter /* Expect three more arguments: PATH USERNAME TOKEN */ 2751251881Speter SVN_ERR(parse_args(&args, os, 3, 3, pool)); 2752251881Speter lock_path = APR_ARRAY_IDX(args, 0, const char *); 2753251881Speter username = APR_ARRAY_IDX(args, 1, const char *); 2754251881Speter lock_token = APR_ARRAY_IDX(args, 2, const char *); 2755251881Speter 2756251881Speter /* Open the repos/FS, and associate an access context containing 2757251881Speter USERNAME. */ 2758362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2759251881Speter fs = svn_repos_fs(repos); 2760251881Speter SVN_ERR(svn_fs_create_access(&access, username, pool)); 2761251881Speter SVN_ERR(svn_fs_set_access(fs, access)); 2762251881Speter 2763362181Sdim SVN_ERR(target_arg_to_fspath(&lock_path, lock_path, pool, pool)); 2764251881Speter if (opt_state->bypass_hooks) 2765362181Sdim SVN_ERR(svn_fs_unlock(fs, lock_path, lock_token, 2766251881Speter FALSE, pool)); 2767251881Speter else 2768362181Sdim SVN_ERR(svn_repos_fs_unlock(repos, lock_path, lock_token, 2769251881Speter FALSE, pool)); 2770251881Speter 2771362181Sdim if (! opt_state->quiet) 2772362181Sdim SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"), 2773362181Sdim lock_path, username)); 2774362181Sdim 2775251881Speter return SVN_NO_ERROR; 2776251881Speter} 2777251881Speter 2778251881Speter 2779251881Speter/* This implements `svn_opt_subcommand_t'. */ 2780251881Speterstatic svn_error_t * 2781251881Spetersubcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2782251881Speter{ 2783251881Speter svn_error_t *err; 2784251881Speter struct svnadmin_opt_state *opt_state = baton; 2785289180Speter svn_stream_t *feedback_stream = NULL; 2786251881Speter 2787251881Speter /* Expect no more arguments. */ 2788251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 2789251881Speter 2790289180Speter SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool)); 2791251881Speter 2792251881Speter /* Restore default signal handlers. */ 2793362181Sdim svn_cmdline__disable_cancellation_handler(); 2794251881Speter 2795251881Speter err = svn_repos_upgrade2(opt_state->repository_path, TRUE, 2796289180Speter repos_notify_handler, feedback_stream, pool); 2797251881Speter if (err) 2798251881Speter { 2799251881Speter if (APR_STATUS_IS_EAGAIN(err->apr_err)) 2800251881Speter { 2801251881Speter svn_error_clear(err); 2802251881Speter err = SVN_NO_ERROR; 2803251881Speter if (! opt_state->wait) 2804251881Speter return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL, 2805251881Speter _("Failed to get exclusive repository " 2806251881Speter "access; perhaps another process\n" 2807251881Speter "such as httpd, svnserve or svn " 2808251881Speter "has it open?")); 2809251881Speter SVN_ERR(svn_cmdline_printf(pool, 2810251881Speter _("Waiting on repository lock; perhaps" 2811251881Speter " another process has it open?\n"))); 2812251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 2813251881Speter SVN_ERR(svn_repos_upgrade2(opt_state->repository_path, FALSE, 2814289180Speter repos_notify_handler, feedback_stream, 2815251881Speter pool)); 2816251881Speter } 2817251881Speter else if (err->apr_err == SVN_ERR_FS_UNSUPPORTED_UPGRADE) 2818251881Speter { 2819251881Speter return svn_error_quick_wrap(err, 2820251881Speter _("Upgrade of this repository's underlying versioned " 2821251881Speter "filesystem is not supported; consider " 2822251881Speter "dumping and loading the data elsewhere")); 2823251881Speter } 2824251881Speter else if (err->apr_err == SVN_ERR_REPOS_UNSUPPORTED_UPGRADE) 2825251881Speter { 2826251881Speter return svn_error_quick_wrap(err, 2827251881Speter _("Upgrade of this repository is not supported; consider " 2828251881Speter "dumping and loading the data elsewhere")); 2829251881Speter } 2830251881Speter } 2831251881Speter SVN_ERR(err); 2832251881Speter 2833251881Speter SVN_ERR(svn_cmdline_printf(pool, _("\nUpgrade completed.\n"))); 2834251881Speter return SVN_NO_ERROR; 2835251881Speter} 2836251881Speter 2837251881Speter 2838289180Speter/* This implements `svn_opt_subcommand_t'. */ 2839289180Speterstatic svn_error_t * 2840289180Spetersubcommand_delrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2841289180Speter{ 2842289180Speter struct svnadmin_opt_state *opt_state = baton; 2843289180Speter apr_array_header_t *args; 2844289180Speter const char *prop_name; 2845289180Speter 2846289180Speter /* Expect one more argument: NAME */ 2847289180Speter SVN_ERR(parse_args(&args, os, 1, 1, pool)); 2848289180Speter prop_name = APR_ARRAY_IDX(args, 0, const char *); 2849289180Speter 2850289180Speter if (opt_state->txn_id) 2851289180Speter { 2852289180Speter if (opt_state->start_revision.kind != svn_opt_revision_unspecified 2853289180Speter || opt_state->end_revision.kind != svn_opt_revision_unspecified) 2854289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2855289180Speter _("--revision (-r) and --transaction (-t) " 2856289180Speter "are mutually exclusive")); 2857289180Speter 2858289180Speter if (opt_state->use_pre_revprop_change_hook 2859289180Speter || opt_state->use_post_revprop_change_hook) 2860289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2861289180Speter _("Calling hooks is incompatible with " 2862289180Speter "--transaction (-t)")); 2863289180Speter } 2864289180Speter else if (opt_state->start_revision.kind != svn_opt_revision_number) 2865289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2866289180Speter _("Missing revision")); 2867289180Speter else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 2868289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2869289180Speter _("Only one revision allowed")); 2870289180Speter 2871289180Speter return set_revprop(prop_name, NULL, opt_state, pool); 2872289180Speter} 2873289180Speter 2874289180Speter 2875362181Sdim/* Set *REV_SIZE to the total size in bytes of the representation on disk 2876362181Sdim * of revision REVISION in FS. 2877362181Sdim * 2878362181Sdim * This is implemented only for FSFS repositories, and otherwise returns 2879362181Sdim * an SVN_ERR_UNSUPPORTED_FEATURE error. 2880362181Sdim * 2881362181Sdim * The size includes revision properties and excludes FSFS indexes. 2882362181Sdim */ 2883362181Sdimstatic svn_error_t * 2884362181Sdimrevision_size(apr_off_t *rev_size, 2885362181Sdim svn_fs_t *fs, 2886362181Sdim svn_revnum_t revision, 2887362181Sdim apr_pool_t *scratch_pool) 2888362181Sdim{ 2889362181Sdim svn_error_t *err; 2890362181Sdim svn_fs_fs__ioctl_revision_size_input_t input = {0}; 2891362181Sdim svn_fs_fs__ioctl_revision_size_output_t *output; 2892362181Sdim 2893362181Sdim input.revision = revision; 2894362181Sdim err = svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_REVISION_SIZE, 2895362181Sdim &input, (void **)&output, 2896362181Sdim check_cancel, NULL, scratch_pool, scratch_pool); 2897362181Sdim if (err && err->apr_err == SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE) 2898362181Sdim { 2899362181Sdim return svn_error_quick_wrapf(err, 2900362181Sdim _("Revision size query is not implemented " 2901362181Sdim "for the filesystem type found in '%s'"), 2902362181Sdim svn_fs_path(fs, scratch_pool)); 2903362181Sdim } 2904362181Sdim SVN_ERR(err); 2905362181Sdim 2906362181Sdim *rev_size = output->rev_size; 2907362181Sdim return SVN_NO_ERROR; 2908362181Sdim} 2909362181Sdim 2910362181Sdim/* This implements `svn_opt_subcommand_t'. */ 2911362181Sdimsvn_error_t * 2912362181Sdimsubcommand_rev_size(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2913362181Sdim{ 2914362181Sdim struct svnadmin_opt_state *opt_state = baton; 2915362181Sdim svn_revnum_t revision; 2916362181Sdim apr_off_t rev_size; 2917362181Sdim svn_repos_t *repos; 2918362181Sdim 2919362181Sdim if (opt_state->start_revision.kind != svn_opt_revision_number 2920362181Sdim || opt_state->end_revision.kind != svn_opt_revision_unspecified) 2921362181Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2922362181Sdim _("Invalid revision specifier")); 2923362181Sdim revision = opt_state->start_revision.value.number; 2924362181Sdim 2925362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 2926362181Sdim SVN_ERR(revision_size(&rev_size, svn_repos_fs(repos), revision, pool)); 2927362181Sdim 2928362181Sdim if (opt_state->quiet) 2929362181Sdim SVN_ERR(svn_cmdline_printf(pool, "%"APR_OFF_T_FMT"\n", rev_size)); 2930362181Sdim else 2931362181Sdim SVN_ERR(svn_cmdline_printf(pool, _("%12"APR_OFF_T_FMT" bytes in revision %ld\n"), 2932362181Sdim rev_size, revision)); 2933362181Sdim 2934362181Sdim return SVN_NO_ERROR; 2935362181Sdim} 2936362181Sdim 2937362181Sdimstatic void 2938362181Sdimbuild_rep_cache_progress_func(svn_revnum_t revision, 2939362181Sdim void *baton, 2940362181Sdim apr_pool_t *pool) 2941362181Sdim{ 2942362181Sdim svn_error_clear(svn_cmdline_printf(pool, 2943362181Sdim _("* Processed revision %ld.\n"), 2944362181Sdim revision)); 2945362181Sdim} 2946362181Sdim 2947362181Sdimstatic svn_error_t * 2948362181Sdimbuild_rep_cache(svn_fs_t *fs, 2949362181Sdim svn_revnum_t start_rev, 2950362181Sdim svn_revnum_t end_rev, 2951362181Sdim struct svnadmin_opt_state *opt_state, 2952362181Sdim apr_pool_t *pool) 2953362181Sdim{ 2954362181Sdim svn_fs_fs__ioctl_build_rep_cache_input_t input = {0}; 2955362181Sdim svn_error_t *err; 2956362181Sdim 2957362181Sdim input.start_rev = start_rev; 2958362181Sdim input.end_rev = end_rev; 2959362181Sdim 2960362181Sdim if (opt_state->quiet) 2961362181Sdim { 2962362181Sdim input.progress_func = NULL; 2963362181Sdim input.progress_baton = NULL; 2964362181Sdim } 2965362181Sdim else 2966362181Sdim { 2967362181Sdim input.progress_func = build_rep_cache_progress_func; 2968362181Sdim input.progress_baton = NULL; 2969362181Sdim } 2970362181Sdim 2971362181Sdim err = svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_BUILD_REP_CACHE, 2972362181Sdim &input, NULL, 2973362181Sdim check_cancel, NULL, pool, pool); 2974362181Sdim if (err && err->apr_err == SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE) 2975362181Sdim { 2976362181Sdim return svn_error_quick_wrapf(err, 2977362181Sdim _("Building rep-cache is not implemented " 2978362181Sdim "for the filesystem type found in '%s'"), 2979362181Sdim svn_fs_path(fs, pool)); 2980362181Sdim } 2981362181Sdim else if (err && err->apr_err == SVN_ERR_FS_REP_SHARING_NOT_ALLOWED) 2982362181Sdim { 2983362181Sdim svn_error_clear(err); 2984362181Sdim SVN_ERR(svn_cmdline_printf(pool, 2985362181Sdim _("svnadmin: Warning - this repository has rep-sharing disabled." 2986362181Sdim " Building rep-cache has no effect.\n"))); 2987362181Sdim return SVN_NO_ERROR; 2988362181Sdim } 2989362181Sdim else 2990362181Sdim { 2991362181Sdim return err; 2992362181Sdim } 2993362181Sdim} 2994362181Sdim 2995362181Sdim/* This implements `svn_opt_subcommand_t'. */ 2996362181Sdimstatic svn_error_t * 2997362181Sdimsubcommand_build_repcache(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2998362181Sdim{ 2999362181Sdim struct svnadmin_opt_state *opt_state = baton; 3000362181Sdim svn_repos_t *repos; 3001362181Sdim svn_fs_t *fs; 3002362181Sdim svn_revnum_t youngest; 3003362181Sdim svn_revnum_t lower; 3004362181Sdim svn_revnum_t upper; 3005362181Sdim 3006362181Sdim /* Expect no more arguments. */ 3007362181Sdim SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 3008362181Sdim 3009362181Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool)); 3010362181Sdim fs = svn_repos_fs(repos); 3011362181Sdim SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 3012362181Sdim 3013362181Sdim SVN_ERR(get_revnum(&lower, &opt_state->start_revision, 3014362181Sdim youngest, repos, pool)); 3015362181Sdim SVN_ERR(get_revnum(&upper, &opt_state->end_revision, 3016362181Sdim youngest, repos, pool)); 3017362181Sdim 3018362181Sdim if (SVN_IS_VALID_REVNUM(lower) && SVN_IS_VALID_REVNUM(upper)) 3019362181Sdim { 3020362181Sdim if (lower > upper) 3021362181Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 3022362181Sdim _("First revision cannot be higher than second")); 3023362181Sdim } 3024362181Sdim else if (SVN_IS_VALID_REVNUM(lower)) 3025362181Sdim { 3026362181Sdim upper = lower; 3027362181Sdim } 3028362181Sdim else 3029362181Sdim { 3030362181Sdim upper = youngest; 3031362181Sdim } 3032362181Sdim 3033362181Sdim SVN_ERR(build_rep_cache(fs, lower, upper, opt_state, pool)); 3034362181Sdim 3035362181Sdim return SVN_NO_ERROR; 3036362181Sdim} 3037362181Sdim 3038251881Speter 3039251881Speter/** Main. **/ 3040251881Speter 3041289180Speter/* 3042289180Speter * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, 3043289180Speter * either return an error to be displayed, or set *EXIT_CODE to non-zero and 3044289180Speter * return SVN_NO_ERROR. 3045289180Speter */ 3046289180Speterstatic svn_error_t * 3047289180Spetersub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) 3048251881Speter{ 3049251881Speter svn_error_t *err; 3050251881Speter apr_status_t apr_err; 3051251881Speter 3052362181Sdim const svn_opt_subcommand_desc3_t *subcommand = NULL; 3053251881Speter struct svnadmin_opt_state opt_state = { 0 }; 3054251881Speter apr_getopt_t *os; 3055251881Speter int opt_id; 3056251881Speter apr_array_header_t *received_opts; 3057251881Speter int i; 3058251881Speter svn_boolean_t dash_F_arg = FALSE; 3059251881Speter 3060251881Speter received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); 3061251881Speter 3062251881Speter /* Check library versions */ 3063289180Speter SVN_ERR(check_lib_versions()); 3064251881Speter 3065251881Speter /* Initialize the FS library. */ 3066289180Speter SVN_ERR(svn_fs_initialize(pool)); 3067251881Speter 3068251881Speter if (argc <= 1) 3069251881Speter { 3070289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 3071289180Speter *exit_code = EXIT_FAILURE; 3072289180Speter return SVN_NO_ERROR; 3073251881Speter } 3074251881Speter 3075251881Speter /* Initialize opt_state. */ 3076251881Speter opt_state.start_revision.kind = svn_opt_revision_unspecified; 3077251881Speter opt_state.end_revision.kind = svn_opt_revision_unspecified; 3078251881Speter opt_state.memory_cache_size = svn_cache_config_get()->cache_size; 3079251881Speter 3080251881Speter /* Parse options. */ 3081289180Speter SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); 3082251881Speter 3083251881Speter os->interleave = 1; 3084251881Speter 3085251881Speter while (1) 3086251881Speter { 3087251881Speter const char *opt_arg; 3088251881Speter const char *utf8_opt_arg; 3089251881Speter 3090251881Speter /* Parse the next option. */ 3091251881Speter apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg); 3092251881Speter if (APR_STATUS_IS_EOF(apr_err)) 3093251881Speter break; 3094251881Speter else if (apr_err) 3095251881Speter { 3096289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 3097289180Speter *exit_code = EXIT_FAILURE; 3098289180Speter return SVN_NO_ERROR; 3099251881Speter } 3100251881Speter 3101251881Speter /* Stash the option code in an array before parsing it. */ 3102251881Speter APR_ARRAY_PUSH(received_opts, int) = opt_id; 3103251881Speter 3104251881Speter switch (opt_id) { 3105251881Speter case 'r': 3106251881Speter { 3107251881Speter if (opt_state.start_revision.kind != svn_opt_revision_unspecified) 3108251881Speter { 3109289180Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 3110289180Speter _("Multiple revision arguments encountered; " 3111289180Speter "try '-r N:M' instead of '-r N -r M'")); 3112251881Speter } 3113251881Speter if (svn_opt_parse_revision(&(opt_state.start_revision), 3114251881Speter &(opt_state.end_revision), 3115251881Speter opt_arg, pool) != 0) 3116251881Speter { 3117289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 3118251881Speter 3119289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 3120251881Speter _("Syntax error in revision argument '%s'"), 3121251881Speter utf8_opt_arg); 3122251881Speter } 3123251881Speter } 3124251881Speter break; 3125251881Speter case 't': 3126251881Speter opt_state.txn_id = opt_arg; 3127251881Speter break; 3128251881Speter 3129251881Speter case 'q': 3130251881Speter opt_state.quiet = TRUE; 3131251881Speter break; 3132251881Speter case 'h': 3133251881Speter case '?': 3134251881Speter opt_state.help = TRUE; 3135251881Speter break; 3136251881Speter case 'M': 3137362181Sdim { 3138362181Sdim apr_uint64_t sz_val; 3139362181Sdim SVN_ERR(svn_cstring_atoui64(&sz_val, opt_arg)); 3140362181Sdim 3141362181Sdim opt_state.memory_cache_size = 0x100000 * sz_val; 3142362181Sdim } 3143251881Speter break; 3144251881Speter case 'F': 3145362181Sdim SVN_ERR(svn_utf_cstring_to_utf8(&(opt_state.file), opt_arg, pool)); 3146251881Speter dash_F_arg = TRUE; 3147362181Sdim break; 3148251881Speter case svnadmin__version: 3149251881Speter opt_state.version = TRUE; 3150251881Speter break; 3151251881Speter case svnadmin__incremental: 3152251881Speter opt_state.incremental = TRUE; 3153251881Speter break; 3154251881Speter case svnadmin__deltas: 3155251881Speter opt_state.use_deltas = TRUE; 3156251881Speter break; 3157251881Speter case svnadmin__ignore_uuid: 3158251881Speter opt_state.uuid_action = svn_repos_load_uuid_ignore; 3159251881Speter break; 3160251881Speter case svnadmin__force_uuid: 3161251881Speter opt_state.uuid_action = svn_repos_load_uuid_force; 3162251881Speter break; 3163251881Speter case svnadmin__pre_1_4_compatible: 3164289180Speter opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 3165289180Speter opt_state.compatible_version->major = 1; 3166289180Speter opt_state.compatible_version->minor = 3; 3167251881Speter break; 3168251881Speter case svnadmin__pre_1_5_compatible: 3169289180Speter opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 3170289180Speter opt_state.compatible_version->major = 1; 3171289180Speter opt_state.compatible_version->minor = 4; 3172251881Speter break; 3173251881Speter case svnadmin__pre_1_6_compatible: 3174289180Speter opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 3175289180Speter opt_state.compatible_version->major = 1; 3176289180Speter opt_state.compatible_version->minor = 5; 3177251881Speter break; 3178251881Speter case svnadmin__compatible_version: 3179251881Speter { 3180251881Speter svn_version_t latest = { SVN_VER_MAJOR, SVN_VER_MINOR, 3181251881Speter SVN_VER_PATCH, NULL }; 3182251881Speter svn_version_t *compatible_version; 3183251881Speter 3184251881Speter /* Parse the version string which carries our target 3185251881Speter compatibility. */ 3186289180Speter SVN_ERR(svn_version__parse_version_string(&compatible_version, 3187251881Speter opt_arg, pool)); 3188251881Speter 3189251881Speter /* We can't create repository with a version older than 1.0.0. */ 3190251881Speter if (! svn_version__at_least(compatible_version, 1, 0, 0)) 3191251881Speter { 3192289180Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 3193289180Speter _("Cannot create pre-1.0-compatible " 3194289180Speter "repositories")); 3195251881Speter } 3196251881Speter 3197251881Speter /* We can't create repository with a version newer than what 3198251881Speter the running version of Subversion supports. */ 3199251881Speter if (! svn_version__at_least(&latest, 3200251881Speter compatible_version->major, 3201251881Speter compatible_version->minor, 3202251881Speter compatible_version->patch)) 3203251881Speter { 3204289180Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 3205289180Speter _("Cannot guarantee compatibility " 3206289180Speter "beyond the current running version " 3207289180Speter "(%s)"), 3208289180Speter SVN_VER_NUM); 3209251881Speter } 3210251881Speter 3211251881Speter opt_state.compatible_version = compatible_version; 3212251881Speter } 3213251881Speter break; 3214289180Speter case svnadmin__keep_going: 3215289180Speter opt_state.keep_going = TRUE; 3216289180Speter break; 3217289180Speter case svnadmin__check_normalization: 3218289180Speter opt_state.check_normalization = TRUE; 3219289180Speter break; 3220289180Speter case svnadmin__metadata_only: 3221289180Speter opt_state.metadata_only = TRUE; 3222289180Speter break; 3223251881Speter case svnadmin__fs_type: 3224289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool)); 3225251881Speter break; 3226251881Speter case svnadmin__parent_dir: 3227289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.parent_dir, opt_arg, 3228251881Speter pool)); 3229251881Speter opt_state.parent_dir 3230251881Speter = svn_dirent_internal_style(opt_state.parent_dir, pool); 3231251881Speter break; 3232251881Speter case svnadmin__use_pre_commit_hook: 3233251881Speter opt_state.use_pre_commit_hook = TRUE; 3234251881Speter break; 3235251881Speter case svnadmin__use_post_commit_hook: 3236251881Speter opt_state.use_post_commit_hook = TRUE; 3237251881Speter break; 3238251881Speter case svnadmin__use_pre_revprop_change_hook: 3239251881Speter opt_state.use_pre_revprop_change_hook = TRUE; 3240251881Speter break; 3241251881Speter case svnadmin__use_post_revprop_change_hook: 3242251881Speter opt_state.use_post_revprop_change_hook = TRUE; 3243251881Speter break; 3244251881Speter case svnadmin__bdb_txn_nosync: 3245251881Speter opt_state.bdb_txn_nosync = TRUE; 3246251881Speter break; 3247251881Speter case svnadmin__bdb_log_keep: 3248251881Speter opt_state.bdb_log_keep = TRUE; 3249251881Speter break; 3250251881Speter case svnadmin__bypass_hooks: 3251251881Speter opt_state.bypass_hooks = TRUE; 3252251881Speter break; 3253251881Speter case svnadmin__bypass_prop_validation: 3254251881Speter opt_state.bypass_prop_validation = TRUE; 3255251881Speter break; 3256289180Speter case svnadmin__ignore_dates: 3257289180Speter opt_state.ignore_dates = TRUE; 3258289180Speter break; 3259251881Speter case svnadmin__clean_logs: 3260251881Speter opt_state.clean_logs = TRUE; 3261251881Speter break; 3262251881Speter case svnadmin__config_dir: 3263289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 3264251881Speter opt_state.config_dir = 3265251881Speter apr_pstrdup(pool, svn_dirent_canonicalize(utf8_opt_arg, pool)); 3266251881Speter break; 3267251881Speter case svnadmin__wait: 3268251881Speter opt_state.wait = TRUE; 3269251881Speter break; 3270362181Sdim case svnadmin__no_flush_to_disk: 3271362181Sdim opt_state.no_flush_to_disk = TRUE; 3272362181Sdim break; 3273362181Sdim case svnadmin__normalize_props: 3274362181Sdim opt_state.normalize_props = TRUE; 3275362181Sdim break; 3276362181Sdim case svnadmin__exclude: 3277362181Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 3278362181Sdim 3279362181Sdim if (! opt_state.exclude) 3280362181Sdim opt_state.exclude = apr_array_make(pool, 1, sizeof(const char *)); 3281362181Sdim APR_ARRAY_PUSH(opt_state.exclude, const char *) = utf8_opt_arg; 3282362181Sdim break; 3283362181Sdim case svnadmin__include: 3284362181Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 3285362181Sdim 3286362181Sdim if (! opt_state.include) 3287362181Sdim opt_state.include = apr_array_make(pool, 1, sizeof(const char *)); 3288362181Sdim APR_ARRAY_PUSH(opt_state.include, const char *) = utf8_opt_arg; 3289362181Sdim break; 3290362181Sdim case svnadmin__glob: 3291362181Sdim opt_state.glob = TRUE; 3292362181Sdim break; 3293251881Speter default: 3294251881Speter { 3295289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 3296289180Speter *exit_code = EXIT_FAILURE; 3297289180Speter return SVN_NO_ERROR; 3298251881Speter } 3299251881Speter } /* close `switch' */ 3300251881Speter } /* close `while' */ 3301251881Speter 3302251881Speter /* If the user asked for help, then the rest of the arguments are 3303251881Speter the names of subcommands to get help on (if any), or else they're 3304251881Speter just typos/mistakes. Whatever the case, the subcommand to 3305251881Speter actually run is subcommand_help(). */ 3306251881Speter if (opt_state.help) 3307362181Sdim subcommand = svn_opt_get_canonical_subcommand3(cmd_table, "help"); 3308251881Speter 3309251881Speter /* If we're not running the `help' subcommand, then look for a 3310251881Speter subcommand in the first argument. */ 3311251881Speter if (subcommand == NULL) 3312251881Speter { 3313251881Speter if (os->ind >= os->argc) 3314251881Speter { 3315251881Speter if (opt_state.version) 3316251881Speter { 3317251881Speter /* Use the "help" subcommand to handle the "--version" option. */ 3318362181Sdim static const svn_opt_subcommand_desc3_t pseudo_cmd = 3319362181Sdim { "--version", subcommand_help, {0}, {""}, 3320251881Speter {svnadmin__version, /* must accept its own option */ 3321251881Speter 'q', /* --quiet */ 3322251881Speter } }; 3323251881Speter 3324251881Speter subcommand = &pseudo_cmd; 3325251881Speter } 3326251881Speter else 3327251881Speter { 3328251881Speter svn_error_clear(svn_cmdline_fprintf(stderr, pool, 3329251881Speter _("subcommand argument required\n"))); 3330289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 3331289180Speter *exit_code = EXIT_FAILURE; 3332289180Speter return SVN_NO_ERROR; 3333251881Speter } 3334251881Speter } 3335251881Speter else 3336251881Speter { 3337362181Sdim const char *first_arg; 3338362181Sdim 3339362181Sdim SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++], 3340362181Sdim pool)); 3341362181Sdim subcommand = svn_opt_get_canonical_subcommand3(cmd_table, first_arg); 3342251881Speter if (subcommand == NULL) 3343251881Speter { 3344251881Speter svn_error_clear( 3345251881Speter svn_cmdline_fprintf(stderr, pool, 3346251881Speter _("Unknown subcommand: '%s'\n"), 3347362181Sdim first_arg)); 3348289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 3349289180Speter *exit_code = EXIT_FAILURE; 3350289180Speter return SVN_NO_ERROR; 3351251881Speter } 3352251881Speter } 3353251881Speter } 3354251881Speter 3355251881Speter /* Every subcommand except `help' and `freeze' with '-F' require a 3356251881Speter second argument -- the repository path. Parse it out here and 3357251881Speter store it in opt_state. */ 3358251881Speter if (!(subcommand->cmd_func == subcommand_help 3359251881Speter || (subcommand->cmd_func == subcommand_freeze && dash_F_arg))) 3360251881Speter { 3361251881Speter const char *repos_path = NULL; 3362251881Speter 3363251881Speter if (os->ind >= os->argc) 3364251881Speter { 3365289180Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 3366289180Speter _("Repository argument required")); 3367251881Speter } 3368251881Speter 3369289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool)); 3370251881Speter 3371251881Speter if (svn_path_is_url(repos_path)) 3372251881Speter { 3373289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 3374289180Speter _("'%s' is a URL when it should be a " 3375289180Speter "local path"), repos_path); 3376251881Speter } 3377251881Speter 3378251881Speter opt_state.repository_path = svn_dirent_internal_style(repos_path, pool); 3379251881Speter } 3380251881Speter 3381251881Speter /* Check that the subcommand wasn't passed any inappropriate options. */ 3382251881Speter for (i = 0; i < received_opts->nelts; i++) 3383251881Speter { 3384251881Speter opt_id = APR_ARRAY_IDX(received_opts, i, int); 3385251881Speter 3386251881Speter /* All commands implicitly accept --help, so just skip over this 3387251881Speter when we see it. Note that we don't want to include this option 3388251881Speter in their "accepted options" list because it would be awfully 3389251881Speter redundant to display it in every commands' help text. */ 3390251881Speter if (opt_id == 'h' || opt_id == '?') 3391251881Speter continue; 3392251881Speter 3393362181Sdim if (! svn_opt_subcommand_takes_option4(subcommand, opt_id, NULL)) 3394251881Speter { 3395251881Speter const char *optstr; 3396251881Speter const apr_getopt_option_t *badopt = 3397362181Sdim svn_opt_get_option_from_code3(opt_id, options_table, subcommand, 3398251881Speter pool); 3399251881Speter svn_opt_format_option(&optstr, badopt, FALSE, pool); 3400251881Speter if (subcommand->name[0] == '-') 3401289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 3402251881Speter else 3403251881Speter svn_error_clear(svn_cmdline_fprintf(stderr, pool 3404251881Speter , _("Subcommand '%s' doesn't accept option '%s'\n" 3405251881Speter "Type 'svnadmin help %s' for usage.\n"), 3406251881Speter subcommand->name, optstr, subcommand->name)); 3407289180Speter *exit_code = EXIT_FAILURE; 3408289180Speter return SVN_NO_ERROR; 3409251881Speter } 3410251881Speter } 3411251881Speter 3412362181Sdim check_cancel = svn_cmdline__setup_cancellation_handler(); 3413251881Speter 3414251881Speter /* Configure FSFS caches for maximum efficiency with svnadmin. 3415251881Speter * Also, apply the respective command line parameters, if given. */ 3416251881Speter { 3417251881Speter svn_cache_config_t settings = *svn_cache_config_get(); 3418251881Speter 3419251881Speter settings.cache_size = opt_state.memory_cache_size; 3420251881Speter settings.single_threaded = TRUE; 3421251881Speter 3422251881Speter svn_cache_config_set(&settings); 3423251881Speter } 3424251881Speter 3425251881Speter /* Run the subcommand. */ 3426251881Speter err = (*subcommand->cmd_func)(os, &opt_state, pool); 3427251881Speter if (err) 3428251881Speter { 3429251881Speter /* For argument-related problems, suggest using the 'help' 3430251881Speter subcommand. */ 3431251881Speter if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS 3432251881Speter || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR) 3433251881Speter { 3434251881Speter err = svn_error_quick_wrap(err, 3435251881Speter _("Try 'svnadmin help' for more info")); 3436251881Speter } 3437289180Speter return err; 3438251881Speter } 3439289180Speter 3440289180Speter return SVN_NO_ERROR; 3441251881Speter} 3442251881Speter 3443251881Speterint 3444251881Spetermain(int argc, const char *argv[]) 3445251881Speter{ 3446251881Speter apr_pool_t *pool; 3447289180Speter int exit_code = EXIT_SUCCESS; 3448289180Speter svn_error_t *err; 3449251881Speter 3450251881Speter /* Initialize the app. */ 3451251881Speter if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS) 3452251881Speter return EXIT_FAILURE; 3453251881Speter 3454251881Speter /* Create our top-level pool. Use a separate mutexless allocator, 3455251881Speter * given this application is single threaded. 3456251881Speter */ 3457251881Speter pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 3458251881Speter 3459289180Speter err = sub_main(&exit_code, argc, argv, pool); 3460251881Speter 3461289180Speter /* Flush stdout and report if it fails. It would be flushed on exit anyway 3462289180Speter but this makes sure that output is not silently lost if it fails. */ 3463289180Speter err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); 3464289180Speter 3465289180Speter if (err) 3466289180Speter { 3467289180Speter exit_code = EXIT_FAILURE; 3468289180Speter svn_cmdline_handle_exit_error(err, NULL, "svnadmin: "); 3469289180Speter } 3470289180Speter 3471251881Speter svn_pool_destroy(pool); 3472362181Sdim 3473362181Sdim svn_cmdline__cancellation_exit(); 3474362181Sdim 3475251881Speter return exit_code; 3476251881Speter} 3477