1/* error.c:  common exception handling for Subversion
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
24
25#include <stdarg.h>
26
27#include <apr_general.h>
28#include <apr_pools.h>
29#include <apr_strings.h>
30
31#include <zlib.h>
32
33#ifndef SVN_ERR__TRACING
34#define SVN_ERR__TRACING
35#endif
36#include "svn_cmdline.h"
37#include "svn_error.h"
38#include "svn_pools.h"
39#include "svn_utf.h"
40
41#ifdef SVN_DEBUG
42/* XXX FIXME: These should be protected by a thread mutex.
43   svn_error__locate and make_error_internal should cooperate
44   in locking and unlocking it. */
45
46/* XXX TODO: Define mutex here #if APR_HAS_THREADS */
47static const char * volatile error_file = NULL;
48static long volatile error_line = -1;
49
50/* file_line for the non-debug case. */
51static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
52#endif /* SVN_DEBUG */
53
54#include "svn_private_config.h"
55#include "private/svn_error_private.h"
56
57
58/*
59 * Undefine the helpers for creating errors.
60 *
61 * *NOTE*: Any use of these functions in any other function may need
62 * to call svn_error__locate() because the macro that would otherwise
63 * do this is being undefined and the filename and line number will
64 * not be properly set in the static error_file and error_line
65 * variables.
66 */
67#undef svn_error_create
68#undef svn_error_createf
69#undef svn_error_quick_wrap
70#undef svn_error_wrap_apr
71
72/* Note: Although this is a "__" function, it was historically in the
73 * public ABI, so we can never change it or remove its signature, even
74 * though it is now only used in SVN_DEBUG mode. */
75void
76svn_error__locate(const char *file, long line)
77{
78#if defined(SVN_DEBUG)
79  /* XXX TODO: Lock mutex here */
80  error_file = file;
81  error_line = line;
82#endif
83}
84
85
86/* Cleanup function for errors.  svn_error_clear () removes this so
87   errors that are properly handled *don't* hit this code. */
88#if defined(SVN_DEBUG)
89static apr_status_t err_abort(void *data)
90{
91  svn_error_t *err = data;  /* For easy viewing in a debugger */
92  err = err; /* Fake a use for the variable to avoid compiler warnings */
93
94  if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
95    abort();
96  return APR_SUCCESS;
97}
98#endif
99
100
101static svn_error_t *
102make_error_internal(apr_status_t apr_err,
103                    svn_error_t *child)
104{
105  apr_pool_t *pool;
106  svn_error_t *new_error;
107
108  /* Reuse the child's pool, or create our own. */
109  if (child)
110    pool = child->pool;
111  else
112    {
113      if (apr_pool_create(&pool, NULL))
114        abort();
115    }
116
117  /* Create the new error structure */
118  new_error = apr_pcalloc(pool, sizeof(*new_error));
119
120  /* Fill 'er up. */
121  new_error->apr_err = apr_err;
122  new_error->child   = child;
123  new_error->pool    = pool;
124#if defined(SVN_DEBUG)
125  new_error->file    = error_file;
126  new_error->line    = error_line;
127  /* XXX TODO: Unlock mutex here */
128
129  if (! child)
130      apr_pool_cleanup_register(pool, new_error,
131                                err_abort,
132                                apr_pool_cleanup_null);
133#endif
134
135  return new_error;
136}
137
138
139
140/*** Creating and destroying errors. ***/
141
142svn_error_t *
143svn_error_create(apr_status_t apr_err,
144                 svn_error_t *child,
145                 const char *message)
146{
147  svn_error_t *err;
148
149  err = make_error_internal(apr_err, child);
150
151  if (message)
152    err->message = apr_pstrdup(err->pool, message);
153
154  return err;
155}
156
157
158svn_error_t *
159svn_error_createf(apr_status_t apr_err,
160                  svn_error_t *child,
161                  const char *fmt,
162                  ...)
163{
164  svn_error_t *err;
165  va_list ap;
166
167  err = make_error_internal(apr_err, child);
168
169  va_start(ap, fmt);
170  err->message = apr_pvsprintf(err->pool, fmt, ap);
171  va_end(ap);
172
173  return err;
174}
175
176
177svn_error_t *
178svn_error_wrap_apr(apr_status_t status,
179                   const char *fmt,
180                   ...)
181{
182  svn_error_t *err, *utf8_err;
183  va_list ap;
184  char errbuf[255];
185  const char *msg_apr, *msg;
186
187  err = make_error_internal(status, NULL);
188
189  if (fmt)
190    {
191      /* Grab the APR error message. */
192      apr_strerror(status, errbuf, sizeof(errbuf));
193      utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
194      if (utf8_err)
195        msg_apr = NULL;
196      svn_error_clear(utf8_err);
197
198      /* Append it to the formatted message. */
199      va_start(ap, fmt);
200      msg = apr_pvsprintf(err->pool, fmt, ap);
201      va_end(ap);
202      if (msg_apr)
203        {
204          err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL);
205        }
206      else
207        {
208          err->message = msg;
209        }
210    }
211
212  return err;
213}
214
215
216svn_error_t *
217svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
218{
219  if (child == SVN_NO_ERROR)
220    return SVN_NO_ERROR;
221
222  return svn_error_create(child->apr_err,
223                          child,
224                          new_msg);
225}
226
227/* Messages in tracing errors all point to this static string. */
228static const char error_tracing_link[] = "traced call";
229
230svn_error_t *
231svn_error__trace(const char *file, long line, svn_error_t *err)
232{
233#ifndef SVN_DEBUG
234
235  /* We shouldn't even be here, but whatever. Just return the error as-is.  */
236  return err;
237
238#else
239
240  /* Only do the work when an error occurs.  */
241  if (err)
242    {
243      svn_error_t *trace;
244      svn_error__locate(file, line);
245      trace = make_error_internal(err->apr_err, err);
246      trace->message = error_tracing_link;
247      return trace;
248    }
249  return SVN_NO_ERROR;
250
251#endif
252}
253
254
255svn_error_t *
256svn_error_compose_create(svn_error_t *err1,
257                         svn_error_t *err2)
258{
259  if (err1 && err2)
260    {
261      svn_error_compose(err1,
262                        svn_error_quick_wrap(err2,
263                                             _("Additional errors:")));
264      return err1;
265    }
266  return err1 ? err1 : err2;
267}
268
269
270void
271svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
272{
273  apr_pool_t *pool = chain->pool;
274  apr_pool_t *oldpool = new_err->pool;
275
276  while (chain->child)
277    chain = chain->child;
278
279#if defined(SVN_DEBUG)
280  /* Kill existing handler since the end of the chain is going to change */
281  apr_pool_cleanup_kill(pool, chain, err_abort);
282#endif
283
284  /* Copy the new error chain into the old chain's pool. */
285  while (new_err)
286    {
287      chain->child = apr_palloc(pool, sizeof(*chain->child));
288      chain = chain->child;
289      *chain = *new_err;
290      if (chain->message)
291        chain->message = apr_pstrdup(pool, new_err->message);
292      chain->pool = pool;
293#if defined(SVN_DEBUG)
294      if (! new_err->child)
295        apr_pool_cleanup_kill(oldpool, new_err, err_abort);
296#endif
297      new_err = new_err->child;
298    }
299
300#if defined(SVN_DEBUG)
301  apr_pool_cleanup_register(pool, chain,
302                            err_abort,
303                            apr_pool_cleanup_null);
304#endif
305
306  /* Destroy the new error chain. */
307  svn_pool_destroy(oldpool);
308}
309
310svn_error_t *
311svn_error_root_cause(svn_error_t *err)
312{
313  while (err)
314    {
315      if (err->child)
316        err = err->child;
317      else
318        break;
319    }
320
321  return err;
322}
323
324svn_error_t *
325svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
326{
327  svn_error_t *child;
328
329  for (child = err; child; child = child->child)
330    if (child->apr_err == apr_err)
331      return child;
332
333  return SVN_NO_ERROR;
334}
335
336svn_error_t *
337svn_error_dup(svn_error_t *err)
338{
339  apr_pool_t *pool;
340  svn_error_t *new_err = NULL, *tmp_err = NULL;
341
342  if (apr_pool_create(&pool, NULL))
343    abort();
344
345  for (; err; err = err->child)
346    {
347      if (! new_err)
348        {
349          new_err = apr_palloc(pool, sizeof(*new_err));
350          tmp_err = new_err;
351        }
352      else
353        {
354          tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
355          tmp_err = tmp_err->child;
356        }
357      *tmp_err = *err;
358      tmp_err->pool = pool;
359      if (tmp_err->message)
360        tmp_err->message = apr_pstrdup(pool, tmp_err->message);
361    }
362
363#if defined(SVN_DEBUG)
364  apr_pool_cleanup_register(pool, tmp_err,
365                            err_abort,
366                            apr_pool_cleanup_null);
367#endif
368
369  return new_err;
370}
371
372void
373svn_error_clear(svn_error_t *err)
374{
375  if (err)
376    {
377#if defined(SVN_DEBUG)
378      while (err->child)
379        err = err->child;
380      apr_pool_cleanup_kill(err->pool, err, err_abort);
381#endif
382      svn_pool_destroy(err->pool);
383    }
384}
385
386svn_boolean_t
387svn_error__is_tracing_link(svn_error_t *err)
388{
389#ifdef SVN_ERR__TRACING
390  /* ### A strcmp()?  Really?  I think it's the best we can do unless
391     ### we add a boolean field to svn_error_t that's set only for
392     ### these "placeholder error chain" items.  Not such a bad idea,
393     ### really...  */
394  return (err && err->message && !strcmp(err->message, error_tracing_link));
395#else
396  return FALSE;
397#endif
398}
399
400svn_error_t *
401svn_error_purge_tracing(svn_error_t *err)
402{
403#ifdef SVN_ERR__TRACING
404  svn_error_t *new_err = NULL, *new_err_leaf = NULL;
405
406  if (! err)
407    return SVN_NO_ERROR;
408
409  do
410    {
411      svn_error_t *tmp_err;
412
413      /* Skip over any trace-only links. */
414      while (err && svn_error__is_tracing_link(err))
415        err = err->child;
416
417      /* The link must be a real link in the error chain, otherwise an
418         error chain with trace only links would map into SVN_NO_ERROR. */
419      if (! err)
420        return svn_error_create(
421                 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
422                 svn_error_compose_create(
423                   svn_error__malfunction(TRUE, __FILE__, __LINE__,
424                                          NULL /* ### say something? */),
425                   err),
426                 NULL);
427
428      /* Copy the current error except for its child error pointer
429         into the new error.  Share any message and source filename
430         strings from the error. */
431      tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
432      *tmp_err = *err;
433      tmp_err->child = NULL;
434
435      /* Add a new link to the new chain (creating the chain if necessary). */
436      if (! new_err)
437        {
438          new_err = tmp_err;
439          new_err_leaf = tmp_err;
440        }
441      else
442        {
443          new_err_leaf->child = tmp_err;
444          new_err_leaf = tmp_err;
445        }
446
447      /* Advance to the next link in the original chain. */
448      err = err->child;
449    } while (err);
450
451  return new_err;
452#else  /* SVN_ERR__TRACING */
453  return err;
454#endif /* SVN_ERR__TRACING */
455}
456
457/* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
458   ### coupled to the current sole caller.*/
459static void
460print_error(svn_error_t *err, FILE *stream, const char *prefix)
461{
462  char errbuf[256];
463  const char *err_string;
464  svn_error_t *temp_err = NULL;  /* ensure initialized even if
465                                    err->file == NULL */
466  /* Pretty-print the error */
467  /* Note: we can also log errors here someday. */
468
469#ifdef SVN_DEBUG
470  /* Note: err->file is _not_ in UTF-8, because it's expanded from
471           the __FILE__ preprocessor macro. */
472  const char *file_utf8;
473
474  if (err->file
475      && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
476                                              err->pool)))
477    svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
478                                        "%s:%ld", err->file, err->line));
479  else
480    {
481      svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
482                                        stream, err->pool));
483      svn_error_clear(temp_err);
484    }
485
486  {
487    const char *symbolic_name;
488    if (svn_error__is_tracing_link(err))
489      /* Skip it; the error code will be printed by the real link. */
490      svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
491    else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
492      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
493                                          ": (apr_err=%s)\n", symbolic_name));
494    else
495      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
496                                          ": (apr_err=%d)\n", err->apr_err));
497  }
498#endif /* SVN_DEBUG */
499
500  /* "traced call" */
501  if (svn_error__is_tracing_link(err))
502    {
503      /* Skip it.  We already printed the file-line coordinates. */
504    }
505  /* Only print the same APR error string once. */
506  else if (err->message)
507    {
508      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
509                                          "%sE%06d: %s\n",
510                                          prefix, err->apr_err, err->message));
511    }
512  else
513    {
514      /* Is this a Subversion-specific error code? */
515      if ((err->apr_err > APR_OS_START_USEERR)
516          && (err->apr_err <= APR_OS_START_CANONERR))
517        err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
518      /* Otherwise, this must be an APR error code. */
519      else if ((temp_err = svn_utf_cstring_to_utf8
520                (&err_string, apr_strerror(err->apr_err, errbuf,
521                                           sizeof(errbuf)), err->pool)))
522        {
523          svn_error_clear(temp_err);
524          err_string = _("Can't recode error string from APR");
525        }
526
527      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
528                                          "%sE%06d: %s\n",
529                                          prefix, err->apr_err, err_string));
530    }
531}
532
533void
534svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
535{
536  svn_handle_error2(err, stream, fatal, "svn: ");
537}
538
539void
540svn_handle_error2(svn_error_t *err,
541                  FILE *stream,
542                  svn_boolean_t fatal,
543                  const char *prefix)
544{
545  /* In a long error chain, there may be multiple errors with the same
546     error code and no custom message.  We only want to print the
547     default message for that code once; printing it multiple times
548     would add no useful information.  The 'empties' array below
549     remembers the codes of empty errors already seen in the chain.
550
551     We could allocate it in err->pool, but there's no telling how
552     long err will live or how many times it will get handled.  So we
553     use a subpool. */
554  apr_pool_t *subpool;
555  apr_array_header_t *empties;
556  svn_error_t *tmp_err;
557
558  /* ### The rest of this file carefully avoids using svn_pool_*(),
559     preferring apr_pool_*() instead.  I can't remember why -- it may
560     be an artifact of r843793, or it may be for some deeper reason --
561     but I'm playing it safe and using apr_pool_*() here too. */
562  apr_pool_create(&subpool, err->pool);
563  empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
564
565  tmp_err = err;
566  while (tmp_err)
567    {
568      svn_boolean_t printed_already = FALSE;
569
570      if (! tmp_err->message)
571        {
572          int i;
573
574          for (i = 0; i < empties->nelts; i++)
575            {
576              if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
577                {
578                  printed_already = TRUE;
579                  break;
580                }
581            }
582        }
583
584      if (! printed_already)
585        {
586          print_error(tmp_err, stream, prefix);
587          if (! tmp_err->message)
588            {
589              APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
590            }
591        }
592
593      tmp_err = tmp_err->child;
594    }
595
596  svn_pool_destroy(subpool);
597
598  fflush(stream);
599  if (fatal)
600    {
601      /* Avoid abort()s in maintainer mode. */
602      svn_error_clear(err);
603
604      /* We exit(1) here instead of abort()ing so that atexit handlers
605         get called. */
606      exit(EXIT_FAILURE);
607    }
608}
609
610
611void
612svn_handle_warning(FILE *stream, svn_error_t *err)
613{
614  svn_handle_warning2(stream, err, "svn: ");
615}
616
617void
618svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
619{
620  char buf[256];
621
622  svn_error_clear(svn_cmdline_fprintf
623                  (stream, err->pool,
624                   _("%swarning: W%06d: %s\n"),
625                   prefix, err->apr_err,
626                   svn_err_best_message(err, buf, sizeof(buf))));
627  fflush(stream);
628}
629
630const char *
631svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
632{
633  /* Skip over any trace records.  */
634  while (svn_error__is_tracing_link(err))
635    err = err->child;
636  if (err->message)
637    return err->message;
638  else
639    return svn_strerror(err->apr_err, buf, bufsize);
640}
641
642
643/* svn_strerror() and helpers */
644
645/* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
646typedef struct err_defn {
647  svn_errno_t errcode; /* 160004 */
648  const char *errname; /* SVN_ERR_FS_CORRUPT */
649  const char *errdesc; /* default message */
650} err_defn;
651
652/* To understand what is going on here, read svn_error_codes.h. */
653#define SVN_ERROR_BUILD_ARRAY
654#include "svn_error_codes.h"
655
656char *
657svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
658{
659  const err_defn *defn;
660
661  for (defn = error_table; defn->errdesc != NULL; ++defn)
662    if (defn->errcode == (svn_errno_t)statcode)
663      {
664        apr_cpystrn(buf, _(defn->errdesc), bufsize);
665        return buf;
666      }
667
668  return apr_strerror(statcode, buf, bufsize);
669}
670
671const char *
672svn_error_symbolic_name(apr_status_t statcode)
673{
674  const err_defn *defn;
675
676  for (defn = error_table; defn->errdesc != NULL; ++defn)
677    if (defn->errcode == (svn_errno_t)statcode)
678      return defn->errname;
679
680  /* "No error" is not in error_table. */
681  if (statcode == SVN_NO_ERROR)
682    return "SVN_NO_ERROR";
683
684  return NULL;
685}
686
687
688
689/* Malfunctions. */
690
691svn_error_t *
692svn_error_raise_on_malfunction(svn_boolean_t can_return,
693                               const char *file, int line,
694                               const char *expr)
695{
696  if (!can_return)
697    abort(); /* Nothing else we can do as a library */
698
699  /* The filename and line number of the error source needs to be set
700     here because svn_error_createf() is not the macro defined in
701     svn_error.h but the real function. */
702  svn_error__locate(file, line);
703
704  if (expr)
705    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
706                             _("In file '%s' line %d: assertion failed (%s)"),
707                             file, line, expr);
708  else
709    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
710                             _("In file '%s' line %d: internal malfunction"),
711                             file, line);
712}
713
714svn_error_t *
715svn_error_abort_on_malfunction(svn_boolean_t can_return,
716                               const char *file, int line,
717                               const char *expr)
718{
719  svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
720
721  svn_handle_error2(err, stderr, FALSE, "svn: ");
722  abort();
723  return err;  /* Not reached. */
724}
725
726/* The current handler for reporting malfunctions, and its default setting. */
727static svn_error_malfunction_handler_t malfunction_handler
728  = svn_error_abort_on_malfunction;
729
730svn_error_malfunction_handler_t
731svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
732{
733  svn_error_malfunction_handler_t old_malfunction_handler
734    = malfunction_handler;
735
736  malfunction_handler = func;
737  return old_malfunction_handler;
738}
739
740/* Note: Although this is a "__" function, it is in the public ABI, so
741 * we can never remove it or change its signature. */
742svn_error_t *
743svn_error__malfunction(svn_boolean_t can_return,
744                       const char *file, int line,
745                       const char *expr)
746{
747  return malfunction_handler(can_return, file, line, expr);
748}
749
750
751/* Misc. */
752
753svn_error_t *
754svn_error__wrap_zlib(int zerr, const char *function, const char *message)
755{
756  apr_status_t status;
757  const char *zmsg;
758
759  if (zerr == Z_OK)
760    return SVN_NO_ERROR;
761
762  switch (zerr)
763    {
764    case Z_STREAM_ERROR:
765      status = SVN_ERR_STREAM_MALFORMED_DATA;
766      zmsg = _("stream error");
767      break;
768
769    case Z_MEM_ERROR:
770      status = APR_ENOMEM;
771      zmsg = _("out of memory");
772      break;
773
774    case Z_BUF_ERROR:
775      status = APR_ENOMEM;
776      zmsg = _("buffer error");
777      break;
778
779    case Z_VERSION_ERROR:
780      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
781      zmsg = _("version error");
782      break;
783
784    case Z_DATA_ERROR:
785      status = SVN_ERR_STREAM_MALFORMED_DATA;
786      zmsg = _("corrupt data");
787      break;
788
789    default:
790      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
791      zmsg = _("unknown error");
792      break;
793    }
794
795  if (message != NULL)
796    return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
797                             zmsg, message);
798  else
799    return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
800}
801