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