summaryrefslogtreecommitdiff
path: root/subversion/svnfsfs
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
commitcf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch)
treeda27775a2161723ef342e91af41a8b51fedef405 /subversion/svnfsfs
parentbb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff)
downloadsubversion-tarball-cf46733632c7279a9fd0fe6ce26f9185a4ae82a9.tar.gz
Diffstat (limited to 'subversion/svnfsfs')
-rw-r--r--subversion/svnfsfs/dump-index-cmd.c106
-rw-r--r--subversion/svnfsfs/load-index-cmd.c193
-rw-r--r--subversion/svnfsfs/stats-cmd.c509
-rw-r--r--subversion/svnfsfs/svnfsfs.c541
-rw-r--r--subversion/svnfsfs/svnfsfs.h73
5 files changed, 1422 insertions, 0 deletions
diff --git a/subversion/svnfsfs/dump-index-cmd.c b/subversion/svnfsfs/dump-index-cmd.c
new file mode 100644
index 0000000..5598c98
--- /dev/null
+++ b/subversion/svnfsfs/dump-index-cmd.c
@@ -0,0 +1,106 @@
+/* dump-index-cmd.c -- implements the dump-index sub-command.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#define APR_WANT_BYTEFUNC
+
+#include "svn_dirent_uri.h"
+#include "svn_pools.h"
+#include "private/svn_fs_fs_private.h"
+
+#include "svnfsfs.h"
+
+/* Return the 8 digit hex string for FNVV1, allocated in POOL.
+ */
+static const char *
+fnv1_to_string(apr_uint32_t fnv1,
+ apr_pool_t *pool)
+{
+ /* Construct a checksum object containing FNV1. */
+ svn_checksum_t checksum = { NULL, svn_checksum_fnv1a_32 };
+ apr_uint32_t digest = htonl(fnv1);
+ checksum.digest = (const unsigned char *)&digest;
+
+ /* Convert the digest to hex. */
+ return svn_checksum_to_cstring_display(&checksum, pool);
+}
+
+/* Map svn_fs_fs__p2l_entry_t.type to C string. */
+static const char *item_type_str[]
+ = {"none ", "frep ", "drep ", "fprop", "dprop", "node ", "chgs ", "rep "};
+
+/* Implements svn_fs_fs__dump_index_func_t as printing one table row
+ * containing the fields of ENTRY to the console.
+ */
+static svn_error_t *
+dump_index_entry(const svn_fs_fs__p2l_entry_t *entry,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ const char *type_str
+ = entry->type < (sizeof(item_type_str) / sizeof(item_type_str[0]))
+ ? item_type_str[entry->type]
+ : "???";
+
+ printf("%12" APR_UINT64_T_HEX_FMT " %12" APR_UINT64_T_HEX_FMT
+ " %s %9ld %8" APR_UINT64_T_FMT " %s\n",
+ (apr_uint64_t)entry->offset, (apr_uint64_t)entry->size,
+ type_str, entry->item.revision, entry->item.number,
+ fnv1_to_string(entry->fnv1_checksum, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Read the repository at PATH beginning with revision START_REVISION and
+ * return the result in *FS. Allocate caches with MEMSIZE bytes total
+ * capacity. Use POOL for non-cache allocations.
+ */
+static svn_error_t *
+dump_index(const char *path,
+ svn_revnum_t revision,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+
+ /* Check repository type and open it. */
+ SVN_ERR(open_fs(&fs, path, pool));
+
+ /* Write header line. */
+ printf(" Start Length Type Revision Item Checksum\n");
+
+ /* Dump the whole index contents */
+ SVN_ERR(svn_fs_fs__dump_index(fs, revision, dump_index_entry, NULL,
+ check_cancel, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+svn_error_t *
+subcommand__dump_index(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ svnfsfs__opt_state *opt_state = baton;
+
+ SVN_ERR(dump_index(opt_state->repository_path,
+ opt_state->start_revision.value.number, pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnfsfs/load-index-cmd.c b/subversion/svnfsfs/load-index-cmd.c
new file mode 100644
index 0000000..4df86bd
--- /dev/null
+++ b/subversion/svnfsfs/load-index-cmd.c
@@ -0,0 +1,193 @@
+/* load-index-cmd.c -- implements the dump-index sub-command.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include "svn_ctype.h"
+#include "svn_dirent_uri.h"
+#include "svn_io.h"
+#include "svn_pools.h"
+
+#include "private/svn_fs_fs_private.h"
+#include "private/svn_sorts_private.h"
+
+#include "svn_private_config.h"
+
+#include "svnfsfs.h"
+
+/* Map svn_fs_fs__p2l_entry_t.type to C string. */
+static const char *item_type_str[]
+ = {"none", "frep", "drep", "fprop", "dprop", "node", "chgs", "rep"};
+
+/* Reverse lookup in ITEM_TYPE_STR: Set *TYPE to the index that contains STR.
+ * Return an error for invalid strings. */
+static svn_error_t *
+str_to_item_type(unsigned *type,
+ const char *str)
+{
+ unsigned i;
+ for (i = 0; i < sizeof(item_type_str) / sizeof(item_type_str[0]); ++i)
+ if (strcmp(item_type_str[i], str) == 0)
+ {
+ *type = i;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_createf(SVN_ERR_BAD_TOKEN, NULL,
+ _("Unknown item type '%s'"), str);
+}
+
+/* Parse the string given as const char * at IDX in TOKENS and return its
+ * value in *VALUE_P. Assume that the string an integer with base RADIX.
+ * Check for index overflows and non-hex chars.
+ */
+static svn_error_t *
+token_to_i64(apr_int64_t *value_p,
+ apr_array_header_t *tokens,
+ int idx,
+ int radix)
+{
+ const char *hex;
+ char *end;
+ apr_int64_t value;
+
+ /* Tell the user when there is not enough information. */
+ SVN_ERR_ASSERT(idx >= 0);
+ if (tokens->nelts <= idx)
+ return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL,
+ _("%i columns needed, %i provided"),
+ idx + 1, tokens->nelts);
+
+ /* hex -> int conversion */
+ hex = APR_ARRAY_IDX(tokens, idx, const char *);
+ value = apr_strtoi64(hex, &end, radix);
+
+ /* Has the whole token be parsed without error? */
+ if (errno || *end != '\0')
+ return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL,
+ _("%s is not a value HEX string"), hex);
+
+ *value_p = value;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the P2L entry given as space separated values in LINE and return it
+ * in *ENTRY. Ignore extra columns. Allocate the result in RESULT_POOL and
+ * use SCRATCH_POOL for temporaries.
+ */
+static svn_error_t *
+parse_index_line(svn_fs_fs__p2l_entry_t **entry,
+ svn_stringbuf_t *line,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *tokens = svn_cstring_split(line->data, " ", TRUE,
+ scratch_pool);
+ svn_fs_fs__p2l_entry_t *result = apr_pcalloc(result_pool, sizeof(*result));
+ apr_int64_t value;
+
+ /* Parse the hex columns. */
+ SVN_ERR(token_to_i64(&value, tokens, 0, 16));
+ result->offset = (apr_off_t)value;
+ SVN_ERR(token_to_i64(&value, tokens, 1, 16));
+ result->size = (apr_off_t)value;
+
+ /* Parse the rightmost colum that we care of. */
+ SVN_ERR(token_to_i64(&value, tokens, 4, 10));
+ result->item.number = (apr_uint64_t)value;
+
+ /* We now know that there were at least 5 columns.
+ * Parse the non-hex columns without index check. */
+ SVN_ERR(str_to_item_type(&result->type,
+ APR_ARRAY_IDX(tokens, 2, const char *)));
+ SVN_ERR(svn_revnum_parse(&result->item.revision,
+ APR_ARRAY_IDX(tokens, 3, const char *), NULL));
+
+ *entry = result;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the space separated P2L index table from INPUT, one entry per line.
+ * Rewrite the respective index files in PATH. Allocate from POOL. */
+static svn_error_t *
+load_index(const char *path,
+ svn_stream_t *input,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_revnum_t revision = SVN_INVALID_REVNUM;
+ apr_array_header_t *entries = apr_array_make(pool, 16, sizeof(void*));
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ /* Check repository type and open it. */
+ SVN_ERR(open_fs(&fs, path, pool));
+
+ while (TRUE)
+ {
+ svn_stringbuf_t *line;
+ svn_fs_fs__p2l_entry_t *entry;
+ svn_boolean_t eol;
+
+ /* Get the next line from the input and stop if there is none. */
+ svn_pool_clear(iterpool);
+ svn_stream_readline(input, &line, "\n", &eol, iterpool);
+ if (eol)
+ break;
+
+ /* Skip header line(s). They contain the sub-string [Ss]tart. */
+ if (strstr(line->data, "tart"))
+ continue;
+
+ /* Ignore empty lines (mostly trailing ones but we don't really care).
+ */
+ svn_stringbuf_strip_whitespace(line);
+ if (line->len == 0)
+ continue;
+
+ /* Parse the entry and append it to ENTRIES. */
+ SVN_ERR(parse_index_line(&entry, line, pool, iterpool));
+ APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = entry;
+
+ /* There should be at least one item that is not empty.
+ * Get a revision from (probably inside) the respective shard. */
+ if ( revision == SVN_INVALID_REVNUM
+ && entry->item.revision != SVN_INVALID_REVNUM)
+ revision = entry->item.revision;
+ }
+
+ /* Rewrite the indexes. */
+ SVN_ERR(svn_fs_fs__load_index(fs, revision, entries, iterpool));
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+svn_error_t *
+subcommand__load_index(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ svnfsfs__opt_state *opt_state = baton;
+ svn_stream_t *input;
+
+ SVN_ERR(svn_stream_for_stdin(&input, pool));
+ SVN_ERR(load_index(opt_state->repository_path, input, pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnfsfs/stats-cmd.c b/subversion/svnfsfs/stats-cmd.c
new file mode 100644
index 0000000..6e820cb
--- /dev/null
+++ b/subversion/svnfsfs/stats-cmd.c
@@ -0,0 +1,509 @@
+/* stats-cmd.c -- implements the size stats sub-command.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <assert.h>
+
+#include "svn_fs.h"
+#include "svn_pools.h"
+#include "svn_sorts.h"
+
+#include "private/svn_sorts_private.h"
+#include "private/svn_string_private.h"
+#include "private/svn_fs_fs_private.h"
+
+#include "svn_private_config.h"
+#include "svnfsfs.h"
+
+/* Return the string, allocated in RESULT_POOL, describing the value 2**I.
+ */
+static const char *
+print_two_power(int i,
+ apr_pool_t *result_pool)
+{
+ /* These are the SI prefixes for base-1000, the binary ones with base-1024
+ are too clumsy and require appending B for "byte" to be intelligible,
+ e.g. "MiB".
+
+ Therefore, we ignore the official standard and revert to the traditional
+ contextual use were the base-1000 prefixes are understood as base-1024
+ when it came to data sizes.
+ */
+ const char *si_prefixes = " kMGTPEZY";
+
+ int number = (i >= 0) ? (1 << (i % 10)) : 0;
+ int thousands = (i >= 0) ? (i / 10) : 0;
+
+ char si_prefix = (thousands < strlen(si_prefixes))
+ ? si_prefixes[thousands]
+ : '?';
+
+ if (si_prefix == ' ')
+ return apr_psprintf(result_pool, "%d", number);
+
+ return apr_psprintf(result_pool, "%d%c", number, si_prefix);
+}
+
+/* Print statistics for the given group of representations to console.
+ * Use POOL for allocations.
+ */
+static void
+print_rep_stats(svn_fs_fs__representation_stats_t *stats,
+ apr_pool_t *pool)
+{
+ printf(_("%20s bytes in %12s reps\n"
+ "%20s bytes in %12s shared reps\n"
+ "%20s bytes expanded size\n"
+ "%20s bytes expanded shared size\n"
+ "%20s bytes with rep-sharing off\n"
+ "%20s shared references\n"),
+ svn__ui64toa_sep(stats->total.packed_size, ',', pool),
+ svn__ui64toa_sep(stats->total.count, ',', pool),
+ svn__ui64toa_sep(stats->shared.packed_size, ',', pool),
+ svn__ui64toa_sep(stats->shared.count, ',', pool),
+ svn__ui64toa_sep(stats->total.expanded_size, ',', pool),
+ svn__ui64toa_sep(stats->shared.expanded_size, ',', pool),
+ svn__ui64toa_sep(stats->expanded_size, ',', pool),
+ svn__ui64toa_sep(stats->references - stats->total.count, ',', pool));
+}
+
+/* Print the (used) contents of CHANGES. Use POOL for allocations.
+ */
+static void
+print_largest_reps(svn_fs_fs__largest_changes_t *changes,
+ apr_pool_t *pool)
+{
+ apr_size_t i;
+ for (i = 0; i < changes->count && changes->changes[i]->size; ++i)
+ printf(_("%12s r%-8ld %s\n"),
+ svn__ui64toa_sep(changes->changes[i]->size, ',', pool),
+ changes->changes[i]->revision,
+ changes->changes[i]->path->data);
+}
+
+/* Print the non-zero section of HISTOGRAM to console.
+ * Use POOL for allocations.
+ */
+static void
+print_histogram(svn_fs_fs__histogram_t *histogram,
+ apr_pool_t *pool)
+{
+ int first = 0;
+ int last = 63;
+ int i;
+
+ /* identify non-zero range */
+ while (last > 0 && histogram->lines[last].count == 0)
+ --last;
+
+ while (first <= last && histogram->lines[first].count == 0)
+ ++first;
+
+ /* display histogram lines */
+ for (i = last; i >= first; --i)
+ printf(_(" %4s .. < %-4s %19s (%2d%%) bytes in %12s (%2d%%) items\n"),
+ print_two_power(i-1, pool), print_two_power(i, pool),
+ svn__ui64toa_sep(histogram->lines[i].sum, ',', pool),
+ (int)(histogram->lines[i].sum * 100 / histogram->total.sum),
+ svn__ui64toa_sep(histogram->lines[i].count, ',', pool),
+ (int)(histogram->lines[i].count * 100 / histogram->total.count));
+}
+
+/* COMPARISON_FUNC for svn_sort__hash.
+ * Sort extension_info_t values by total count in descending order.
+ */
+static int
+compare_count(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ const svn_fs_fs__extension_info_t *lhs = a->value;
+ const svn_fs_fs__extension_info_t *rhs = b->value;
+ apr_int64_t diff = lhs->node_histogram.total.count
+ - rhs->node_histogram.total.count;
+
+ return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
+}
+
+/* COMPARISON_FUNC for svn_sort__hash.
+ * Sort extension_info_t values by total uncompressed size in descending order.
+ */
+static int
+compare_node_size(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ const svn_fs_fs__extension_info_t *lhs = a->value;
+ const svn_fs_fs__extension_info_t *rhs = b->value;
+ apr_int64_t diff = lhs->node_histogram.total.sum
+ - rhs->node_histogram.total.sum;
+
+ return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
+}
+
+/* COMPARISON_FUNC for svn_sort__hash.
+ * Sort extension_info_t values by total prep count in descending order.
+ */
+static int
+compare_rep_size(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ const svn_fs_fs__extension_info_t *lhs = a->value;
+ const svn_fs_fs__extension_info_t *rhs = b->value;
+ apr_int64_t diff = lhs->rep_histogram.total.sum
+ - rhs->rep_histogram.total.sum;
+
+ return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
+}
+
+/* Return an array of extension_info_t* for the (up to) 16 most prominent
+ * extensions in STATS according to the sort criterion COMPARISON_FUNC.
+ * Allocate results in POOL.
+ */
+static apr_array_header_t *
+get_by_extensions(svn_fs_fs__stats_t *stats,
+ int (*comparison_func)(const svn_sort__item_t *,
+ const svn_sort__item_t *),
+ apr_pool_t *pool)
+{
+ /* sort all data by extension */
+ apr_array_header_t *sorted
+ = svn_sort__hash(stats->by_extension, comparison_func, pool);
+
+ /* select the top (first) 16 entries */
+ int count = MIN(sorted->nelts, 16);
+ apr_array_header_t *result
+ = apr_array_make(pool, count, sizeof(svn_fs_fs__extension_info_t*));
+ int i;
+
+ for (i = 0; i < count; ++i)
+ APR_ARRAY_PUSH(result, svn_fs_fs__extension_info_t*)
+ = APR_ARRAY_IDX(sorted, i, svn_sort__item_t).value;
+
+ return result;
+}
+
+/* Add all extension_info_t* entries of TO_ADD not already in TARGET to
+ * TARGET.
+ */
+static void
+merge_by_extension(apr_array_header_t *target,
+ apr_array_header_t *to_add)
+{
+ int i, k, count;
+
+ count = target->nelts;
+ for (i = 0; i < to_add->nelts; ++i)
+ {
+ svn_fs_fs__extension_info_t *info
+ = APR_ARRAY_IDX(to_add, i, svn_fs_fs__extension_info_t *);
+ for (k = 0; k < count; ++k)
+ if (info == APR_ARRAY_IDX(target, k, svn_fs_fs__extension_info_t *))
+ break;
+
+ if (k == count)
+ APR_ARRAY_PUSH(target, svn_fs_fs__extension_info_t*) = info;
+ }
+}
+
+/* Print the (up to) 16 extensions in STATS with the most changes.
+ * Use POOL for allocations.
+ */
+static void
+print_extensions_by_changes(svn_fs_fs__stats_t *stats,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *data = get_by_extensions(stats, compare_count, pool);
+ apr_int64_t sum = 0;
+ int i;
+
+ for (i = 0; i < data->nelts; ++i)
+ {
+ svn_fs_fs__extension_info_t *info
+ = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
+
+ /* If there are elements, then their count cannot be 0. */
+ assert(stats->file_histogram.total.count);
+
+ sum += info->node_histogram.total.count;
+ printf(_("%11s %20s (%2d%%) representations\n"),
+ info->extension,
+ svn__ui64toa_sep(info->node_histogram.total.count, ',', pool),
+ (int)(info->node_histogram.total.count * 100 /
+ stats->file_histogram.total.count));
+ }
+
+ if (stats->file_histogram.total.count)
+ {
+ printf(_("%11s %20s (%2d%%) representations\n"),
+ "(others)",
+ svn__ui64toa_sep(stats->file_histogram.total.count - sum, ',',
+ pool),
+ (int)((stats->file_histogram.total.count - sum) * 100 /
+ stats->file_histogram.total.count));
+ }
+}
+
+/* Calculate a percentage, handling edge cases. */
+static int
+get_percentage(apr_uint64_t part,
+ apr_uint64_t total)
+{
+ /* This include total == 0. */
+ if (part >= total)
+ return 100;
+
+ /* Standard case. */
+ return (int)(part * 100.0 / total);
+}
+
+/* Print the (up to) 16 extensions in STATS with the largest total size of
+ * changed file content. Use POOL for allocations.
+ */
+static void
+print_extensions_by_nodes(svn_fs_fs__stats_t *stats,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *data = get_by_extensions(stats, compare_node_size, pool);
+ apr_int64_t sum = 0;
+ int i;
+
+ for (i = 0; i < data->nelts; ++i)
+ {
+ svn_fs_fs__extension_info_t *info
+ = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
+ sum += info->node_histogram.total.sum;
+ printf(_("%11s %20s (%2d%%) bytes\n"),
+ info->extension,
+ svn__ui64toa_sep(info->node_histogram.total.sum, ',', pool),
+ get_percentage(info->node_histogram.total.sum,
+ stats->file_histogram.total.sum));
+ }
+
+ if (stats->file_histogram.total.sum > sum)
+ {
+ /* Total sum can't be zero here. */
+ printf(_("%11s %20s (%2d%%) bytes\n"),
+ "(others)",
+ svn__ui64toa_sep(stats->file_histogram.total.sum - sum, ',',
+ pool),
+ get_percentage(stats->file_histogram.total.sum - sum,
+ stats->file_histogram.total.sum));
+ }
+}
+
+/* Print the (up to) 16 extensions in STATS with the largest total size of
+ * changed file content. Use POOL for allocations.
+ */
+static void
+print_extensions_by_reps(svn_fs_fs__stats_t *stats,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *data = get_by_extensions(stats, compare_rep_size, pool);
+ apr_int64_t sum = 0;
+ int i;
+
+ for (i = 0; i < data->nelts; ++i)
+ {
+ svn_fs_fs__extension_info_t *info
+ = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
+ sum += info->rep_histogram.total.sum;
+ printf(_("%11s %20s (%2d%%) bytes\n"),
+ info->extension,
+ svn__ui64toa_sep(info->rep_histogram.total.sum, ',', pool),
+ get_percentage(info->rep_histogram.total.sum,
+ stats->rep_size_histogram.total.sum));
+ }
+
+ if (stats->rep_size_histogram.total.sum > sum)
+ {
+ /* Total sum can't be zero here. */
+ printf(_("%11s %20s (%2d%%) bytes\n"),
+ "(others)",
+ svn__ui64toa_sep(stats->rep_size_histogram.total.sum - sum, ',',
+ pool),
+ get_percentage(stats->rep_size_histogram.total.sum - sum,
+ stats->rep_size_histogram.total.sum));
+ }
+}
+
+/* Print per-extension histograms for the most frequent extensions in STATS.
+ * Use POOL for allocations. */
+static void
+print_histograms_by_extension(svn_fs_fs__stats_t *stats,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *data = get_by_extensions(stats, compare_count, pool);
+ int i;
+
+ merge_by_extension(data, get_by_extensions(stats, compare_node_size, pool));
+ merge_by_extension(data, get_by_extensions(stats, compare_rep_size, pool));
+
+ for (i = 0; i < data->nelts; ++i)
+ {
+ svn_fs_fs__extension_info_t *info
+ = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
+ printf("\nHistogram of '%s' file sizes:\n", info->extension);
+ print_histogram(&info->node_histogram, pool);
+ printf("\nHistogram of '%s' file representation sizes:\n",
+ info->extension);
+ print_histogram(&info->rep_histogram, pool);
+ }
+}
+
+/* Print the contents of STATS to the console.
+ * Use POOL for allocations.
+ */
+static void
+print_stats(svn_fs_fs__stats_t *stats,
+ apr_pool_t *pool)
+{
+ /* print results */
+ printf("\nGlobal statistics:\n");
+ printf(_("%20s bytes in %12s revisions\n"
+ "%20s bytes in %12s changes\n"
+ "%20s bytes in %12s node revision records\n"
+ "%20s bytes in %12s representations\n"
+ "%20s bytes expanded representation size\n"
+ "%20s bytes with rep-sharing off\n"),
+ svn__ui64toa_sep(stats->total_size, ',', pool),
+ svn__ui64toa_sep(stats->revision_count, ',', pool),
+ svn__ui64toa_sep(stats->change_len, ',', pool),
+ svn__ui64toa_sep(stats->change_count, ',', pool),
+ svn__ui64toa_sep(stats->total_node_stats.size, ',', pool),
+ svn__ui64toa_sep(stats->total_node_stats.count, ',', pool),
+ svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool),
+ svn__ui64toa_sep(stats->total_rep_stats.total.expanded_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->total_rep_stats.expanded_size, ',', pool));
+
+ printf("\nNoderev statistics:\n");
+ printf(_("%20s bytes in %12s nodes total\n"
+ "%20s bytes in %12s directory noderevs\n"
+ "%20s bytes in %12s file noderevs\n"),
+ svn__ui64toa_sep(stats->total_node_stats.size, ',', pool),
+ svn__ui64toa_sep(stats->total_node_stats.count, ',', pool),
+ svn__ui64toa_sep(stats->dir_node_stats.size, ',', pool),
+ svn__ui64toa_sep(stats->dir_node_stats.count, ',', pool),
+ svn__ui64toa_sep(stats->file_node_stats.size, ',', pool),
+ svn__ui64toa_sep(stats->file_node_stats.count, ',', pool));
+
+ printf("\nRepresentation statistics:\n");
+ printf(_("%20s bytes in %12s representations total\n"
+ "%20s bytes in %12s directory representations\n"
+ "%20s bytes in %12s file representations\n"
+ "%20s bytes in %12s representations of added file nodes\n"
+ "%20s bytes in %12s directory property representations\n"
+ "%20s bytes in %12s file property representations\n"
+ "%20s bytes in header & footer overhead\n"),
+ svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool),
+ svn__ui64toa_sep(stats->dir_rep_stats.total.packed_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->dir_rep_stats.total.count, ',', pool),
+ svn__ui64toa_sep(stats->file_rep_stats.total.packed_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->file_rep_stats.total.count, ',', pool),
+ svn__ui64toa_sep(stats->added_rep_size_histogram.total.sum, ',',
+ pool),
+ svn__ui64toa_sep(stats->added_rep_size_histogram.total.count, ',',
+ pool),
+ svn__ui64toa_sep(stats->dir_prop_rep_stats.total.packed_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->dir_prop_rep_stats.total.count, ',', pool),
+ svn__ui64toa_sep(stats->file_prop_rep_stats.total.packed_size, ',',
+ pool),
+ svn__ui64toa_sep(stats->file_prop_rep_stats.total.count, ',', pool),
+ svn__ui64toa_sep(stats->total_rep_stats.total.overhead_size, ',',
+ pool));
+
+ printf("\nDirectory representation statistics:\n");
+ print_rep_stats(&stats->dir_rep_stats, pool);
+ printf("\nFile representation statistics:\n");
+ print_rep_stats(&stats->file_rep_stats, pool);
+ printf("\nDirectory property representation statistics:\n");
+ print_rep_stats(&stats->dir_prop_rep_stats, pool);
+ printf("\nFile property representation statistics:\n");
+ print_rep_stats(&stats->file_prop_rep_stats, pool);
+
+ printf("\nLargest representations:\n");
+ print_largest_reps(stats->largest_changes, pool);
+ printf("\nExtensions by number of representations:\n");
+ print_extensions_by_changes(stats, pool);
+ printf("\nExtensions by size of changed files:\n");
+ print_extensions_by_nodes(stats, pool);
+ printf("\nExtensions by size of representations:\n");
+ print_extensions_by_reps(stats, pool);
+
+ printf("\nHistogram of expanded node sizes:\n");
+ print_histogram(&stats->node_size_histogram, pool);
+ printf("\nHistogram of representation sizes:\n");
+ print_histogram(&stats->rep_size_histogram, pool);
+ printf("\nHistogram of file sizes:\n");
+ print_histogram(&stats->file_histogram, pool);
+ printf("\nHistogram of file representation sizes:\n");
+ print_histogram(&stats->file_rep_histogram, pool);
+ printf("\nHistogram of file property sizes:\n");
+ print_histogram(&stats->file_prop_histogram, pool);
+ printf("\nHistogram of file property representation sizes:\n");
+ print_histogram(&stats->file_prop_rep_histogram, pool);
+ printf("\nHistogram of directory sizes:\n");
+ print_histogram(&stats->dir_histogram, pool);
+ printf("\nHistogram of directory representation sizes:\n");
+ print_histogram(&stats->dir_rep_histogram, pool);
+ printf("\nHistogram of directory property sizes:\n");
+ print_histogram(&stats->dir_prop_histogram, pool);
+ printf("\nHistogram of directory property representation sizes:\n");
+ print_histogram(&stats->dir_prop_rep_histogram, pool);
+
+ print_histograms_by_extension(stats, pool);
+}
+
+/* Our progress function simply prints the REVISION number and makes it
+ * appear immediately.
+ */
+static void
+print_progress(svn_revnum_t revision,
+ void *baton,
+ apr_pool_t *pool)
+{
+ printf("%8ld", revision);
+ fflush(stdout);
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+svn_error_t *
+subcommand__stats(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ svnfsfs__opt_state *opt_state = baton;
+ svn_fs_fs__stats_t *stats;
+ svn_fs_t *fs;
+
+ printf("Reading revisions\n");
+ SVN_ERR(open_fs(&fs, opt_state->repository_path, pool));
+ SVN_ERR(svn_fs_fs__get_stats(&stats, fs, print_progress, NULL,
+ check_cancel, NULL, pool, pool));
+
+ print_stats(stats, pool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnfsfs/svnfsfs.c b/subversion/svnfsfs/svnfsfs.c
new file mode 100644
index 0000000..f8b1e4b
--- /dev/null
+++ b/subversion/svnfsfs/svnfsfs.c
@@ -0,0 +1,541 @@
+/*
+ * svnfsfs.c: FSFS repository manipulation tool main file.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <apr_signal.h>
+
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_opt.h"
+#include "svn_utf.h"
+#include "svn_path.h"
+#include "svn_dirent_uri.h"
+#include "svn_repos.h"
+#include "svn_cache_config.h"
+#include "svn_version.h"
+
+#include "private/svn_cmdline_private.h"
+
+#include "svn_private_config.h"
+
+#include "svnfsfs.h"
+
+
+/*** Code. ***/
+
+/* A flag to see if we've been cancelled by the client or not. */
+static volatile sig_atomic_t cancelled = FALSE;
+
+/* A signal handler to support cancellation. */
+static void
+signal_handler(int signum)
+{
+ apr_signal(signum, SIG_IGN);
+ cancelled = TRUE;
+}
+
+
+/* A helper to set up the cancellation signal handlers. */
+static void
+setup_cancellation_signals(void (*handler)(int signum))
+{
+ apr_signal(SIGINT, handler);
+#ifdef SIGBREAK
+ /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
+ apr_signal(SIGBREAK, handler);
+#endif
+#ifdef SIGHUP
+ apr_signal(SIGHUP, handler);
+#endif
+#ifdef SIGTERM
+ apr_signal(SIGTERM, handler);
+#endif
+}
+
+
+svn_error_t *
+check_cancel(void *baton)
+{
+ if (cancelled)
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
+ else
+ return SVN_NO_ERROR;
+}
+
+
+/* Custom filesystem warning function. */
+static void
+warning_func(void *baton,
+ svn_error_t *err)
+{
+ if (! err)
+ return;
+ svn_handle_warning2(stderr, err, "svnfsfs: ");
+}
+
+
+/* Version compatibility check */
+static svn_error_t *
+check_lib_versions(void)
+{
+ static const svn_version_checklist_t checklist[] =
+ {
+ { "svn_subr", svn_subr_version },
+ { "svn_repos", svn_repos_version },
+ { "svn_fs", svn_fs_version },
+ { "svn_delta", svn_delta_version },
+ { NULL, NULL }
+ };
+ SVN_VERSION_DEFINE(my_version);
+
+ return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
+}
+
+
+
+/** Subcommands. **/
+
+enum svnfsfs__cmdline_options_t
+ {
+ svnfsfs__version = SVN_OPT_FIRST_LONGOPT_ID
+ };
+
+/* Option codes and descriptions.
+ *
+ * The entire list must be terminated with an entry of nulls.
+ */
+static const apr_getopt_option_t options_table[] =
+ {
+ {"help", 'h', 0,
+ N_("show help on a subcommand")},
+
+ {NULL, '?', 0,
+ N_("show help on a subcommand")},
+
+ {"version", svnfsfs__version, 0,
+ N_("show program version information")},
+
+ {"quiet", 'q', 0,
+ N_("no progress (only errors to stderr)")},
+
+ {"revision", 'r', 1,
+ N_("specify revision number ARG (or X:Y range)")},
+
+ {"memory-cache-size", 'M', 1,
+ N_("size of the extra in-memory cache in MB used to\n"
+ " minimize redundant operations. Default: 16.")},
+
+ {NULL}
+ };
+
+
+/* Array of available subcommands.
+ * The entire list must be terminated with an entry of nulls.
+ */
+static const svn_opt_subcommand_desc2_t cmd_table[] =
+{
+ {"help", subcommand__help, {"?", "h"}, N_
+ ("usage: svnfsfs help [SUBCOMMAND...]\n\n"
+ "Describe the usage of this program or its subcommands.\n"),
+ {0} },
+
+ {"dump-index", subcommand__dump_index, {0}, N_
+ ("usage: svnfsfs dump-index REPOS_PATH -r REV\n\n"
+ "Dump the index contents for the revision / pack file containing revision REV\n"
+ "to console. This is only available for FSFS format 7 (SVN 1.9+) repositories.\n"
+ "The table produced contains a header in the first line followed by one line\n"
+ "per index entry, ordered by location in the revision / pack file. Columns:\n\n"
+ " * Byte offset (hex) at which the item starts\n"
+ " * Length (hex) of the item in bytes\n"
+ " * Item type (string) is one of the following:\n\n"
+ " none ... Unused section. File contents shall be NULs.\n"
+ " frep ... File representation.\n"
+ " drep ... Directory representation.\n"
+ " fprop .. File property.\n"
+ " dprop .. Directory property.\n"
+ " node ... Node revision.\n"
+ " chgs ... Changed paths list.\n"
+ " rep .... Representation of unknown type. Should not be used.\n"
+ " ??? .... Invalid. Index data is corrupt.\n\n"
+ " The distinction between frep, drep, fprop and dprop is a mere internal\n"
+ " classification used for various optimizations and does not affect the\n"
+ " operational correctness.\n\n"
+ " * Revision that the item belongs to (decimal)\n"
+ " * Item number (decimal) within that revision\n"
+ " * Modified FNV1a checksum (8 hex digits)\n"),
+ {'r', 'M'} },
+
+ {"load-index", subcommand__load_index, {0}, N_
+ ("usage: svnfsfs load-index REPOS_PATH\n\n"
+ "Read index contents from console. The format is the same as produced by the\n"
+ "dump-index command, except that checksum as well as header are optional and will\n"
+ "be ignored. The data must cover the full revision / pack file; the revision\n"
+ "number is automatically extracted from input stream. No ordering is required.\n"),
+ {'M'} },
+
+ {"stats", subcommand__stats, {0}, N_
+ ("usage: svnfsfs stats REPOS_PATH\n\n"
+ "Write object size statistics to console.\n"),
+ {'M'} },
+
+ { NULL, NULL, {0}, NULL, {0} }
+};
+
+
+svn_error_t *
+open_fs(svn_fs_t **fs,
+ const char *path,
+ apr_pool_t *pool)
+{
+ const char *fs_type;
+
+ /* Verify that we can handle the repository type. */
+ path = svn_dirent_join(path, "db", pool);
+ SVN_ERR(svn_fs_type(&fs_type, path, pool));
+ if (strcmp(fs_type, SVN_FS_TYPE_FSFS))
+ return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_TYPE, NULL,
+ _("%s repositories are not supported"),
+ fs_type);
+
+ /* Now open it. */
+ SVN_ERR(svn_fs_open2(fs, path, NULL, pool, pool));
+ svn_fs_set_warning_func(*fs, warning_func, NULL);
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+svn_error_t *
+subcommand__help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ svnfsfs__opt_state *opt_state = baton;
+ const char *header =
+ _("general usage: svnfsfs SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n"
+ "Subversion FSFS repository manipulation tool.\n"
+ "Type 'svnfsfs help <subcommand>' for help on a specific subcommand.\n"
+ "Type 'svnfsfs --version' to see the program version.\n"
+ "\n"
+ "Available subcommands:\n");
+
+ SVN_ERR(svn_opt_print_help4(os, "svnfsfs",
+ opt_state ? opt_state->version : FALSE,
+ opt_state ? opt_state->quiet : FALSE,
+ /*###opt_state ? opt_state->verbose :*/ FALSE,
+ NULL,
+ header, cmd_table, options_table, NULL, NULL,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+/** Main. **/
+
+/*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ */
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
+{
+ svn_error_t *err;
+ apr_status_t apr_err;
+
+ const svn_opt_subcommand_desc2_t *subcommand = NULL;
+ svnfsfs__opt_state opt_state = { 0 };
+ apr_getopt_t *os;
+ int opt_id;
+ apr_array_header_t *received_opts;
+ int i;
+
+ received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
+
+ /* Check library versions */
+ SVN_ERR(check_lib_versions());
+
+ /* Initialize the FS library. */
+ SVN_ERR(svn_fs_initialize(pool));
+
+ if (argc <= 1)
+ {
+ SVN_ERR(subcommand__help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
+
+ /* Initialize opt_state. */
+ opt_state.start_revision.kind = svn_opt_revision_unspecified;
+ opt_state.end_revision.kind = svn_opt_revision_unspecified;
+ opt_state.memory_cache_size = svn_cache_config_get()->cache_size;
+
+ /* Parse options. */
+ SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
+
+ os->interleave = 1;
+
+ while (1)
+ {
+ const char *opt_arg;
+ const char *utf8_opt_arg;
+
+ /* Parse the next option. */
+ apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg);
+ if (APR_STATUS_IS_EOF(apr_err))
+ break;
+ else if (apr_err)
+ {
+ SVN_ERR(subcommand__help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
+
+ /* Stash the option code in an array before parsing it. */
+ APR_ARRAY_PUSH(received_opts, int) = opt_id;
+
+ switch (opt_id) {
+ case 'r':
+ {
+ if (opt_state.start_revision.kind != svn_opt_revision_unspecified)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Multiple revision arguments encountered; "
+ "try '-r N:M' instead of '-r N -r M'"));
+ }
+ if (svn_opt_parse_revision(&(opt_state.start_revision),
+ &(opt_state.end_revision),
+ opt_arg, pool) != 0)
+ {
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
+
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Syntax error in revision argument '%s'"),
+ utf8_opt_arg);
+ }
+ }
+ break;
+ case 'q':
+ opt_state.quiet = TRUE;
+ break;
+ case 'h':
+ case '?':
+ opt_state.help = TRUE;
+ break;
+ case 'M':
+ opt_state.memory_cache_size
+ = 0x100000 * apr_strtoi64(opt_arg, NULL, 0);
+ break;
+ case svnfsfs__version:
+ opt_state.version = TRUE;
+ break;
+ default:
+ {
+ SVN_ERR(subcommand__help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
+ } /* close `switch' */
+ } /* close `while' */
+
+ /* If the user asked for help, then the rest of the arguments are
+ the names of subcommands to get help on (if any), or else they're
+ just typos/mistakes. Whatever the case, the subcommand to
+ actually run is subcommand_help(). */
+ if (opt_state.help)
+ subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");
+
+ /* If we're not running the `help' subcommand, then look for a
+ subcommand in the first argument. */
+ if (subcommand == NULL)
+ {
+ if (os->ind >= os->argc)
+ {
+ if (opt_state.version)
+ {
+ /* Use the "help" subcommand to handle the "--version" option. */
+ static const svn_opt_subcommand_desc2_t pseudo_cmd =
+ { "--version", subcommand__help, {0}, "",
+ {svnfsfs__version, /* must accept its own option */
+ 'q', /* --quiet */
+ } };
+
+ subcommand = &pseudo_cmd;
+ }
+ else
+ {
+ svn_error_clear(svn_cmdline_fprintf(stderr, pool,
+ _("subcommand argument required\n")));
+ SVN_ERR(subcommand__help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
+ }
+ else
+ {
+ const char *first_arg = os->argv[os->ind++];
+ subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
+ if (subcommand == NULL)
+ {
+ const char *first_arg_utf8;
+ SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
+ first_arg, pool));
+ svn_error_clear(
+ svn_cmdline_fprintf(stderr, pool,
+ _("Unknown subcommand: '%s'\n"),
+ first_arg_utf8));
+ SVN_ERR(subcommand__help(NULL, NULL, pool));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
+ }
+ }
+
+ /* Every subcommand except `help' requires a second argument -- the
+ repository path. Parse it out here and store it in opt_state. */
+ if (!(subcommand->cmd_func == subcommand__help))
+ {
+ const char *repos_path = NULL;
+
+ if (os->ind >= os->argc)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Repository argument required"));
+ }
+
+ SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool));
+
+ if (svn_path_is_url(repos_path))
+ {
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is a URL when it should be a "
+ "local path"), repos_path);
+ }
+
+ opt_state.repository_path = svn_dirent_internal_style(repos_path, pool);
+ }
+
+ /* Check that the subcommand wasn't passed any inappropriate options. */
+ for (i = 0; i < received_opts->nelts; i++)
+ {
+ opt_id = APR_ARRAY_IDX(received_opts, i, int);
+
+ /* All commands implicitly accept --help, so just skip over this
+ when we see it. Note that we don't want to include this option
+ in their "accepted options" list because it would be awfully
+ redundant to display it in every commands' help text. */
+ if (opt_id == 'h' || opt_id == '?')
+ continue;
+
+ if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
+ {
+ const char *optstr;
+ const apr_getopt_option_t *badopt =
+ svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
+ pool);
+ svn_opt_format_option(&optstr, badopt, FALSE, pool);
+ if (subcommand->name[0] == '-')
+ SVN_ERR(subcommand__help(NULL, NULL, pool));
+ else
+ svn_error_clear(svn_cmdline_fprintf(stderr, pool
+ , _("Subcommand '%s' doesn't accept option '%s'\n"
+ "Type 'svnfsfs help %s' for usage.\n"),
+ subcommand->name, optstr, subcommand->name));
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
+ }
+
+ /* Set up our cancellation support. */
+ setup_cancellation_signals(signal_handler);
+
+#ifdef SIGPIPE
+ /* Disable SIGPIPE generation for the platforms that have it. */
+ apr_signal(SIGPIPE, SIG_IGN);
+#endif
+
+#ifdef SIGXFSZ
+ /* Disable SIGXFSZ generation for the platforms that have it, otherwise
+ * working with large files when compiled against an APR that doesn't have
+ * large file support will crash the program, which is uncool. */
+ apr_signal(SIGXFSZ, SIG_IGN);
+#endif
+
+ /* Configure FSFS caches for maximum efficiency with svnfsfs.
+ * Also, apply the respective command line parameters, if given. */
+ {
+ svn_cache_config_t settings = *svn_cache_config_get();
+
+ settings.cache_size = opt_state.memory_cache_size;
+ settings.single_threaded = TRUE;
+
+ svn_cache_config_set(&settings);
+ }
+
+ /* Run the subcommand. */
+ err = (*subcommand->cmd_func)(os, &opt_state, pool);
+ if (err)
+ {
+ /* For argument-related problems, suggest using the 'help'
+ subcommand. */
+ if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
+ || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
+ {
+ err = svn_error_quick_wrap(err,
+ _("Try 'svnfsfs help' for more info"));
+ }
+ return err;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+int
+main(int argc, const char *argv[])
+{
+ apr_pool_t *pool;
+ int exit_code = EXIT_SUCCESS;
+ svn_error_t *err;
+
+ /* Initialize the app. */
+ if (svn_cmdline_init("svnfsfs", stderr) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+
+ /* Create our top-level pool. Use a separate mutexless allocator,
+ * given this application is single threaded.
+ */
+ pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+ err = sub_main(&exit_code, argc, argv, pool);
+
+ /* Flush stdout and report if it fails. It would be flushed on exit anyway
+ but this makes sure that output is not silently lost if it fails. */
+ err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+ if (err)
+ {
+ exit_code = EXIT_FAILURE;
+ svn_cmdline_handle_exit_error(err, NULL, "svnfsfs: ");
+ }
+
+ svn_pool_destroy(pool);
+ return exit_code;
+}
diff --git a/subversion/svnfsfs/svnfsfs.h b/subversion/svnfsfs/svnfsfs.h
new file mode 100644
index 0000000..132b2bc
--- /dev/null
+++ b/subversion/svnfsfs/svnfsfs.h
@@ -0,0 +1,73 @@
+/*
+ * svnfsfs.h: shared stuff in the command line program
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+#ifndef SVNFSFS_H
+#define SVNFSFS_H
+
+/*** Includes. ***/
+
+#include "svn_opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*** Command dispatch. ***/
+
+/* Baton for passing option/argument state to a subcommand function. */
+typedef struct svnfsfs__opt_state
+{
+ const char *repository_path;
+ svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */
+ svn_boolean_t help; /* --help or -? */
+ svn_boolean_t version; /* --version */
+ svn_boolean_t quiet; /* --quiet */
+ apr_uint64_t memory_cache_size; /* --memory-cache-size M */
+} svnfsfs__opt_state;
+
+/* Declare all the command procedures */
+svn_opt_subcommand_t
+ subcommand__help,
+ subcommand__dump_index,
+ subcommand__load_index,
+ subcommand__stats;
+
+
+/* Check that the filesystem at PATH is an FSFS repository and then open it.
+ * Return the filesystem in *FS, allocated in POOL. */
+svn_error_t *
+open_fs(svn_fs_t **fs,
+ const char *path,
+ apr_pool_t *pool);
+
+/* Our cancellation callback. */
+svn_error_t *
+check_cancel(void *baton);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVNFSFS_H */