]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/subversion/subversion/libsvn_subr/error.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / contrib / subversion / subversion / libsvn_subr / error.c
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 \f
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 */
47 static const char * volatile error_file = NULL;
48 static long volatile error_line = -1;
49
50 /* file_line for the non-debug case. */
51 static 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 \f
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. */
75 void
76 svn_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)
89 static 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
101 static svn_error_t *
102 make_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 \f
140 /*** Creating and destroying errors. ***/
141
142 svn_error_t *
143 svn_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
158 svn_error_t *
159 svn_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
177 svn_error_t *
178 svn_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
216 svn_error_t *
217 svn_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. */
228 static const char error_tracing_link[] = "traced call";
229
230 svn_error_t *
231 svn_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
255 svn_error_t *
256 svn_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
270 void
271 svn_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       if (chain->file)
293         chain->file = apr_pstrdup(pool, new_err->file);
294       chain->pool = pool;
295 #if defined(SVN_DEBUG)
296       if (! new_err->child)
297         apr_pool_cleanup_kill(oldpool, new_err, err_abort);
298 #endif
299       new_err = new_err->child;
300     }
301
302 #if defined(SVN_DEBUG)
303   apr_pool_cleanup_register(pool, chain,
304                             err_abort,
305                             apr_pool_cleanup_null);
306 #endif
307
308   /* Destroy the new error chain. */
309   svn_pool_destroy(oldpool);
310 }
311
312 svn_error_t *
313 svn_error_root_cause(svn_error_t *err)
314 {
315   while (err)
316     {
317       if (err->child)
318         err = err->child;
319       else
320         break;
321     }
322
323   return err;
324 }
325
326 svn_error_t *
327 svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
328 {
329   svn_error_t *child;
330
331   for (child = err; child; child = child->child)
332     if (child->apr_err == apr_err)
333       return child;
334
335   return SVN_NO_ERROR;
336 }
337
338 svn_error_t *
339 svn_error_dup(svn_error_t *err)
340 {
341   apr_pool_t *pool;
342   svn_error_t *new_err = NULL, *tmp_err = NULL;
343
344   if (apr_pool_create(&pool, NULL))
345     abort();
346
347   for (; err; err = err->child)
348     {
349       if (! new_err)
350         {
351           new_err = apr_palloc(pool, sizeof(*new_err));
352           tmp_err = new_err;
353         }
354       else
355         {
356           tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
357           tmp_err = tmp_err->child;
358         }
359       *tmp_err = *err;
360       tmp_err->pool = pool;
361       if (tmp_err->message)
362         tmp_err->message = apr_pstrdup(pool, tmp_err->message);
363       if (tmp_err->file)
364         tmp_err->file = apr_pstrdup(pool, tmp_err->file);
365     }
366
367 #if defined(SVN_DEBUG)
368   apr_pool_cleanup_register(pool, tmp_err,
369                             err_abort,
370                             apr_pool_cleanup_null);
371 #endif
372
373   return new_err;
374 }
375
376 void
377 svn_error_clear(svn_error_t *err)
378 {
379   if (err)
380     {
381 #if defined(SVN_DEBUG)
382       while (err->child)
383         err = err->child;
384       apr_pool_cleanup_kill(err->pool, err, err_abort);
385 #endif
386       svn_pool_destroy(err->pool);
387     }
388 }
389
390 svn_boolean_t
391 svn_error__is_tracing_link(svn_error_t *err)
392 {
393 #ifdef SVN_ERR__TRACING
394   /* ### A strcmp()?  Really?  I think it's the best we can do unless
395      ### we add a boolean field to svn_error_t that's set only for
396      ### these "placeholder error chain" items.  Not such a bad idea,
397      ### really...  */
398   return (err && err->message && !strcmp(err->message, error_tracing_link));
399 #else
400   return FALSE;
401 #endif
402 }
403
404 svn_error_t *
405 svn_error_purge_tracing(svn_error_t *err)
406 {
407 #ifdef SVN_ERR__TRACING
408   svn_error_t *new_err = NULL, *new_err_leaf = NULL;
409
410   if (! err)
411     return SVN_NO_ERROR;
412
413   do
414     {
415       svn_error_t *tmp_err;
416
417       /* Skip over any trace-only links. */
418       while (err && svn_error__is_tracing_link(err))
419         err = err->child;
420
421       /* The link must be a real link in the error chain, otherwise an
422          error chain with trace only links would map into SVN_NO_ERROR. */
423       if (! err)
424         return svn_error_create(
425                  SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
426                  svn_error_compose_create(
427                    svn_error__malfunction(TRUE, __FILE__, __LINE__,
428                                           NULL /* ### say something? */),
429                    err),
430                  NULL);
431
432       /* Copy the current error except for its child error pointer
433          into the new error.  Share any message and source filename
434          strings from the error. */
435       tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
436       *tmp_err = *err;
437       tmp_err->child = NULL;
438
439       /* Add a new link to the new chain (creating the chain if necessary). */
440       if (! new_err)
441         {
442           new_err = tmp_err;
443           new_err_leaf = tmp_err;
444         }
445       else
446         {
447           new_err_leaf->child = tmp_err;
448           new_err_leaf = tmp_err;
449         }
450
451       /* Advance to the next link in the original chain. */
452       err = err->child;
453     } while (err);
454
455   return new_err;
456 #else  /* SVN_ERR__TRACING */
457   return err;
458 #endif /* SVN_ERR__TRACING */
459 }
460
461 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
462    ### coupled to the current sole caller.*/
463 static void
464 print_error(svn_error_t *err, FILE *stream, const char *prefix)
465 {
466   char errbuf[256];
467   const char *err_string;
468   svn_error_t *temp_err = NULL;  /* ensure initialized even if
469                                     err->file == NULL */
470   /* Pretty-print the error */
471   /* Note: we can also log errors here someday. */
472
473 #ifdef SVN_DEBUG
474   /* Note: err->file is _not_ in UTF-8, because it's expanded from
475            the __FILE__ preprocessor macro. */
476   const char *file_utf8;
477
478   if (err->file
479       && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
480                                               err->pool)))
481     svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
482                                         "%s:%ld", err->file, err->line));
483   else
484     {
485       svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
486                                         stream, err->pool));
487       svn_error_clear(temp_err);
488     }
489
490   {
491     const char *symbolic_name;
492     if (svn_error__is_tracing_link(err))
493       /* Skip it; the error code will be printed by the real link. */
494       svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
495     else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
496       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
497                                           ": (apr_err=%s)\n", symbolic_name));
498     else
499       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
500                                           ": (apr_err=%d)\n", err->apr_err));
501   }
502 #endif /* SVN_DEBUG */
503
504   /* "traced call" */
505   if (svn_error__is_tracing_link(err))
506     {
507       /* Skip it.  We already printed the file-line coordinates. */
508     }
509   /* Only print the same APR error string once. */
510   else if (err->message)
511     {
512       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
513                                           "%sE%06d: %s\n",
514                                           prefix, err->apr_err, err->message));
515     }
516   else
517     {
518       /* Is this a Subversion-specific error code? */
519       if ((err->apr_err > APR_OS_START_USEERR)
520           && (err->apr_err <= APR_OS_START_CANONERR))
521         err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
522       /* Otherwise, this must be an APR error code. */
523       else if ((temp_err = svn_utf_cstring_to_utf8
524                 (&err_string, apr_strerror(err->apr_err, errbuf,
525                                            sizeof(errbuf)), err->pool)))
526         {
527           svn_error_clear(temp_err);
528           err_string = _("Can't recode error string from APR");
529         }
530
531       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
532                                           "%sE%06d: %s\n",
533                                           prefix, err->apr_err, err_string));
534     }
535 }
536
537 void
538 svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
539 {
540   svn_handle_error2(err, stream, fatal, "svn: ");
541 }
542
543 void
544 svn_handle_error2(svn_error_t *err,
545                   FILE *stream,
546                   svn_boolean_t fatal,
547                   const char *prefix)
548 {
549   /* In a long error chain, there may be multiple errors with the same
550      error code and no custom message.  We only want to print the
551      default message for that code once; printing it multiple times
552      would add no useful information.  The 'empties' array below
553      remembers the codes of empty errors already seen in the chain.
554
555      We could allocate it in err->pool, but there's no telling how
556      long err will live or how many times it will get handled.  So we
557      use a subpool. */
558   apr_pool_t *subpool;
559   apr_array_header_t *empties;
560   svn_error_t *tmp_err;
561
562   /* ### The rest of this file carefully avoids using svn_pool_*(),
563      preferring apr_pool_*() instead.  I can't remember why -- it may
564      be an artifact of r843793, or it may be for some deeper reason --
565      but I'm playing it safe and using apr_pool_*() here too. */
566   apr_pool_create(&subpool, err->pool);
567   empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
568
569   tmp_err = err;
570   while (tmp_err)
571     {
572       svn_boolean_t printed_already = FALSE;
573
574       if (! tmp_err->message)
575         {
576           int i;
577
578           for (i = 0; i < empties->nelts; i++)
579             {
580               if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
581                 {
582                   printed_already = TRUE;
583                   break;
584                 }
585             }
586         }
587
588       if (! printed_already)
589         {
590           print_error(tmp_err, stream, prefix);
591           if (! tmp_err->message)
592             {
593               APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
594             }
595         }
596
597       tmp_err = tmp_err->child;
598     }
599
600   svn_pool_destroy(subpool);
601
602   fflush(stream);
603   if (fatal)
604     {
605       /* Avoid abort()s in maintainer mode. */
606       svn_error_clear(err);
607
608       /* We exit(1) here instead of abort()ing so that atexit handlers
609          get called. */
610       exit(EXIT_FAILURE);
611     }
612 }
613
614
615 void
616 svn_handle_warning(FILE *stream, svn_error_t *err)
617 {
618   svn_handle_warning2(stream, err, "svn: ");
619 }
620
621 void
622 svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
623 {
624   char buf[256];
625
626   svn_error_clear(svn_cmdline_fprintf
627                   (stream, err->pool,
628                    _("%swarning: W%06d: %s\n"),
629                    prefix, err->apr_err,
630                    svn_err_best_message(err, buf, sizeof(buf))));
631   fflush(stream);
632 }
633
634 const char *
635 svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
636 {
637   /* Skip over any trace records.  */
638   while (svn_error__is_tracing_link(err))
639     err = err->child;
640   if (err->message)
641     return err->message;
642   else
643     return svn_strerror(err->apr_err, buf, bufsize);
644 }
645
646 \f
647 /* svn_strerror() and helpers */
648
649 /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
650 typedef struct err_defn {
651   svn_errno_t errcode; /* 160004 */
652   const char *errname; /* SVN_ERR_FS_CORRUPT */
653   const char *errdesc; /* default message */
654 } err_defn;
655
656 /* To understand what is going on here, read svn_error_codes.h. */
657 #define SVN_ERROR_BUILD_ARRAY
658 #include "svn_error_codes.h"
659
660 char *
661 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
662 {
663   const err_defn *defn;
664
665   for (defn = error_table; defn->errdesc != NULL; ++defn)
666     if (defn->errcode == (svn_errno_t)statcode)
667       {
668         apr_cpystrn(buf, _(defn->errdesc), bufsize);
669         return buf;
670       }
671
672   return apr_strerror(statcode, buf, bufsize);
673 }
674
675 const char *
676 svn_error_symbolic_name(apr_status_t statcode)
677 {
678   const err_defn *defn;
679
680   for (defn = error_table; defn->errdesc != NULL; ++defn)
681     if (defn->errcode == (svn_errno_t)statcode)
682       return defn->errname;
683
684   /* "No error" is not in error_table. */
685   if (statcode == SVN_NO_ERROR)
686     return "SVN_NO_ERROR";
687
688   return NULL;
689 }
690
691
692 \f
693 /* Malfunctions. */
694
695 svn_error_t *
696 svn_error_raise_on_malfunction(svn_boolean_t can_return,
697                                const char *file, int line,
698                                const char *expr)
699 {
700   if (!can_return)
701     abort(); /* Nothing else we can do as a library */
702
703   /* The filename and line number of the error source needs to be set
704      here because svn_error_createf() is not the macro defined in
705      svn_error.h but the real function. */
706   svn_error__locate(file, line);
707
708   if (expr)
709     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
710                              _("In file '%s' line %d: assertion failed (%s)"),
711                              file, line, expr);
712   else
713     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
714                              _("In file '%s' line %d: internal malfunction"),
715                              file, line);
716 }
717
718 svn_error_t *
719 svn_error_abort_on_malfunction(svn_boolean_t can_return,
720                                const char *file, int line,
721                                const char *expr)
722 {
723   svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
724
725   svn_handle_error2(err, stderr, FALSE, "svn: ");
726   abort();
727   return err;  /* Not reached. */
728 }
729
730 /* The current handler for reporting malfunctions, and its default setting. */
731 static svn_error_malfunction_handler_t malfunction_handler
732   = svn_error_abort_on_malfunction;
733
734 svn_error_malfunction_handler_t
735 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
736 {
737   svn_error_malfunction_handler_t old_malfunction_handler
738     = malfunction_handler;
739
740   malfunction_handler = func;
741   return old_malfunction_handler;
742 }
743
744 /* Note: Although this is a "__" function, it is in the public ABI, so
745  * we can never remove it or change its signature. */
746 svn_error_t *
747 svn_error__malfunction(svn_boolean_t can_return,
748                        const char *file, int line,
749                        const char *expr)
750 {
751   return malfunction_handler(can_return, file, line, expr);
752 }
753
754 \f
755 /* Misc. */
756
757 svn_error_t *
758 svn_error__wrap_zlib(int zerr, const char *function, const char *message)
759 {
760   apr_status_t status;
761   const char *zmsg;
762
763   if (zerr == Z_OK)
764     return SVN_NO_ERROR;
765
766   switch (zerr)
767     {
768     case Z_STREAM_ERROR:
769       status = SVN_ERR_STREAM_MALFORMED_DATA;
770       zmsg = _("stream error");
771       break;
772
773     case Z_MEM_ERROR:
774       status = APR_ENOMEM;
775       zmsg = _("out of memory");
776       break;
777
778     case Z_BUF_ERROR:
779       status = APR_ENOMEM;
780       zmsg = _("buffer error");
781       break;
782
783     case Z_VERSION_ERROR:
784       status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
785       zmsg = _("version error");
786       break;
787
788     case Z_DATA_ERROR:
789       status = SVN_ERR_STREAM_MALFORMED_DATA;
790       zmsg = _("corrupt data");
791       break;
792
793     default:
794       status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
795       zmsg = _("unknown error");
796       break;
797     }
798
799   if (message != NULL)
800     return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
801                              zmsg, message);
802   else
803     return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
804 }