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. */
36 static 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. */
41 static svn_error_t *
str_to_item_type(unsigned * type,const char * str)42 str_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 */
61 static svn_error_t *
token_to_i64(apr_int64_t * value_p,apr_array_header_t * tokens,int idx,int radix)62 token_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 */
95 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)96 parse_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. */
129 static svn_error_t *
load_index(const char * path,svn_stream_t * input,apr_pool_t * pool)130 load_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 svn_fs_fs__ioctl_load_index_input_t ioctl_input = {0};
139
140 /* Check repository type and open it. */
141 SVN_ERR(open_fs(&fs, path, pool));
142
143 while (TRUE)
144 {
145 svn_stringbuf_t *line;
146 svn_fs_fs__p2l_entry_t *entry;
147 svn_boolean_t eol;
148
149 /* Get the next line from the input and stop if there is none. */
150 svn_pool_clear(iterpool);
151 SVN_ERR(svn_stream_readline(input, &line, "\n", &eol, iterpool));
152 if (eol)
153 break;
154
155 /* Skip header line(s). They contain the sub-string [Ss]tart. */
156 if (strstr(line->data, "tart"))
157 continue;
158
159 /* Ignore empty lines (mostly trailing ones but we don't really care).
160 */
161 svn_stringbuf_strip_whitespace(line);
162 if (line->len == 0)
163 continue;
164
165 /* Parse the entry and append it to ENTRIES. */
166 SVN_ERR(parse_index_line(&entry, line, pool, iterpool));
167 APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = entry;
168
169 /* There should be at least one item that is not empty.
170 * Get a revision from (probably inside) the respective shard. */
171 if ( revision == SVN_INVALID_REVNUM
172 && entry->item.revision != SVN_INVALID_REVNUM)
173 revision = entry->item.revision;
174 }
175
176 /* Rewrite the indexes. */
177 ioctl_input.revision = revision;
178 ioctl_input.entries = entries;
179 SVN_ERR(svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_LOAD_INDEX, &ioctl_input, NULL,
180 NULL, NULL, pool, pool));
181 svn_pool_destroy(iterpool);
182
183 return SVN_NO_ERROR;
184 }
185
186 /* This implements `svn_opt_subcommand_t'. */
187 svn_error_t *
subcommand__load_index(apr_getopt_t * os,void * baton,apr_pool_t * pool)188 subcommand__load_index(apr_getopt_t *os, void *baton, apr_pool_t *pool)
189 {
190 svnfsfs__opt_state *opt_state = baton;
191 svn_stream_t *input;
192
193 SVN_ERR(svn_stream_for_stdin2(&input, TRUE, pool));
194 SVN_ERR(load_index(opt_state->repository_path, input, pool));
195
196 return SVN_NO_ERROR;
197 }
198