]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/svnfsfs/svnfsfs.c
Update svn-1.9.7 to 1.10.0.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / svnfsfs / svnfsfs.c
1 /*
2  * svnfsfs.c: FSFS repository manipulation tool main file.
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include "svn_pools.h"
25 #include "svn_cmdline.h"
26 #include "svn_opt.h"
27 #include "svn_utf.h"
28 #include "svn_path.h"
29 #include "svn_dirent_uri.h"
30 #include "svn_repos.h"
31 #include "svn_cache_config.h"
32 #include "svn_version.h"
33
34 #include "private/svn_cmdline_private.h"
35
36 #include "svn_private_config.h"
37
38 #include "svnfsfs.h"
39
40 \f
41 /*** Code. ***/
42
43 svn_cancel_func_t check_cancel = NULL;
44
45 /* Custom filesystem warning function. */
46 static void
47 warning_func(void *baton,
48              svn_error_t *err)
49 {
50   if (! err)
51     return;
52   svn_handle_warning2(stderr, err, "svnfsfs: ");
53 }
54
55
56 /* Version compatibility check */
57 static svn_error_t *
58 check_lib_versions(void)
59 {
60   static const svn_version_checklist_t checklist[] =
61     {
62       { "svn_subr",  svn_subr_version },
63       { "svn_repos", svn_repos_version },
64       { "svn_fs",    svn_fs_version },
65       { "svn_delta", svn_delta_version },
66       { NULL, NULL }
67     };
68   SVN_VERSION_DEFINE(my_version);
69
70   return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
71 }
72
73
74 \f
75 /** Subcommands. **/
76
77 enum svnfsfs__cmdline_options_t
78   {
79     svnfsfs__version = SVN_OPT_FIRST_LONGOPT_ID
80   };
81
82 /* Option codes and descriptions.
83  *
84  * The entire list must be terminated with an entry of nulls.
85  */
86 static const apr_getopt_option_t options_table[] =
87   {
88     {"help",          'h', 0,
89      N_("show help on a subcommand")},
90
91     {NULL,            '?', 0,
92      N_("show help on a subcommand")},
93
94     {"version",       svnfsfs__version, 0,
95      N_("show program version information")},
96
97     {"quiet",         'q', 0,
98      N_("no progress (only errors to stderr)")},
99
100     {"revision",      'r', 1,
101      N_("specify revision number ARG (or X:Y range)")},
102
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.")},
106
107     {NULL}
108   };
109
110
111 /* Array of available subcommands.
112  * The entire list must be terminated with an entry of nulls.
113  */
114 static const svn_opt_subcommand_desc2_t cmd_table[] =
115 {
116   {"help", subcommand__help, {"?", "h"}, N_
117    ("usage: svnfsfs help [SUBCOMMAND...]\n\n"
118     "Describe the usage of this program or its subcommands.\n"),
119    {0} },
120
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"),
145    {'r', 'M'} },
146
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"),
153    {'M'} },
154
155   {"stats", subcommand__stats, {0}, N_
156    ("usage: svnfsfs stats REPOS_PATH\n\n"
157     "Write object size statistics to console.\n"),
158    {'M'} },
159
160   { NULL, NULL, {0}, NULL, {0} }
161 };
162
163
164 svn_error_t *
165 open_fs(svn_fs_t **fs,
166         const char *path,
167         apr_pool_t *pool)
168 {
169   const char *fs_type;
170
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"),
177                              fs_type);
178
179   /* Now open it. */
180   SVN_ERR(svn_fs_open2(fs, path, NULL, pool, pool));
181   svn_fs_set_warning_func(*fs, warning_func, NULL);
182
183   return SVN_NO_ERROR;
184 }
185
186 /* This implements `svn_opt_subcommand_t'. */
187 svn_error_t *
188 subcommand__help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
189 {
190   svnfsfs__opt_state *opt_state = baton;
191   const char *header =
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"
196       "\n"
197       "Available subcommands:\n");
198
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,
203                               NULL,
204                               header, cmd_table, options_table, NULL, NULL,
205                               pool));
206
207   return SVN_NO_ERROR;
208 }
209
210 \f
211 /** Main. **/
212
213 /*
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.
217  */
218 static svn_error_t *
219 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
220 {
221   svn_error_t *err;
222   apr_status_t apr_err;
223
224   const svn_opt_subcommand_desc2_t *subcommand = NULL;
225   svnfsfs__opt_state opt_state = { 0 };
226   apr_getopt_t *os;
227   int opt_id;
228   apr_array_header_t *received_opts;
229   int i;
230
231   received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
232
233   /* Check library versions */
234   SVN_ERR(check_lib_versions());
235
236   /* Initialize the FS library. */
237   SVN_ERR(svn_fs_initialize(pool));
238
239   if (argc <= 1)
240     {
241       SVN_ERR(subcommand__help(NULL, NULL, pool));
242       *exit_code = EXIT_FAILURE;
243       return SVN_NO_ERROR;
244     }
245
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;
250
251   /* Parse options. */
252   SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
253
254   os->interleave = 1;
255
256   while (1)
257     {
258       const char *opt_arg;
259       const char *utf8_opt_arg;
260
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))
264         break;
265       else if (apr_err)
266         {
267           SVN_ERR(subcommand__help(NULL, NULL, pool));
268           *exit_code = EXIT_FAILURE;
269           return SVN_NO_ERROR;
270         }
271
272       /* Stash the option code in an array before parsing it. */
273       APR_ARRAY_PUSH(received_opts, int) = opt_id;
274
275       switch (opt_id) {
276       case 'r':
277         {
278           if (opt_state.start_revision.kind != svn_opt_revision_unspecified)
279             {
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'"));
283             }
284           if (svn_opt_parse_revision(&(opt_state.start_revision),
285                                      &(opt_state.end_revision),
286                                      opt_arg, pool) != 0)
287             {
288               SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
289
290               return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
291                         _("Syntax error in revision argument '%s'"),
292                         utf8_opt_arg);
293             }
294         }
295         break;
296       case 'q':
297         opt_state.quiet = TRUE;
298         break;
299       case 'h':
300       case '?':
301         opt_state.help = TRUE;
302         break;
303       case 'M':
304         {
305           apr_uint64_t sz_val;
306           SVN_ERR(svn_cstring_atoui64(&sz_val, opt_arg));
307
308           opt_state.memory_cache_size = 0x100000 * sz_val;
309         }
310         break;
311       case svnfsfs__version:
312         opt_state.version = TRUE;
313         break;
314       default:
315         {
316           SVN_ERR(subcommand__help(NULL, NULL, pool));
317           *exit_code = EXIT_FAILURE;
318           return SVN_NO_ERROR;
319         }
320       }  /* close `switch' */
321     }  /* close `while' */
322
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(). */
327   if (opt_state.help)
328     subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");
329
330   /* If we're not running the `help' subcommand, then look for a
331      subcommand in the first argument. */
332   if (subcommand == NULL)
333     {
334       if (os->ind >= os->argc)
335         {
336           if (opt_state.version)
337             {
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 */
342                    'q',  /* --quiet */
343                   } };
344
345               subcommand = &pseudo_cmd;
346             }
347           else
348             {
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;
353               return SVN_NO_ERROR;
354             }
355         }
356       else
357         {
358           const char *first_arg;
359
360           SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
361                                           pool));
362           subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
363           if (subcommand == NULL)
364             {
365               svn_error_clear(
366                 svn_cmdline_fprintf(stderr, pool,
367                                     _("Unknown subcommand: '%s'\n"),
368                                     first_arg));
369               SVN_ERR(subcommand__help(NULL, NULL, pool));
370               *exit_code = EXIT_FAILURE;
371               return SVN_NO_ERROR;
372             }
373         }
374     }
375
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))
379     {
380       const char *repos_path = NULL;
381
382       if (os->ind >= os->argc)
383         {
384           return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
385                                   _("Repository argument required"));
386         }
387
388       SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool));
389
390       if (svn_path_is_url(repos_path))
391         {
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);
395         }
396
397       opt_state.repository_path = svn_dirent_internal_style(repos_path, pool);
398     }
399
400   /* Check that the subcommand wasn't passed any inappropriate options. */
401   for (i = 0; i < received_opts->nelts; i++)
402     {
403       opt_id = APR_ARRAY_IDX(received_opts, i, int);
404
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 == '?')
410         continue;
411
412       if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
413         {
414           const char *optstr;
415           const apr_getopt_option_t *badopt =
416             svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
417                                           pool);
418           svn_opt_format_option(&optstr, badopt, FALSE, pool);
419           if (subcommand->name[0] == '-')
420             SVN_ERR(subcommand__help(NULL, NULL, pool));
421           else
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;
427           return SVN_NO_ERROR;
428         }
429     }
430
431   /* Set up our cancellation support. */
432   check_cancel = svn_cmdline__setup_cancellation_handler();
433
434   /* Configure FSFS caches for maximum efficiency with svnfsfs.
435    * Also, apply the respective command line parameters, if given. */
436   {
437     svn_cache_config_t settings = *svn_cache_config_get();
438
439     settings.cache_size = opt_state.memory_cache_size;
440     settings.single_threaded = TRUE;
441
442     svn_cache_config_set(&settings);
443   }
444
445   /* Run the subcommand. */
446   err = (*subcommand->cmd_func)(os, &opt_state, pool);
447   if (err)
448     {
449       /* For argument-related problems, suggest using the 'help'
450          subcommand. */
451       if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
452           || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
453         {
454           err = svn_error_quick_wrap(err,
455                                      _("Try 'svnfsfs help' for more info"));
456         }
457       return err;
458     }
459
460   return SVN_NO_ERROR;
461 }
462
463 int
464 main(int argc, const char *argv[])
465 {
466   apr_pool_t *pool;
467   int exit_code = EXIT_SUCCESS;
468   svn_error_t *err;
469
470   /* Initialize the app. */
471   if (svn_cmdline_init("svnfsfs", stderr) != EXIT_SUCCESS)
472     return EXIT_FAILURE;
473
474   /* Create our top-level pool.  Use a separate mutexless allocator,
475    * given this application is single threaded.
476    */
477   pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
478
479   err = sub_main(&exit_code, argc, argv, pool);
480
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));
484
485   if (err)
486     {
487       exit_code = EXIT_FAILURE;
488       svn_cmdline_handle_exit_error(err, NULL, "svnfsfs: ");
489     }
490
491   svn_pool_destroy(pool);
492
493   svn_cmdline__cancellation_exit();
494
495   return exit_code;
496 }