fsck: introduce identifiers for fsck messages

Instead of specifying whether a message by the fsck machinery constitutes an error or a warning, let's specify an identifier relating to the concrete problem that was encountered. This is necessary for upcoming support to be able to demote certain errors to warnings. In the process, simplify the requirements on the calling code: instead of having to handle full-blown varargs in every callback, we now send a string buffer ready to be used by the callback. We could use a simple enum for the message IDs here, but we want to guarantee that the enum values are associated with the appropriate message types (i.e. error or warning?). Besides, we want to introduce a parser in the next commit that maps the string representation to the enum value, hence we use the slightly ugly preprocessor construct that is extensible for use with said parser. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jun 22, 2015 at 17:25 UTC c99ba492f1cc3e632ed12d46d7358effb6fa7f03
3 files changed +154 -78
builtin/fsck.c
+8 -18
@@ -46,33 +46,23 @@ static int show_dangling = 1;
46 #define DIRENT_SORT_HINT(de) ((de)->d_ino)
47 #endif
48
49 -static void objreport(struct object *obj, const char *severity,
50 - const char *err, va_list params)
49 +static void objreport(struct object *obj, const char *msg_type,
50 + const char *err)
51 {
52 - fprintf(stderr, "%s in %s %s: ",
53 - severity, typename(obj->type), sha1_to_hex(obj->sha1));
54 - vfprintf(stderr, err, params);
55 - fputs("\n", stderr);
52 + fprintf(stderr, "%s in %s %s: %s\n",
53 + msg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);
54 }
55
58 -__attribute__((format (printf, 2, 3)))
59 -static int objerror(struct object *obj, const char *err, ...)
56 +static int objerror(struct object *obj, const char *err)
57 {
61 - va_list params;
62 - va_start(params, err);
58 errors_found |= ERROR_OBJECT;
64 - objreport(obj, "error", err, params);
65 - va_end(params);
59 + objreport(obj, "error", err);
60 return -1;
61 }
62
69 -__attribute__((format (printf, 3, 4)))
70 -static int fsck_error_func(struct object *obj, int type, const char *err, ...)
63 +static int fsck_error_func(struct object *obj, int type, const char *message)
64 {
72 - va_list params;
73 - va_start(params, err);
74 - objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
75 - va_end(params);
65 + objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
66 return (type == FSCK_WARN) ? 0 : 1;
67 }
68
fsck.c
+144 -57
@@ -9,6 +9,98 @@
9 #include "refs.h"
10 #include "utf8.h"
11
12 +#define FOREACH_MSG_ID(FUNC) \
13 + /* errors */ \
14 + FUNC(BAD_DATE, ERROR) \
15 + FUNC(BAD_DATE_OVERFLOW, ERROR) \
16 + FUNC(BAD_EMAIL, ERROR) \
17 + FUNC(BAD_NAME, ERROR) \
18 + FUNC(BAD_OBJECT_SHA1, ERROR) \
19 + FUNC(BAD_PARENT_SHA1, ERROR) \
20 + FUNC(BAD_TAG_OBJECT, ERROR) \
21 + FUNC(BAD_TIMEZONE, ERROR) \
22 + FUNC(BAD_TREE, ERROR) \
23 + FUNC(BAD_TREE_SHA1, ERROR) \
24 + FUNC(BAD_TYPE, ERROR) \
25 + FUNC(DUPLICATE_ENTRIES, ERROR) \
26 + FUNC(MISSING_AUTHOR, ERROR) \
27 + FUNC(MISSING_COMMITTER, ERROR) \
28 + FUNC(MISSING_EMAIL, ERROR) \
29 + FUNC(MISSING_GRAFT, ERROR) \
30 + FUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \
31 + FUNC(MISSING_OBJECT, ERROR) \
32 + FUNC(MISSING_PARENT, ERROR) \
33 + FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \
34 + FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \
35 + FUNC(MISSING_TAG, ERROR) \
36 + FUNC(MISSING_TAG_ENTRY, ERROR) \
37 + FUNC(MISSING_TAG_OBJECT, ERROR) \
38 + FUNC(MISSING_TREE, ERROR) \
39 + FUNC(MISSING_TYPE, ERROR) \
40 + FUNC(MISSING_TYPE_ENTRY, ERROR) \
41 + FUNC(NUL_IN_HEADER, ERROR) \
42 + FUNC(TAG_OBJECT_NOT_TAG, ERROR) \
43 + FUNC(TREE_NOT_SORTED, ERROR) \
44 + FUNC(UNKNOWN_TYPE, ERROR) \
45 + FUNC(UNTERMINATED_HEADER, ERROR) \
46 + FUNC(ZERO_PADDED_DATE, ERROR) \
47 + /* warnings */ \
48 + FUNC(BAD_FILEMODE, WARN) \
49 + FUNC(BAD_TAG_NAME, WARN) \
50 + FUNC(EMPTY_NAME, WARN) \
51 + FUNC(FULL_PATHNAME, WARN) \
52 + FUNC(HAS_DOT, WARN) \
53 + FUNC(HAS_DOTDOT, WARN) \
54 + FUNC(HAS_DOTGIT, WARN) \
55 + FUNC(MISSING_TAGGER_ENTRY, WARN) \
56 + FUNC(NULL_SHA1, WARN) \
57 + FUNC(ZERO_PADDED_FILEMODE, WARN)
58 +
59 +#define MSG_ID(id, msg_type) FSCK_MSG_##id,
60 +enum fsck_msg_id {
61 + FOREACH_MSG_ID(MSG_ID)
62 + FSCK_MSG_MAX
63 +};
64 +#undef MSG_ID
65 +
66 +#define MSG_ID(id, msg_type) { FSCK_##msg_type },
67 +static struct {
68 + int msg_type;
69 +} msg_id_info[FSCK_MSG_MAX + 1] = {
70 + FOREACH_MSG_ID(MSG_ID)
71 + { -1 }
72 +};
73 +#undef MSG_ID
74 +
75 +static int fsck_msg_type(enum fsck_msg_id msg_id,
76 + struct fsck_options *options)
77 +{
78 + int msg_type;
79 +
80 + msg_type = msg_id_info[msg_id].msg_type;
81 + if (options->strict && msg_type == FSCK_WARN)
82 + msg_type = FSCK_ERROR;
83 +
84 + return msg_type;
85 +}
86 +
87 +__attribute__((format (printf, 4, 5)))
88 +static int report(struct fsck_options *options, struct object *object,
89 + enum fsck_msg_id id, const char *fmt, ...)
90 +{
91 + va_list ap;
92 + struct strbuf sb = STRBUF_INIT;
93 + int msg_type = fsck_msg_type(id, options), result;
94 +
95 + va_start(ap, fmt);
96 + strbuf_vaddf(&sb, fmt, ap);
97 + result = options->error_func(object, msg_type, sb.buf);
98 + strbuf_release(&sb);
99 + va_end(ap);
100 +
101 + return result;
102 +}
103 +
104 static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
105 {
106 struct tree_desc desc;
@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
311
312 retval = 0;
313 if (has_null_sha1)
222 - retval += options->error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1");
314 + retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
315 if (has_full_path)
224 - retval += options->error_func(&item->object, FSCK_WARN, "contains full pathnames");
316 + retval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
317 if (has_empty_name)
226 - retval += options->error_func(&item->object, FSCK_WARN, "contains empty pathname");
318 + retval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
319 if (has_dot)
228 - retval += options->error_func(&item->object, FSCK_WARN, "contains '.'");
320 + retval += report(options, &item->object, FSCK_MSG_HAS_DOT, "contains '.'");
321 if (has_dotdot)
230 - retval += options->error_func(&item->object, FSCK_WARN, "contains '..'");
322 + retval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, "contains '..'");
323 if (has_dotgit)
232 - retval += options->error_func(&item->object, FSCK_WARN, "contains '.git'");
324 + retval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
325 if (has_zero_pad)
234 - retval += options->error_func(&item->object, FSCK_WARN, "contains zero-padded file modes");
326 + retval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
327 if (has_bad_modes)
236 - retval += options->error_func(&item->object, FSCK_WARN, "contains bad file modes");
328 + retval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
329 if (has_dup_entries)
238 - retval += options->error_func(&item->object, FSCK_ERROR, "contains duplicate file entries");
330 + retval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
331 if (not_properly_sorted)
240 - retval += options->error_func(&item->object, FSCK_ERROR, "not properly sorted");
332 + retval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
333 return retval;
334 }
335
@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,
342 for (i = 0; i < size; i++) {
343 switch (buffer[i]) {
344 case '\0':
253 - return options->error_func(obj, FSCK_ERROR,
254 - "unterminated header: NUL at offset %d", i);
345 + return report(options, obj,
346 + FSCK_MSG_NUL_IN_HEADER,
347 + "unterminated header: NUL at offset %ld", i);
348 case '\n':
349 if (i + 1 < size && buffer[i + 1] == '\n')
350 return 0;
351 }
352 }
353
261 - return options->error_func(obj, FSCK_ERROR, "unterminated header");
354 + return report(options, obj,
355 + FSCK_MSG_UNTERMINATED_HEADER, "unterminated header");
356 }
357
358 static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)
@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
360 char *end;
361
362 if (**ident == '<')
269 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
363 + return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
364 *ident += strcspn(*ident, "<>\n");
365 if (**ident == '>')
272 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad name");
366 + return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name");
367 if (**ident != '<')
274 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing email");
368 + return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email");
369 if ((*ident)[-1] != ' ')
276 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
370 + return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
371 (*ident)++;
372 *ident += strcspn(*ident, "<>\n");
373 if (**ident != '>')
280 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad email");
374 + return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email");
375 (*ident)++;
376 if (**ident != ' ')
283 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before date");
377 + return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
378 (*ident)++;
379 if (**ident == '0' && (*ident)[1] != ' ')
286 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
380 + return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
381 if (date_overflows(strtoul(*ident, &end, 10)))
288 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow");
382 + return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
383 if (end == *ident || *end != ' ')
290 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
384 + return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
385 *ident = end + 1;
386 if ((**ident != '+' && **ident != '-') ||
387 !isdigit((*ident)[1]) ||
@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
389 !isdigit((*ident)[3]) ||
390 !isdigit((*ident)[4]) ||
391 ((*ident)[5] != '\n'))
298 - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad time zone");
392 + return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone");
393 (*ident) += 6;
394 return 0;
395 }
@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
406 return -1;
407
408 if (!skip_prefix(buffer, "tree ", &buffer))
315 - return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
409 + return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
410 if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n')
317 - return options->error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
411 + return report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
412 buffer += 41;
413 while (skip_prefix(buffer, "parent ", &buffer)) {
414 if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
321 - return options->error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
415 + return report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
416 buffer += 41;
417 parent_line_count++;
418 }
@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
422 if (graft->nr_parent == -1 && !parent_count)
423 ; /* shallow commit */
424 else if (graft->nr_parent != parent_count)
331 - return options->error_func(&commit->object, FSCK_ERROR, "graft objects missing");
425 + return report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing");
426 } else {
427 if (parent_count != parent_line_count)
334 - return options->error_func(&commit->object, FSCK_ERROR, "parent objects missing");
428 + return report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing");
429 }
430 if (!skip_prefix(buffer, "author ", &buffer))
337 - return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
431 + return report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
432 err = fsck_ident(&buffer, &commit->object, options);
433 if (err)
434 return err;
435 if (!skip_prefix(buffer, "committer ", &buffer))
342 - return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
436 + return report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
437 err = fsck_ident(&buffer, &commit->object, options);
438 if (err)
439 return err;
440 if (!commit->tree)
347 - return options->error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
441 + return report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
442
443 return 0;
444 }
@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
470 buffer = to_free =
471 read_sha1_file(tag->object.sha1, &type, &size);
472 if (!buffer)
379 - return options->error_func(&tag->object, FSCK_ERROR,
473 + return report(options, &tag->object,
474 + FSCK_MSG_MISSING_TAG_OBJECT,
475 "cannot read tag object");
476
477 if (type != OBJ_TAG) {
383 - ret = options->error_func(&tag->object, FSCK_ERROR,
478 + ret = report(options, &tag->object,
479 + FSCK_MSG_TAG_OBJECT_NOT_TAG,
480 "expected tag got %s",
481 typename(type));
482 goto done;
@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
487 goto done;
488
489 if (!skip_prefix(buffer, "object ", &buffer)) {
394 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'object' line");
490 + ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
491 goto done;
492 }
493 if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') {
398 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid 'object' line format - bad sha1");
494 + ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
495 goto done;
496 }
497 buffer += 41;
498
499 if (!skip_prefix(buffer, "type ", &buffer)) {
404 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'type' line");
500 + ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
501 goto done;
502 }
503 eol = strchr(buffer, '\n');
504 if (!eol) {
409 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line");
505 + ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
506 goto done;
507 }
508 if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
413 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid 'type' value");
509 + ret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
510 if (ret)
511 goto done;
512 buffer = eol + 1;
513
514 if (!skip_prefix(buffer, "tag ", &buffer)) {
419 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'tag' line");
515 + ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
516 goto done;
517 }
518 eol = strchr(buffer, '\n');
519 if (!eol) {
424 - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line");
520 + ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
521 goto done;
522 }
523 strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer);
524 if (check_refname_format(sb.buf, 0))
429 - options->error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %.*s",
525 + report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,
526 + "invalid 'tag' name: %.*s",
527 (int)(eol - buffer), buffer);
528 buffer = eol + 1;
529
530 if (!skip_prefix(buffer, "tagger ", &buffer))
531 /* early tags do not contain 'tagger' lines; warn only */
435 - options->error_func(&tag->object, FSCK_WARN, "invalid format - expected 'tagger' line");
532 + report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
533 else
534 ret = fsck_ident(&buffer, &tag->object, options);
535
@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,
545 struct object *tagged = tag->tagged;
546
547 if (!tagged)
451 - return options->error_func(&tag->object, FSCK_ERROR, "could not load tagged object");
548 + return report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, "could not load tagged object");
549
550 return fsck_tag_buffer(tag, data, size, options);
551 }
@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
554 struct fsck_options *options)
555 {
556 if (!obj)
460 - return options->error_func(obj, FSCK_ERROR, "no valid object to fsck");
557 + return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
558
559 if (obj->type == OBJ_BLOB)
560 return 0;
@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
567 return fsck_tag((struct tag *) obj, (const char *) data,
568 size, options);
569
473 - return options->error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)",
570 + return report(options, obj, FSCK_MSG_UNKNOWN_TYPE, "unknown type '%d' (internal fsck error)",
571 obj->type);
572 }
573
477 -int fsck_error_function(struct object *obj, int type, const char *fmt, ...)
574 +int fsck_error_function(struct object *obj, int msg_type, const char *message)
575 {
479 - va_list ap;
480 - struct strbuf sb = STRBUF_INIT;
481 -
482 - strbuf_addf(&sb, "object %s:", sha1_to_hex(obj->sha1));
483 -
484 - va_start(ap, fmt);
485 - strbuf_vaddf(&sb, fmt, ap);
486 - va_end(ap);
487 -
488 - error("%s", sb.buf);
489 - strbuf_release(&sb);
576 + error("object %s: %s", sha1_to_hex(obj->sha1), message);
577 return 1;
578 }
fsck.h
+2 -3
@@ -17,10 +17,9 @@ struct fsck_options;
17 typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);
18
19 /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
20 -typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);
20 +typedef int (*fsck_error)(struct object *obj, int type, const char *message);
21
22 -__attribute__((format (printf, 3, 4)))
23 -int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
22 +int fsck_error_function(struct object *obj, int type, const char *message);
23
24 struct fsck_options {
25 fsck_walk_func walk;