2 * svnfsfs.c: FSFS repository manipulation tool main file.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #include "svn_pools.h"
25 #include "svn_cmdline.h"
29 #include "svn_dirent_uri.h"
30 #include "svn_repos.h"
31 #include "svn_cache_config.h"
32 #include "svn_version.h"
34 #include "private/svn_cmdline_private.h"
36 #include "svn_private_config.h"
43 svn_cancel_func_t check_cancel = NULL;
45 /* Custom filesystem warning function. */
47 warning_func(void *baton,
52 svn_handle_warning2(stderr, err, "svnfsfs: ");
56 /* Version compatibility check */
58 check_lib_versions(void)
60 static const svn_version_checklist_t checklist[] =
62 { "svn_subr", svn_subr_version },
63 { "svn_repos", svn_repos_version },
64 { "svn_fs", svn_fs_version },
65 { "svn_delta", svn_delta_version },
68 SVN_VERSION_DEFINE(my_version);
70 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
77 enum svnfsfs__cmdline_options_t
79 svnfsfs__version = SVN_OPT_FIRST_LONGOPT_ID
82 /* Option codes and descriptions.
84 * The entire list must be terminated with an entry of nulls.
86 static const apr_getopt_option_t options_table[] =
89 N_("show help on a subcommand")},
92 N_("show help on a subcommand")},
94 {"version", svnfsfs__version, 0,
95 N_("show program version information")},
98 N_("no progress (only errors to stderr)")},
101 N_("specify revision number ARG (or X:Y range)")},
103 {"memory-cache-size", 'M', 1,
104 N_("size of the extra in-memory cache in MB used to\n"
105 " minimize redundant operations. Default: 16.")},
111 /* Array of available subcommands.
112 * The entire list must be terminated with an entry of nulls.
114 static const svn_opt_subcommand_desc2_t cmd_table[] =
116 {"help", subcommand__help, {"?", "h"}, N_
117 ("usage: svnfsfs help [SUBCOMMAND...]\n\n"
118 "Describe the usage of this program or its subcommands.\n"),
121 {"dump-index", subcommand__dump_index, {0}, N_
122 ("usage: svnfsfs dump-index REPOS_PATH -r REV\n\n"
123 "Dump the index contents for the revision / pack file containing revision REV\n"
124 "to console. This is only available for FSFS format 7 (SVN 1.9+) repositories.\n"
125 "The table produced contains a header in the first line followed by one line\n"
126 "per index entry, ordered by location in the revision / pack file. Columns:\n\n"
127 " * Byte offset (hex) at which the item starts\n"
128 " * Length (hex) of the item in bytes\n"
129 " * Item type (string) is one of the following:\n\n"
130 " none ... Unused section. File contents shall be NULs.\n"
131 " frep ... File representation.\n"
132 " drep ... Directory representation.\n"
133 " fprop .. File property.\n"
134 " dprop .. Directory property.\n"
135 " node ... Node revision.\n"
136 " chgs ... Changed paths list.\n"
137 " rep .... Representation of unknown type. Should not be used.\n"
138 " ??? .... Invalid. Index data is corrupt.\n\n"
139 " The distinction between frep, drep, fprop and dprop is a mere internal\n"
140 " classification used for various optimizations and does not affect the\n"
141 " operational correctness.\n\n"
142 " * Revision that the item belongs to (decimal)\n"
143 " * Item number (decimal) within that revision\n"
144 " * Modified FNV1a checksum (8 hex digits)\n"),
147 {"load-index", subcommand__load_index, {0}, N_
148 ("usage: svnfsfs load-index REPOS_PATH\n\n"
149 "Read index contents from console. The format is the same as produced by the\n"
150 "dump-index command, except that checksum as well as header are optional and will\n"
151 "be ignored. The data must cover the full revision / pack file; the revision\n"
152 "number is automatically extracted from input stream. No ordering is required.\n"),
155 {"stats", subcommand__stats, {0}, N_
156 ("usage: svnfsfs stats REPOS_PATH\n\n"
157 "Write object size statistics to console.\n"),
160 { NULL, NULL, {0}, NULL, {0} }
165 open_fs(svn_fs_t **fs,
171 /* Verify that we can handle the repository type. */
172 path = svn_dirent_join(path, "db", pool);
173 SVN_ERR(svn_fs_type(&fs_type, path, pool));
174 if (strcmp(fs_type, SVN_FS_TYPE_FSFS))
175 return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_TYPE, NULL,
176 _("%s repositories are not supported"),
180 SVN_ERR(svn_fs_open2(fs, path, NULL, pool, pool));
181 svn_fs_set_warning_func(*fs, warning_func, NULL);
186 /* This implements `svn_opt_subcommand_t'. */
188 subcommand__help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
190 svnfsfs__opt_state *opt_state = baton;
192 _("general usage: svnfsfs SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n"
193 "Subversion FSFS repository manipulation tool.\n"
194 "Type 'svnfsfs help <subcommand>' for help on a specific subcommand.\n"
195 "Type 'svnfsfs --version' to see the program version.\n"
197 "Available subcommands:\n");
199 SVN_ERR(svn_opt_print_help4(os, "svnfsfs",
200 opt_state ? opt_state->version : FALSE,
201 opt_state ? opt_state->quiet : FALSE,
202 /*###opt_state ? opt_state->verbose :*/ FALSE,
204 header, cmd_table, options_table, NULL, NULL,
214 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
215 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
216 * return SVN_NO_ERROR.
219 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
222 apr_status_t apr_err;
224 const svn_opt_subcommand_desc2_t *subcommand = NULL;
225 svnfsfs__opt_state opt_state = { 0 };
228 apr_array_header_t *received_opts;
231 received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
233 /* Check library versions */
234 SVN_ERR(check_lib_versions());
236 /* Initialize the FS library. */
237 SVN_ERR(svn_fs_initialize(pool));
241 SVN_ERR(subcommand__help(NULL, NULL, pool));
242 *exit_code = EXIT_FAILURE;
246 /* Initialize opt_state. */
247 opt_state.start_revision.kind = svn_opt_revision_unspecified;
248 opt_state.end_revision.kind = svn_opt_revision_unspecified;
249 opt_state.memory_cache_size = svn_cache_config_get()->cache_size;
252 SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
259 const char *utf8_opt_arg;
261 /* Parse the next option. */
262 apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg);
263 if (APR_STATUS_IS_EOF(apr_err))
267 SVN_ERR(subcommand__help(NULL, NULL, pool));
268 *exit_code = EXIT_FAILURE;
272 /* Stash the option code in an array before parsing it. */
273 APR_ARRAY_PUSH(received_opts, int) = opt_id;
278 if (opt_state.start_revision.kind != svn_opt_revision_unspecified)
280 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
281 _("Multiple revision arguments encountered; "
282 "try '-r N:M' instead of '-r N -r M'"));
284 if (svn_opt_parse_revision(&(opt_state.start_revision),
285 &(opt_state.end_revision),
288 SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
290 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
291 _("Syntax error in revision argument '%s'"),
297 opt_state.quiet = TRUE;
301 opt_state.help = TRUE;
306 SVN_ERR(svn_cstring_atoui64(&sz_val, opt_arg));
308 opt_state.memory_cache_size = 0x100000 * sz_val;
311 case svnfsfs__version:
312 opt_state.version = TRUE;
316 SVN_ERR(subcommand__help(NULL, NULL, pool));
317 *exit_code = EXIT_FAILURE;
320 } /* close `switch' */
321 } /* close `while' */
323 /* If the user asked for help, then the rest of the arguments are
324 the names of subcommands to get help on (if any), or else they're
325 just typos/mistakes. Whatever the case, the subcommand to
326 actually run is subcommand_help(). */
328 subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");
330 /* If we're not running the `help' subcommand, then look for a
331 subcommand in the first argument. */
332 if (subcommand == NULL)
334 if (os->ind >= os->argc)
336 if (opt_state.version)
338 /* Use the "help" subcommand to handle the "--version" option. */
339 static const svn_opt_subcommand_desc2_t pseudo_cmd =
340 { "--version", subcommand__help, {0}, "",
341 {svnfsfs__version, /* must accept its own option */
345 subcommand = &pseudo_cmd;
349 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
350 _("subcommand argument required\n")));
351 SVN_ERR(subcommand__help(NULL, NULL, pool));
352 *exit_code = EXIT_FAILURE;
358 const char *first_arg;
360 SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
362 subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
363 if (subcommand == NULL)
366 svn_cmdline_fprintf(stderr, pool,
367 _("Unknown subcommand: '%s'\n"),
369 SVN_ERR(subcommand__help(NULL, NULL, pool));
370 *exit_code = EXIT_FAILURE;
376 /* Every subcommand except `help' requires a second argument -- the
377 repository path. Parse it out here and store it in opt_state. */
378 if (!(subcommand->cmd_func == subcommand__help))
380 const char *repos_path = NULL;
382 if (os->ind >= os->argc)
384 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
385 _("Repository argument required"));
388 SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool));
390 if (svn_path_is_url(repos_path))
392 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
393 _("'%s' is a URL when it should be a "
394 "local path"), repos_path);
397 opt_state.repository_path = svn_dirent_internal_style(repos_path, pool);
400 /* Check that the subcommand wasn't passed any inappropriate options. */
401 for (i = 0; i < received_opts->nelts; i++)
403 opt_id = APR_ARRAY_IDX(received_opts, i, int);
405 /* All commands implicitly accept --help, so just skip over this
406 when we see it. Note that we don't want to include this option
407 in their "accepted options" list because it would be awfully
408 redundant to display it in every commands' help text. */
409 if (opt_id == 'h' || opt_id == '?')
412 if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
415 const apr_getopt_option_t *badopt =
416 svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
418 svn_opt_format_option(&optstr, badopt, FALSE, pool);
419 if (subcommand->name[0] == '-')
420 SVN_ERR(subcommand__help(NULL, NULL, pool));
422 svn_error_clear(svn_cmdline_fprintf(stderr, pool
423 , _("Subcommand '%s' doesn't accept option '%s'\n"
424 "Type 'svnfsfs help %s' for usage.\n"),
425 subcommand->name, optstr, subcommand->name));
426 *exit_code = EXIT_FAILURE;
431 /* Set up our cancellation support. */
432 check_cancel = svn_cmdline__setup_cancellation_handler();
434 /* Configure FSFS caches for maximum efficiency with svnfsfs.
435 * Also, apply the respective command line parameters, if given. */
437 svn_cache_config_t settings = *svn_cache_config_get();
439 settings.cache_size = opt_state.memory_cache_size;
440 settings.single_threaded = TRUE;
442 svn_cache_config_set(&settings);
445 /* Run the subcommand. */
446 err = (*subcommand->cmd_func)(os, &opt_state, pool);
449 /* For argument-related problems, suggest using the 'help'
451 if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
452 || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
454 err = svn_error_quick_wrap(err,
455 _("Try 'svnfsfs help' for more info"));
464 main(int argc, const char *argv[])
467 int exit_code = EXIT_SUCCESS;
470 /* Initialize the app. */
471 if (svn_cmdline_init("svnfsfs", stderr) != EXIT_SUCCESS)
474 /* Create our top-level pool. Use a separate mutexless allocator,
475 * given this application is single threaded.
477 pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
479 err = sub_main(&exit_code, argc, argv, pool);
481 /* Flush stdout and report if it fails. It would be flushed on exit anyway
482 but this makes sure that output is not silently lost if it fails. */
483 err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
487 exit_code = EXIT_FAILURE;
488 svn_cmdline_handle_exit_error(err, NULL, "svnfsfs: ");
491 svn_pool_destroy(pool);
493 svn_cmdline__cancellation_exit();