diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/svnfsfs | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-cf46733632c7279a9fd0fe6ce26f9185a4ae82a9.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/svnfsfs')
-rw-r--r-- | subversion/svnfsfs/dump-index-cmd.c | 106 | ||||
-rw-r--r-- | subversion/svnfsfs/load-index-cmd.c | 193 | ||||
-rw-r--r-- | subversion/svnfsfs/stats-cmd.c | 509 | ||||
-rw-r--r-- | subversion/svnfsfs/svnfsfs.c | 541 | ||||
-rw-r--r-- | subversion/svnfsfs/svnfsfs.h | 73 |
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 */ |