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