1/* load-index-cmd.c -- implements the dump-index sub-command. 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include "svn_ctype.h" 24#include "svn_dirent_uri.h" 25#include "svn_io.h" 26#include "svn_pools.h" 27 28#include "private/svn_fs_fs_private.h" 29#include "private/svn_sorts_private.h" 30 31#include "svn_private_config.h" 32 33#include "svnfsfs.h" 34 35/* Map svn_fs_fs__p2l_entry_t.type to C string. */ 36static const char *item_type_str[] 37 = {"none", "frep", "drep", "fprop", "dprop", "node", "chgs", "rep"}; 38 39/* Reverse lookup in ITEM_TYPE_STR: Set *TYPE to the index that contains STR. 40 * Return an error for invalid strings. */ 41static svn_error_t * 42str_to_item_type(unsigned *type, 43 const char *str) 44{ 45 unsigned i; 46 for (i = 0; i < sizeof(item_type_str) / sizeof(item_type_str[0]); ++i) 47 if (strcmp(item_type_str[i], str) == 0) 48 { 49 *type = i; 50 return SVN_NO_ERROR; 51 } 52 53 return svn_error_createf(SVN_ERR_BAD_TOKEN, NULL, 54 _("Unknown item type '%s'"), str); 55} 56 57/* Parse the string given as const char * at IDX in TOKENS and return its 58 * value in *VALUE_P. Assume that the string an integer with base RADIX. 59 * Check for index overflows and non-hex chars. 60 */ 61static svn_error_t * 62token_to_i64(apr_int64_t *value_p, 63 apr_array_header_t *tokens, 64 int idx, 65 int radix) 66{ 67 const char *hex; 68 char *end; 69 apr_int64_t value; 70 71 /* Tell the user when there is not enough information. */ 72 SVN_ERR_ASSERT(idx >= 0); 73 if (tokens->nelts <= idx) 74 return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, 75 _("%i columns needed, %i provided"), 76 idx + 1, tokens->nelts); 77 78 /* hex -> int conversion */ 79 hex = APR_ARRAY_IDX(tokens, idx, const char *); 80 value = apr_strtoi64(hex, &end, radix); 81 82 /* Has the whole token be parsed without error? */ 83 if (errno || *end != '\0') 84 return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, 85 _("%s is not a value HEX string"), hex); 86 87 *value_p = value; 88 return SVN_NO_ERROR; 89} 90 91/* Parse the P2L entry given as space separated values in LINE and return it 92 * in *ENTRY. Ignore extra columns. Allocate the result in RESULT_POOL and 93 * use SCRATCH_POOL for temporaries. 94 */ 95static svn_error_t * 96parse_index_line(svn_fs_fs__p2l_entry_t **entry, 97 svn_stringbuf_t *line, 98 apr_pool_t *result_pool, 99 apr_pool_t *scratch_pool) 100{ 101 apr_array_header_t *tokens = svn_cstring_split(line->data, " ", TRUE, 102 scratch_pool); 103 svn_fs_fs__p2l_entry_t *result = apr_pcalloc(result_pool, sizeof(*result)); 104 apr_int64_t value; 105 106 /* Parse the hex columns. */ 107 SVN_ERR(token_to_i64(&value, tokens, 0, 16)); 108 result->offset = (apr_off_t)value; 109 SVN_ERR(token_to_i64(&value, tokens, 1, 16)); 110 result->size = (apr_off_t)value; 111 112 /* Parse the rightmost colum that we care of. */ 113 SVN_ERR(token_to_i64(&value, tokens, 4, 10)); 114 result->item.number = (apr_uint64_t)value; 115 116 /* We now know that there were at least 5 columns. 117 * Parse the non-hex columns without index check. */ 118 SVN_ERR(str_to_item_type(&result->type, 119 APR_ARRAY_IDX(tokens, 2, const char *))); 120 SVN_ERR(svn_revnum_parse(&result->item.revision, 121 APR_ARRAY_IDX(tokens, 3, const char *), NULL)); 122 123 *entry = result; 124 return SVN_NO_ERROR; 125} 126 127/* Parse the space separated P2L index table from INPUT, one entry per line. 128 * Rewrite the respective index files in PATH. Allocate from POOL. */ 129static svn_error_t * 130load_index(const char *path, 131 svn_stream_t *input, 132 apr_pool_t *pool) 133{ 134 svn_fs_t *fs; 135 svn_revnum_t revision = SVN_INVALID_REVNUM; 136 apr_array_header_t *entries = apr_array_make(pool, 16, sizeof(void*)); 137 apr_pool_t *iterpool = svn_pool_create(pool); 138 139 /* Check repository type and open it. */ 140 SVN_ERR(open_fs(&fs, path, pool)); 141 142 while (TRUE) 143 { 144 svn_stringbuf_t *line; 145 svn_fs_fs__p2l_entry_t *entry; 146 svn_boolean_t eol; 147 148 /* Get the next line from the input and stop if there is none. */ 149 svn_pool_clear(iterpool); 150 svn_stream_readline(input, &line, "\n", &eol, iterpool); 151 if (eol) 152 break; 153 154 /* Skip header line(s). They contain the sub-string [Ss]tart. */ 155 if (strstr(line->data, "tart")) 156 continue; 157 158 /* Ignore empty lines (mostly trailing ones but we don't really care). 159 */ 160 svn_stringbuf_strip_whitespace(line); 161 if (line->len == 0) 162 continue; 163 164 /* Parse the entry and append it to ENTRIES. */ 165 SVN_ERR(parse_index_line(&entry, line, pool, iterpool)); 166 APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = entry; 167 168 /* There should be at least one item that is not empty. 169 * Get a revision from (probably inside) the respective shard. */ 170 if ( revision == SVN_INVALID_REVNUM 171 && entry->item.revision != SVN_INVALID_REVNUM) 172 revision = entry->item.revision; 173 } 174 175 /* Rewrite the indexes. */ 176 SVN_ERR(svn_fs_fs__load_index(fs, revision, entries, iterpool)); 177 svn_pool_destroy(iterpool); 178 179 return SVN_NO_ERROR; 180} 181 182/* This implements `svn_opt_subcommand_t'. */ 183svn_error_t * 184subcommand__load_index(apr_getopt_t *os, void *baton, apr_pool_t *pool) 185{ 186 svnfsfs__opt_state *opt_state = baton; 187 svn_stream_t *input; 188 189 SVN_ERR(svn_stream_for_stdin(&input, pool)); 190 SVN_ERR(load_index(opt_state->repository_path, input, pool)); 191 192 return SVN_NO_ERROR; 193} 194