1 /* svn-wc-db-tester.c
2 *
3 * This is a crude command line tool that makes it possible to
4 * run the wc-db validation checks directly.
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied. See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 */
25
26 #include "svn_cmdline.h"
27 #include "svn_pools.h"
28 #include "svn_wc.h"
29 #include "svn_utf.h"
30 #include "svn_path.h"
31 #include "svn_opt.h"
32 #include "svn_version.h"
33
34 #include "private/svn_wc_private.h"
35 #include "private/svn_cmdline_private.h"
36
37 #include "../../../subversion/libsvn_wc/wc.h"
38 #include "../../../subversion/libsvn_wc/wc_db.h"
39
40 #include "svn_private_config.h"
41
42 #define OPT_VERSION SVN_OPT_FIRST_LONGOPT_ID
43
44 static svn_error_t *
version(apr_pool_t * pool)45 version(apr_pool_t *pool)
46 {
47 return svn_opt_print_help5(NULL, "svn-wc-db-tester", TRUE, FALSE, FALSE,
48 NULL, NULL, NULL, NULL, NULL, NULL, pool);
49 }
50
51 static void
usage(apr_pool_t * pool)52 usage(apr_pool_t *pool)
53 {
54 svn_error_clear(svn_cmdline_fprintf
55 (stderr, pool,
56 _("Type 'svn-wc-db-tester --help' for usage.\n")));
57 }
58
59 struct verify_baton
60 {
61 svn_boolean_t found_err;
62 };
63
64 static svn_error_t *
verify_cb(void * baton,const char * wc_abspath,const char * local_relpath,int op_depth,int id,const char * msg,apr_pool_t * scratch_pool)65 verify_cb(void *baton,
66 const char *wc_abspath,
67 const char *local_relpath,
68 int op_depth,
69 int id,
70 const char *msg,
71 apr_pool_t *scratch_pool)
72 {
73 struct verify_baton *vb = baton;
74
75 if (op_depth >= 0)
76 {
77 SVN_ERR(svn_cmdline_printf(scratch_pool, "%s (depth=%d) DBV%04d: %s\n",
78 local_relpath, op_depth, id, msg));
79 }
80 else
81 {
82 SVN_ERR(svn_cmdline_printf(scratch_pool, "%s DBV%04d: %s\n",
83 local_relpath, id, msg));
84 }
85
86 vb->found_err = TRUE;
87 return SVN_NO_ERROR;
88 }
89
90 static svn_error_t *
verify_db(int argc,const char * path,apr_pool_t * pool)91 verify_db(int argc, const char *path, apr_pool_t *pool)
92 {
93 const char *local_abspath;
94 svn_wc_context_t *wc_ctx;
95 struct verify_baton vb = { FALSE };
96
97 /* Read the parameters */
98 path = svn_dirent_internal_style(path, pool);
99
100 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
101
102 SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool));
103
104 SVN_ERR(svn_wc__db_verify_db_full(wc_ctx->db, local_abspath,
105 verify_cb, &vb, pool));
106
107 if (vb.found_err)
108 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
109 _("Found one or more potential wc.db inconsistencies"));
110
111 return SVN_NO_ERROR;
112 }
113
114
115 static void
help(const apr_getopt_option_t * options,apr_pool_t * pool)116 help(const apr_getopt_option_t *options, apr_pool_t *pool)
117 {
118 svn_error_clear
119 (svn_cmdline_fprintf
120 (stdout, pool,
121 _("usage: svn-wc-db-tester [OPTIONS] WC_PATH\n\n"
122 " Run verifications on the working copy\n"
123 "\n"
124 " WC_PATH's parent directory must be a working copy, otherwise a\n"
125 " tree conflict cannot be raised.\n"
126 "\n"
127 "Valid options:\n")));
128 while (options->description)
129 {
130 const char *optstr;
131 svn_opt_format_option(&optstr, options, TRUE, pool);
132 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
133 ++options;
134 }
135 }
136
137
138 /* Version compatibility check */
139 static svn_error_t *
check_lib_versions(void)140 check_lib_versions(void)
141 {
142 static const svn_version_checklist_t checklist[] =
143 {
144 { "svn_subr", svn_subr_version },
145 { "svn_wc", svn_wc_version },
146 { NULL, NULL }
147 };
148 SVN_VERSION_DEFINE(my_version);
149
150 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
151 }
152
153 /*
154 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
155 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
156 * return SVN_NO_ERROR.
157 */
158 static svn_error_t *
sub_main(int * exit_code,int argc,const char * argv[],apr_pool_t * pool)159 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
160 {
161 apr_getopt_t *os;
162 const apr_getopt_option_t options[] =
163 {
164 {"help", 'h', 0, N_("display this help")},
165 {"version", OPT_VERSION, 0,
166 N_("show program version information")},
167 {0, 0, 0, 0}
168 };
169 apr_array_header_t *remaining_argv;
170
171 /* Check library versions */
172 SVN_ERR(check_lib_versions());
173
174 #if defined(WIN32) || defined(__CYGWIN__)
175 /* Set the working copy administrative directory name. */
176 if (getenv("SVN_ASP_DOT_NET_HACK"))
177 {
178 SVN_ERR(svn_wc_set_adm_dir("_svn", pool));
179 }
180 #endif
181
182 SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
183
184 os->interleave = 1;
185 while (1)
186 {
187 int opt;
188 const char *arg;
189 apr_status_t status = apr_getopt_long(os, options, &opt, &arg);
190 if (APR_STATUS_IS_EOF(status))
191 break;
192 if (status != APR_SUCCESS)
193 {
194 usage(pool);
195 *exit_code = EXIT_FAILURE;
196 return SVN_NO_ERROR;
197 }
198
199 switch (opt)
200 {
201 case 'h':
202 help(options, pool);
203 return SVN_NO_ERROR;
204 case OPT_VERSION:
205 SVN_ERR(version(pool));
206 return SVN_NO_ERROR;
207 default:
208 usage(pool);
209 *exit_code = EXIT_FAILURE;
210 return SVN_NO_ERROR;
211 }
212 }
213
214 /* Convert the remaining arguments to UTF-8. */
215 remaining_argv = apr_array_make(pool, 0, sizeof(const char *));
216 while (os->ind < argc)
217 {
218 const char *s;
219
220 SVN_ERR(svn_utf_cstring_to_utf8(&s, os->argv[os->ind++], pool));
221 APR_ARRAY_PUSH(remaining_argv, const char *) = s;
222 }
223
224 if (remaining_argv->nelts != 1)
225 {
226 usage(pool);
227 *exit_code = EXIT_FAILURE;
228 return SVN_NO_ERROR;
229 }
230
231 /* Do the main task */
232 SVN_ERR(verify_db(remaining_argv->nelts,
233 APR_ARRAY_IDX(remaining_argv, 0, const char *),
234 pool));
235
236 return SVN_NO_ERROR;
237 }
238
239 int
main(int argc,const char * argv[])240 main(int argc, const char *argv[])
241 {
242 apr_pool_t *pool;
243 int exit_code = EXIT_SUCCESS;
244 svn_error_t *err;
245
246 /* Initialize the app. */
247 if (svn_cmdline_init("svn-wc-db-tester", stderr) != EXIT_SUCCESS)
248 return EXIT_FAILURE;
249
250 /* Create our top-level pool. Use a separate mutexless allocator,
251 * given this application is single threaded.
252 */
253 pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
254
255 err = sub_main(&exit_code, argc, argv, pool);
256
257 /* Flush stdout and report if it fails. It would be flushed on exit anyway
258 but this makes sure that output is not silently lost if it fails. */
259 err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
260
261 if (err)
262 {
263 exit_code = EXIT_FAILURE;
264 svn_cmdline_handle_exit_error(err, NULL, "svn-wc-db-tester: ");
265 }
266
267 svn_pool_destroy(pool);
268 return exit_code;
269 }
270