]> git.ipfire.org Git - thirdparty/git.git/blame - fsck.c
fsck_describe_object(): build on our get_object_name() primitive
[thirdparty/git.git] / fsck.c
CommitLineData
355885d5 1#include "cache.h"
cbd53a21 2#include "object-store.h"
109cd76d 3#include "repository.h"
355885d5
MK
4#include "object.h"
5#include "blob.h"
6#include "tree.h"
7#include "tree-walk.h"
8#include "commit.h"
9#include "tag.h"
10#include "fsck.h"
cec097be 11#include "refs.h"
a18fcc9f 12#include "utf8.h"
7b35efd7 13#include "decorate.h"
159e7b08 14#include "oidset.h"
27387444 15#include "packfile.h"
ed8b10f6
JK
16#include "submodule-config.h"
17#include "config.h"
3ac68a93 18#include "help.h"
159e7b08
JK
19
20static struct oidset gitmodules_found = OIDSET_INIT;
21static struct oidset gitmodules_done = OIDSET_INIT;
355885d5 22
f50c4407 23#define FSCK_FATAL -1
f27d05b1 24#define FSCK_INFO -2
f50c4407 25
c99ba492 26#define FOREACH_MSG_ID(FUNC) \
f50c4407
JS
27 /* fatal errors */ \
28 FUNC(NUL_IN_HEADER, FATAL) \
29 FUNC(UNTERMINATED_HEADER, FATAL) \
c99ba492
JS
30 /* errors */ \
31 FUNC(BAD_DATE, ERROR) \
32 FUNC(BAD_DATE_OVERFLOW, ERROR) \
33 FUNC(BAD_EMAIL, ERROR) \
34 FUNC(BAD_NAME, ERROR) \
35 FUNC(BAD_OBJECT_SHA1, ERROR) \
36 FUNC(BAD_PARENT_SHA1, ERROR) \
37 FUNC(BAD_TAG_OBJECT, ERROR) \
38 FUNC(BAD_TIMEZONE, ERROR) \
39 FUNC(BAD_TREE, ERROR) \
40 FUNC(BAD_TREE_SHA1, ERROR) \
41 FUNC(BAD_TYPE, ERROR) \
42 FUNC(DUPLICATE_ENTRIES, ERROR) \
43 FUNC(MISSING_AUTHOR, ERROR) \
44 FUNC(MISSING_COMMITTER, ERROR) \
45 FUNC(MISSING_EMAIL, ERROR) \
c99ba492
JS
46 FUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \
47 FUNC(MISSING_OBJECT, ERROR) \
c99ba492
JS
48 FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \
49 FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \
50 FUNC(MISSING_TAG, ERROR) \
51 FUNC(MISSING_TAG_ENTRY, ERROR) \
c99ba492 52 FUNC(MISSING_TREE, ERROR) \
159e7b08 53 FUNC(MISSING_TREE_OBJECT, ERROR) \
c99ba492
JS
54 FUNC(MISSING_TYPE, ERROR) \
55 FUNC(MISSING_TYPE_ENTRY, ERROR) \
c9ad147f 56 FUNC(MULTIPLE_AUTHORS, ERROR) \
c99ba492
JS
57 FUNC(TREE_NOT_SORTED, ERROR) \
58 FUNC(UNKNOWN_TYPE, ERROR) \
c99ba492 59 FUNC(ZERO_PADDED_DATE, ERROR) \
159e7b08
JK
60 FUNC(GITMODULES_MISSING, ERROR) \
61 FUNC(GITMODULES_BLOB, ERROR) \
0d68764d 62 FUNC(GITMODULES_LARGE, ERROR) \
ed8b10f6 63 FUNC(GITMODULES_NAME, ERROR) \
b7b1fca1 64 FUNC(GITMODULES_SYMLINK, ERROR) \
a124133e 65 FUNC(GITMODULES_URL, ERROR) \
1a7fd1fb 66 FUNC(GITMODULES_PATH, ERROR) \
c99ba492
JS
67 /* warnings */ \
68 FUNC(BAD_FILEMODE, WARN) \
c99ba492
JS
69 FUNC(EMPTY_NAME, WARN) \
70 FUNC(FULL_PATHNAME, WARN) \
71 FUNC(HAS_DOT, WARN) \
72 FUNC(HAS_DOTDOT, WARN) \
73 FUNC(HAS_DOTGIT, WARN) \
c99ba492 74 FUNC(NULL_SHA1, WARN) \
f27d05b1 75 FUNC(ZERO_PADDED_FILEMODE, WARN) \
6d2d780f 76 FUNC(NUL_IN_COMMIT, WARN) \
f27d05b1 77 /* infos (reported as warnings, but ignored by default) */ \
64eb14d3 78 FUNC(GITMODULES_PARSE, INFO) \
f27d05b1
JS
79 FUNC(BAD_TAG_NAME, INFO) \
80 FUNC(MISSING_TAGGER_ENTRY, INFO)
c99ba492
JS
81
82#define MSG_ID(id, msg_type) FSCK_MSG_##id,
83enum fsck_msg_id {
84 FOREACH_MSG_ID(MSG_ID)
85 FSCK_MSG_MAX
86};
87#undef MSG_ID
88
f417eed8 89#define STR(x) #x
a4a9cc19 90#define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
c99ba492 91static struct {
f417eed8
JS
92 const char *id_string;
93 const char *downcased;
a4a9cc19 94 const char *camelcased;
c99ba492
JS
95 int msg_type;
96} msg_id_info[FSCK_MSG_MAX + 1] = {
97 FOREACH_MSG_ID(MSG_ID)
a4a9cc19 98 { NULL, NULL, NULL, -1 }
c99ba492
JS
99};
100#undef MSG_ID
101
a46baac6 102static void prepare_msg_ids(void)
f417eed8
JS
103{
104 int i;
105
a46baac6
NTND
106 if (msg_id_info[0].downcased)
107 return;
108
109 /* convert id_string to lower case, without underscores. */
110 for (i = 0; i < FSCK_MSG_MAX; i++) {
111 const char *p = msg_id_info[i].id_string;
112 int len = strlen(p);
113 char *q = xmalloc(len);
114
115 msg_id_info[i].downcased = q;
116 while (*p)
117 if (*p == '_')
118 p++;
119 else
120 *(q)++ = tolower(*(p)++);
121 *q = '\0';
a4a9cc19
NTND
122
123 p = msg_id_info[i].id_string;
124 q = xmalloc(len);
125 msg_id_info[i].camelcased = q;
126 while (*p) {
127 if (*p == '_') {
128 p++;
129 if (*p)
130 *q++ = *p++;
131 } else {
132 *q++ = tolower(*p++);
133 }
f417eed8 134 }
a4a9cc19 135 *q = '\0';
f417eed8 136 }
a46baac6
NTND
137}
138
139static int parse_msg_id(const char *text)
140{
141 int i;
142
143 prepare_msg_ids();
f417eed8
JS
144
145 for (i = 0; i < FSCK_MSG_MAX; i++)
146 if (!strcmp(text, msg_id_info[i].downcased))
147 return i;
148
149 return -1;
150}
151
3ac68a93
NTND
152void list_config_fsck_msg_ids(struct string_list *list, const char *prefix)
153{
154 int i;
155
156 prepare_msg_ids();
157
3ac68a93 158 for (i = 0; i < FSCK_MSG_MAX; i++)
a4a9cc19 159 list_config_item(list, prefix, msg_id_info[i].camelcased);
3ac68a93
NTND
160}
161
c99ba492
JS
162static int fsck_msg_type(enum fsck_msg_id msg_id,
163 struct fsck_options *options)
164{
165 int msg_type;
166
0282f4dc
JS
167 assert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);
168
169 if (options->msg_type)
170 msg_type = options->msg_type[msg_id];
171 else {
172 msg_type = msg_id_info[msg_id].msg_type;
173 if (options->strict && msg_type == FSCK_WARN)
174 msg_type = FSCK_ERROR;
175 }
c99ba492
JS
176
177 return msg_type;
178}
179
0282f4dc
JS
180static int parse_msg_type(const char *str)
181{
182 if (!strcmp(str, "error"))
183 return FSCK_ERROR;
184 else if (!strcmp(str, "warn"))
185 return FSCK_WARN;
efaba7cc
JS
186 else if (!strcmp(str, "ignore"))
187 return FSCK_IGNORE;
0282f4dc
JS
188 else
189 die("Unknown fsck message type: '%s'", str);
190}
191
5d477a33
JS
192int is_valid_msg_type(const char *msg_id, const char *msg_type)
193{
194 if (parse_msg_id(msg_id) < 0)
195 return 0;
196 parse_msg_type(msg_type);
197 return 1;
198}
199
0282f4dc
JS
200void fsck_set_msg_type(struct fsck_options *options,
201 const char *msg_id, const char *msg_type)
202{
203 int id = parse_msg_id(msg_id), type;
204
205 if (id < 0)
206 die("Unhandled message id: %s", msg_id);
207 type = parse_msg_type(msg_type);
208
f50c4407
JS
209 if (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL)
210 die("Cannot demote %s to %s", msg_id, msg_type);
211
0282f4dc
JS
212 if (!options->msg_type) {
213 int i;
b32fa95f
JK
214 int *msg_type;
215 ALLOC_ARRAY(msg_type, FSCK_MSG_MAX);
0282f4dc
JS
216 for (i = 0; i < FSCK_MSG_MAX; i++)
217 msg_type[i] = fsck_msg_type(i, options);
218 options->msg_type = msg_type;
219 }
220
221 options->msg_type[id] = type;
222}
223
224void fsck_set_msg_types(struct fsck_options *options, const char *values)
225{
226 char *buf = xstrdup(values), *to_free = buf;
227 int done = 0;
228
229 while (!done) {
230 int len = strcspn(buf, " ,|"), equal;
231
232 done = !buf[len];
233 if (!len) {
234 buf++;
235 continue;
236 }
237 buf[len] = '\0';
238
239 for (equal = 0;
240 equal < len && buf[equal] != '=' && buf[equal] != ':';
241 equal++)
242 buf[equal] = tolower(buf[equal]);
243 buf[equal] = '\0';
244
cd94c6f9
JS
245 if (!strcmp(buf, "skiplist")) {
246 if (equal == len)
247 die("skiplist requires a path");
24eb33eb 248 oidset_parse_file(&options->skiplist, buf + equal + 1);
cd94c6f9
JS
249 buf += len + 1;
250 continue;
251 }
252
0282f4dc
JS
253 if (equal == len)
254 die("Missing '=': '%s'", buf);
355885d5 255
0282f4dc
JS
256 fsck_set_msg_type(options, buf, buf + equal + 1);
257 buf += len + 1;
258 }
259 free(to_free);
260}
261
71ab8fa8
JS
262static void append_msg_id(struct strbuf *sb, const char *msg_id)
263{
264 for (;;) {
265 char c = *(msg_id)++;
266
267 if (!c)
268 break;
269 if (c != '_')
270 strbuf_addch(sb, tolower(c));
271 else {
272 assert(*msg_id);
273 strbuf_addch(sb, *(msg_id)++);
274 }
275 }
276
277 strbuf_addstr(sb, ": ");
278}
279
fb162877
RJ
280static int object_on_skiplist(struct fsck_options *opts, struct object *obj)
281{
3b41fb0c 282 return opts && obj && oidset_contains(&opts->skiplist, &obj->oid);
fb162877
RJ
283}
284
c99ba492
JS
285__attribute__((format (printf, 4, 5)))
286static int report(struct fsck_options *options, struct object *object,
287 enum fsck_msg_id id, const char *fmt, ...)
288{
289 va_list ap;
290 struct strbuf sb = STRBUF_INIT;
291 int msg_type = fsck_msg_type(id, options), result;
292
efaba7cc
JS
293 if (msg_type == FSCK_IGNORE)
294 return 0;
295
fb162877 296 if (object_on_skiplist(options, object))
cd94c6f9
JS
297 return 0;
298
f50c4407
JS
299 if (msg_type == FSCK_FATAL)
300 msg_type = FSCK_ERROR;
f27d05b1
JS
301 else if (msg_type == FSCK_INFO)
302 msg_type = FSCK_WARN;
f50c4407 303
71ab8fa8
JS
304 append_msg_id(&sb, msg_id_info[id].id_string);
305
c99ba492
JS
306 va_start(ap, fmt);
307 strbuf_vaddf(&sb, fmt, ap);
1cd772cc 308 result = options->error_func(options, object, msg_type, sb.buf);
c99ba492
JS
309 strbuf_release(&sb);
310 va_end(ap);
311
312 return result;
313}
314
a59cfb32
JK
315void fsck_enable_object_names(struct fsck_options *options)
316{
317 if (!options->object_names)
318 options->object_names = xcalloc(1, sizeof(struct decoration));
319}
320
321const char *fsck_get_object_name(struct fsck_options *options, struct object *obj)
7b35efd7
JS
322{
323 if (!options->object_names)
324 return NULL;
325 return lookup_decoration(options->object_names, obj);
326}
327
a59cfb32
JK
328void fsck_put_object_name(struct fsck_options *options, struct object *obj,
329 const char *fmt, ...)
7b35efd7
JS
330{
331 va_list ap;
332 struct strbuf buf = STRBUF_INIT;
333 char *existing;
334
335 if (!options->object_names)
336 return;
337 existing = lookup_decoration(options->object_names, obj);
338 if (existing)
339 return;
340 va_start(ap, fmt);
341 strbuf_vaddf(&buf, fmt, ap);
342 add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
343 va_end(ap);
344}
345
a59cfb32
JK
346const char *fsck_describe_object(struct fsck_options *options,
347 struct object *obj)
90cf590f 348{
a59cfb32
JK
349 static struct strbuf bufs[] = {
350 STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
351 };
352 static int b = 0;
353 struct strbuf *buf;
d40bbc10 354 const char *name = fsck_get_object_name(options, obj);
a59cfb32
JK
355
356 buf = bufs + b;
357 b = (b + 1) % ARRAY_SIZE(bufs);
358 strbuf_reset(buf);
359 strbuf_addstr(buf, oid_to_hex(&obj->oid));
360 if (name)
361 strbuf_addf(buf, " (%s)", name);
90cf590f 362
a59cfb32 363 return buf->buf;
90cf590f
JS
364}
365
22410549 366static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
355885d5
MK
367{
368 struct tree_desc desc;
369 struct name_entry entry;
370 int res = 0;
7b35efd7 371 const char *name;
355885d5
MK
372
373 if (parse_tree(tree))
374 return -1;
375
a59cfb32 376 name = fsck_get_object_name(options, &tree->object);
8354fa3d
DT
377 if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
378 return -1;
379 while (tree_entry_gently(&desc, &entry)) {
7b35efd7 380 struct object *obj;
355885d5
MK
381 int result;
382
383 if (S_ISGITLINK(entry.mode))
384 continue;
7b35efd7
JS
385
386 if (S_ISDIR(entry.mode)) {
ea82b2a0 387 obj = (struct object *)lookup_tree(the_repository, &entry.oid);
2720f6db 388 if (name && obj)
a59cfb32
JK
389 fsck_put_object_name(options, obj, "%s%s/",
390 name, entry.path);
7b35efd7
JS
391 result = options->walk(obj, OBJ_TREE, data, options);
392 }
393 else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
ea82b2a0 394 obj = (struct object *)lookup_blob(the_repository, &entry.oid);
2720f6db 395 if (name && obj)
a59cfb32
JK
396 fsck_put_object_name(options, obj, "%s%s",
397 name, entry.path);
7b35efd7
JS
398 result = options->walk(obj, OBJ_BLOB, data, options);
399 }
355885d5 400 else {
82247e9b 401 result = error("in tree %s: entry %s has bad mode %.6o",
a59cfb32
JK
402 fsck_describe_object(options, &tree->object),
403 entry.path, entry.mode);
355885d5
MK
404 }
405 if (result < 0)
406 return result;
407 if (!res)
408 res = result;
409 }
410 return res;
411}
412
22410549 413static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
355885d5 414{
7b35efd7 415 int counter = 0, generation = 0, name_prefix_len = 0;
355885d5
MK
416 struct commit_list *parents;
417 int res;
418 int result;
7b35efd7 419 const char *name;
355885d5
MK
420
421 if (parse_commit(commit))
422 return -1;
423
a59cfb32 424 name = fsck_get_object_name(options, &commit->object);
7b35efd7 425 if (name)
a59cfb32
JK
426 fsck_put_object_name(options, &get_commit_tree(commit)->object,
427 "%s:", name);
7b35efd7 428
2e27bd77
DS
429 result = options->walk((struct object *)get_commit_tree(commit),
430 OBJ_TREE, data, options);
355885d5
MK
431 if (result < 0)
432 return result;
433 res = result;
434
435 parents = commit->parents;
7b35efd7
JS
436 if (name && parents) {
437 int len = strlen(name), power;
438
439 if (len && name[len - 1] == '^') {
440 generation = 1;
441 name_prefix_len = len - 1;
442 }
443 else { /* parse ~<generation> suffix */
444 for (generation = 0, power = 1;
445 len && isdigit(name[len - 1]);
446 power *= 10)
447 generation += power * (name[--len] - '0');
448 if (power > 1 && len && name[len - 1] == '~')
449 name_prefix_len = len - 1;
450 }
451 }
452
355885d5 453 while (parents) {
7b35efd7
JS
454 if (name) {
455 struct object *obj = &parents->item->object;
456
b84c7838 457 if (counter++)
a59cfb32
JK
458 fsck_put_object_name(options, obj, "%s^%d",
459 name, counter);
7b35efd7 460 else if (generation > 0)
a59cfb32
JK
461 fsck_put_object_name(options, obj, "%.*s~%d",
462 name_prefix_len, name,
463 generation + 1);
7b35efd7 464 else
a59cfb32
JK
465 fsck_put_object_name(options, obj, "%s^",
466 name);
7b35efd7 467 }
22410549 468 result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
355885d5
MK
469 if (result < 0)
470 return result;
471 if (!res)
472 res = result;
473 parents = parents->next;
474 }
475 return res;
476}
477
22410549 478static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
355885d5 479{
a59cfb32 480 const char *name = fsck_get_object_name(options, &tag->object);
7b35efd7 481
355885d5
MK
482 if (parse_tag(tag))
483 return -1;
7b35efd7 484 if (name)
a59cfb32 485 fsck_put_object_name(options, tag->tagged, "%s", name);
22410549 486 return options->walk(tag->tagged, OBJ_ANY, data, options);
355885d5
MK
487}
488
22410549 489int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
355885d5
MK
490{
491 if (!obj)
492 return -1;
a2b22854
JK
493
494 if (obj->type == OBJ_NONE)
109cd76d 495 parse_object(the_repository, &obj->oid);
a2b22854 496
355885d5
MK
497 switch (obj->type) {
498 case OBJ_BLOB:
499 return 0;
500 case OBJ_TREE:
22410549 501 return fsck_walk_tree((struct tree *)obj, data, options);
355885d5 502 case OBJ_COMMIT:
22410549 503 return fsck_walk_commit((struct commit *)obj, data, options);
355885d5 504 case OBJ_TAG:
22410549 505 return fsck_walk_tag((struct tag *)obj, data, options);
355885d5 506 default:
a59cfb32
JK
507 error("Unknown object type for %s",
508 fsck_describe_object(options, obj));
355885d5
MK
509 return -1;
510 }
511}
ba002f3b
MK
512
513/*
514 * The entries in a tree are ordered in the _path_ order,
515 * which means that a directory entry is ordered by adding
516 * a slash to the end of it.
517 *
518 * So a directory called "a" is ordered _after_ a file
519 * called "a.c", because "a/" sorts after "a.c".
520 */
521#define TREE_UNORDERED (-1)
522#define TREE_HAS_DUPS (-2)
523
524static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
525{
526 int len1 = strlen(name1);
527 int len2 = strlen(name2);
528 int len = len1 < len2 ? len1 : len2;
529 unsigned char c1, c2;
530 int cmp;
531
532 cmp = memcmp(name1, name2, len);
533 if (cmp < 0)
534 return 0;
535 if (cmp > 0)
536 return TREE_UNORDERED;
537
538 /*
539 * Ok, the first <len> characters are the same.
540 * Now we need to order the next one, but turn
541 * a '\0' into a '/' for a directory entry.
542 */
543 c1 = name1[len];
544 c2 = name2[len];
545 if (!c1 && !c2)
546 /*
547 * git-write-tree used to write out a nonsense tree that has
548 * entries with the same name, one blob and one tree. Make
549 * sure we do not have duplicate entries.
550 */
551 return TREE_HAS_DUPS;
552 if (!c1 && S_ISDIR(mode1))
553 c1 = '/';
554 if (!c2 && S_ISDIR(mode2))
555 c2 = '/';
556 return c1 < c2 ? 0 : TREE_UNORDERED;
557}
558
23a173a7
JK
559static int fsck_tree(struct tree *item,
560 const char *buffer, unsigned long size,
561 struct fsck_options *options)
ba002f3b 562{
8354fa3d 563 int retval = 0;
c479d14a 564 int has_null_sha1 = 0;
ba002f3b
MK
565 int has_full_path = 0;
566 int has_empty_name = 0;
5d34a435
JK
567 int has_dot = 0;
568 int has_dotdot = 0;
5c17f512 569 int has_dotgit = 0;
ba002f3b
MK
570 int has_zero_pad = 0;
571 int has_bad_modes = 0;
572 int has_dup_entries = 0;
573 int not_properly_sorted = 0;
574 struct tree_desc desc;
575 unsigned o_mode;
576 const char *o_name;
ba002f3b 577
23a173a7 578 if (init_tree_desc_gently(&desc, buffer, size)) {
8354fa3d
DT
579 retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
580 return retval;
581 }
ba002f3b
MK
582
583 o_mode = 0;
584 o_name = NULL;
ba002f3b
MK
585
586 while (desc.size) {
5ec1e728 587 unsigned short mode;
ba002f3b 588 const char *name;
ce6663a9 589 const struct object_id *oid;
ba002f3b 590
ce6663a9 591 oid = tree_entry_extract(&desc, &name, &mode);
ba002f3b 592
ce6663a9 593 has_null_sha1 |= is_null_oid(oid);
effd12ec
HS
594 has_full_path |= !!strchr(name, '/');
595 has_empty_name |= !*name;
596 has_dot |= !strcmp(name, ".");
597 has_dotdot |= !strcmp(name, "..");
ed9c3220 598 has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name);
ba002f3b 599 has_zero_pad |= *(char *)desc.buffer == '0';
159e7b08 600
b7b1fca1
JK
601 if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
602 if (!S_ISLNK(mode))
603 oidset_insert(&gitmodules_found, oid);
604 else
605 retval += report(options, &item->object,
606 FSCK_MSG_GITMODULES_SYMLINK,
607 ".gitmodules is a symbolic link");
608 }
159e7b08 609
8354fa3d
DT
610 if (update_tree_entry_gently(&desc)) {
611 retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
612 break;
613 }
ba002f3b
MK
614
615 switch (mode) {
616 /*
617 * Standard modes..
618 */
619 case S_IFREG | 0755:
620 case S_IFREG | 0644:
621 case S_IFLNK:
622 case S_IFDIR:
623 case S_IFGITLINK:
624 break;
625 /*
626 * This is nonstandard, but we had a few of these
627 * early on when we honored the full set of mode
628 * bits..
629 */
630 case S_IFREG | 0664:
22410549 631 if (!options->strict)
ba002f3b 632 break;
1cf01a34 633 /* fallthrough */
ba002f3b
MK
634 default:
635 has_bad_modes = 1;
636 }
637
638 if (o_name) {
639 switch (verify_ordered(o_mode, o_name, mode, name)) {
640 case TREE_UNORDERED:
641 not_properly_sorted = 1;
642 break;
643 case TREE_HAS_DUPS:
644 has_dup_entries = 1;
645 break;
646 default:
647 break;
648 }
649 }
650
651 o_mode = mode;
652 o_name = name;
ba002f3b
MK
653 }
654
c479d14a 655 if (has_null_sha1)
c99ba492 656 retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
ba002f3b 657 if (has_full_path)
c99ba492 658 retval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
ba002f3b 659 if (has_empty_name)
c99ba492 660 retval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
5d34a435 661 if (has_dot)
c99ba492 662 retval += report(options, &item->object, FSCK_MSG_HAS_DOT, "contains '.'");
5d34a435 663 if (has_dotdot)
c99ba492 664 retval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, "contains '..'");
5c17f512 665 if (has_dotgit)
c99ba492 666 retval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
ba002f3b 667 if (has_zero_pad)
c99ba492 668 retval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
ba002f3b 669 if (has_bad_modes)
c99ba492 670 retval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
ba002f3b 671 if (has_dup_entries)
c99ba492 672 retval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
ba002f3b 673 if (not_properly_sorted)
c99ba492 674 retval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
ba002f3b
MK
675 return retval;
676}
677
84d18c0b 678static int verify_headers(const void *data, unsigned long size,
b2f44feb 679 struct object *obj, struct fsck_options *options)
4d0d8975
JS
680{
681 const char *buffer = (const char *)data;
682 unsigned long i;
683
684 for (i = 0; i < size; i++) {
685 switch (buffer[i]) {
686 case '\0':
c99ba492
JS
687 return report(options, obj,
688 FSCK_MSG_NUL_IN_HEADER,
689 "unterminated header: NUL at offset %ld", i);
4d0d8975
JS
690 case '\n':
691 if (i + 1 < size && buffer[i + 1] == '\n')
692 return 0;
693 }
694 }
695
84d18c0b
JH
696 /*
697 * We did not find double-LF that separates the header
698 * and the body. Not having a body is not a crime but
699 * we do want to see the terminating LF for the last header
700 * line.
701 */
702 if (size && buffer[size - 1] == '\n')
703 return 0;
704
c99ba492
JS
705 return report(options, obj,
706 FSCK_MSG_UNTERMINATED_HEADER, "unterminated header");
4d0d8975
JS
707}
708
22410549 709static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)
daae1922 710{
e6826e33 711 const char *p = *ident;
d4b8de04
JK
712 char *end;
713
e6826e33
JS
714 *ident = strchrnul(*ident, '\n');
715 if (**ident == '\n')
716 (*ident)++;
717
718 if (*p == '<')
c99ba492 719 return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
e6826e33
JS
720 p += strcspn(p, "<>\n");
721 if (*p == '>')
c99ba492 722 return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name");
e6826e33 723 if (*p != '<')
c99ba492 724 return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email");
e6826e33 725 if (p[-1] != ' ')
c99ba492 726 return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
e6826e33
JS
727 p++;
728 p += strcspn(p, "<>\n");
729 if (*p != '>')
c99ba492 730 return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email");
e6826e33
JS
731 p++;
732 if (*p != ' ')
c99ba492 733 return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
e6826e33
JS
734 p++;
735 if (*p == '0' && p[1] != ' ')
c99ba492 736 return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
1aeb7e75 737 if (date_overflows(parse_timestamp(p, &end, 10)))
c99ba492 738 return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
e6826e33 739 if ((end == p || *end != ' '))
c99ba492 740 return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
e6826e33
JS
741 p = end + 1;
742 if ((*p != '+' && *p != '-') ||
743 !isdigit(p[1]) ||
744 !isdigit(p[2]) ||
745 !isdigit(p[3]) ||
746 !isdigit(p[4]) ||
747 (p[5] != '\n'))
c99ba492 748 return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone");
e6826e33 749 p += 6;
daae1922
JN
750 return 0;
751}
752
23a173a7
JK
753static int fsck_commit(struct commit *commit, const char *buffer,
754 unsigned long size, struct fsck_options *options)
ba002f3b 755{
c54f5ca9 756 struct object_id tree_oid, oid;
ec652315 757 unsigned author_count;
daae1922 758 int err;
6d2d780f 759 const char *buffer_begin = buffer;
c54f5ca9 760 const char *p;
ba002f3b 761
b2f44feb 762 if (verify_headers(buffer, size, &commit->object, options))
4d0d8975
JS
763 return -1;
764
cf4fff57 765 if (!skip_prefix(buffer, "tree ", &buffer))
c99ba492 766 return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
c54f5ca9 767 if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
b3584761
JS
768 err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
769 if (err)
770 return err;
771 }
c54f5ca9 772 buffer = p + 1;
cf4fff57 773 while (skip_prefix(buffer, "parent ", &buffer)) {
c54f5ca9 774 if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') {
b3584761
JS
775 err = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
776 if (err)
777 return err;
778 }
c54f5ca9 779 buffer = p + 1;
ba002f3b 780 }
c9ad147f
JS
781 author_count = 0;
782 while (skip_prefix(buffer, "author ", &buffer)) {
783 author_count++;
784 err = fsck_ident(&buffer, &commit->object, options);
785 if (err)
786 return err;
ba002f3b 787 }
c9ad147f
JS
788 if (author_count < 1)
789 err = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
790 else if (author_count > 1)
791 err = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
daae1922
JN
792 if (err)
793 return err;
cf4fff57 794 if (!skip_prefix(buffer, "committer ", &buffer))
c99ba492 795 return report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
22410549 796 err = fsck_ident(&buffer, &commit->object, options);
daae1922
JN
797 if (err)
798 return err;
6d2d780f
JH
799 if (memchr(buffer_begin, '\0', size)) {
800 err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT,
801 "NUL byte in the commit object body");
802 if (err)
803 return err;
804 }
ba002f3b
MK
805 return 0;
806}
807
23a173a7 808static int fsck_tag(struct tag *tag, const char *buffer,
2175a0c6 809 unsigned long size, struct fsck_options *options)
cec097be 810{
c54f5ca9 811 struct object_id oid;
cec097be 812 int ret = 0;
23a173a7 813 char *eol;
cec097be 814 struct strbuf sb = STRBUF_INIT;
c54f5ca9 815 const char *p;
cec097be 816
8a272f29
RS
817 ret = verify_headers(buffer, size, &tag->object, options);
818 if (ret)
cec097be
JS
819 goto done;
820
821 if (!skip_prefix(buffer, "object ", &buffer)) {
c99ba492 822 ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
cec097be
JS
823 goto done;
824 }
c54f5ca9 825 if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') {
c99ba492 826 ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
7d7d5b05
JS
827 if (ret)
828 goto done;
cec097be 829 }
c54f5ca9 830 buffer = p + 1;
cec097be
JS
831
832 if (!skip_prefix(buffer, "type ", &buffer)) {
c99ba492 833 ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
cec097be
JS
834 goto done;
835 }
836 eol = strchr(buffer, '\n');
837 if (!eol) {
c99ba492 838 ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
cec097be
JS
839 goto done;
840 }
841 if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
c99ba492 842 ret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
cec097be
JS
843 if (ret)
844 goto done;
845 buffer = eol + 1;
846
847 if (!skip_prefix(buffer, "tag ", &buffer)) {
c99ba492 848 ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
cec097be
JS
849 goto done;
850 }
851 eol = strchr(buffer, '\n');
852 if (!eol) {
c99ba492 853 ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
cec097be
JS
854 goto done;
855 }
856 strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer);
f27d05b1
JS
857 if (check_refname_format(sb.buf, 0)) {
858 ret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,
c99ba492 859 "invalid 'tag' name: %.*s",
7add4419 860 (int)(eol - buffer), buffer);
f27d05b1
JS
861 if (ret)
862 goto done;
863 }
cec097be
JS
864 buffer = eol + 1;
865
f27d05b1 866 if (!skip_prefix(buffer, "tagger ", &buffer)) {
cec097be 867 /* early tags do not contain 'tagger' lines; warn only */
f27d05b1
JS
868 ret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
869 if (ret)
870 goto done;
871 }
cec097be 872 else
22410549 873 ret = fsck_ident(&buffer, &tag->object, options);
cec097be
JS
874
875done:
876 strbuf_release(&sb);
cec097be
JS
877 return ret;
878}
879
ed8b10f6
JK
880struct fsck_gitmodules_data {
881 struct object *obj;
882 struct fsck_options *options;
883 int ret;
884};
885
886static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
887{
888 struct fsck_gitmodules_data *data = vdata;
889 const char *subsection, *key;
890 int subsection_len;
891 char *name;
892
893 if (parse_config_key(var, "submodule", &subsection, &subsection_len, &key) < 0 ||
894 !subsection)
895 return 0;
896
897 name = xmemdupz(subsection, subsection_len);
898 if (check_submodule_name(name) < 0)
899 data->ret |= report(data->options, data->obj,
900 FSCK_MSG_GITMODULES_NAME,
901 "disallowed submodule name: %s",
902 name);
a124133e
JK
903 if (!strcmp(key, "url") && value &&
904 looks_like_command_line_option(value))
905 data->ret |= report(data->options, data->obj,
906 FSCK_MSG_GITMODULES_URL,
907 "disallowed submodule url: %s",
908 value);
1a7fd1fb
JK
909 if (!strcmp(key, "path") && value &&
910 looks_like_command_line_option(value))
911 data->ret |= report(data->options, data->obj,
912 FSCK_MSG_GITMODULES_PATH,
913 "disallowed submodule path: %s",
914 value);
ed8b10f6
JK
915 free(name);
916
917 return 0;
918}
919
7ac4f3a0
JK
920static int fsck_blob(struct blob *blob, const char *buf,
921 unsigned long size, struct fsck_options *options)
922{
ed8b10f6 923 struct fsck_gitmodules_data data;
de6bd9e3 924 struct config_options config_opts = { 0 };
ed8b10f6
JK
925
926 if (!oidset_contains(&gitmodules_found, &blob->object.oid))
927 return 0;
928 oidset_insert(&gitmodules_done, &blob->object.oid);
929
fb162877
RJ
930 if (object_on_skiplist(options, &blob->object))
931 return 0;
932
ed8b10f6
JK
933 if (!buf) {
934 /*
935 * A missing buffer here is a sign that the caller found the
936 * blob too gigantic to load into memory. Let's just consider
937 * that an error.
938 */
939 return report(options, &blob->object,
0d68764d 940 FSCK_MSG_GITMODULES_LARGE,
ed8b10f6
JK
941 ".gitmodules too large to parse");
942 }
943
944 data.obj = &blob->object;
945 data.options = options;
946 data.ret = 0;
de6bd9e3 947 config_opts.error_action = CONFIG_ERROR_SILENT;
ed8b10f6 948 if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
de6bd9e3 949 ".gitmodules", buf, size, &data, &config_opts))
ed8b10f6
JK
950 data.ret |= report(options, &blob->object,
951 FSCK_MSG_GITMODULES_PARSE,
952 "could not parse gitmodules blob");
953
954 return data.ret;
7ac4f3a0
JK
955}
956
90a398bb 957int fsck_object(struct object *obj, void *data, unsigned long size,
22410549 958 struct fsck_options *options)
ba002f3b
MK
959{
960 if (!obj)
c99ba492 961 return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
ba002f3b
MK
962
963 if (obj->type == OBJ_BLOB)
7ac4f3a0 964 return fsck_blob((struct blob *)obj, data, size, options);
ba002f3b 965 if (obj->type == OBJ_TREE)
23a173a7 966 return fsck_tree((struct tree *) obj, data, size, options);
ba002f3b 967 if (obj->type == OBJ_COMMIT)
90a398bb 968 return fsck_commit((struct commit *) obj, (const char *) data,
22410549 969 size, options);
ba002f3b 970 if (obj->type == OBJ_TAG)
90a398bb 971 return fsck_tag((struct tag *) obj, (const char *) data,
22410549 972 size, options);
ba002f3b 973
c99ba492 974 return report(options, obj, FSCK_MSG_UNKNOWN_TYPE, "unknown type '%d' (internal fsck error)",
ba002f3b
MK
975 obj->type);
976}
d6ffc8d7 977
1cd772cc
JS
978int fsck_error_function(struct fsck_options *o,
979 struct object *obj, int msg_type, const char *message)
d6ffc8d7 980{
0282f4dc 981 if (msg_type == FSCK_WARN) {
a59cfb32 982 warning("object %s: %s", fsck_describe_object(o, obj), message);
0282f4dc
JS
983 return 0;
984 }
a59cfb32 985 error("object %s: %s", fsck_describe_object(o, obj), message);
d6ffc8d7
MK
986 return 1;
987}
159e7b08
JK
988
989int fsck_finish(struct fsck_options *options)
990{
991 int ret = 0;
992 struct oidset_iter iter;
993 const struct object_id *oid;
994
995 oidset_iter_init(&gitmodules_found, &iter);
996 while ((oid = oidset_iter_next(&iter))) {
997 struct blob *blob;
998 enum object_type type;
999 unsigned long size;
1000 char *buf;
1001
1002 if (oidset_contains(&gitmodules_done, oid))
1003 continue;
1004
da14a7ff 1005 blob = lookup_blob(the_repository, oid);
159e7b08 1006 if (!blob) {
0ebbcf70 1007 struct object *obj = lookup_unknown_object(oid);
47cc9131 1008 ret |= report(options, obj,
159e7b08
JK
1009 FSCK_MSG_GITMODULES_BLOB,
1010 "non-blob found at .gitmodules");
1011 continue;
1012 }
1013
7913f53b 1014 buf = read_object_file(oid, &type, &size);
159e7b08 1015 if (!buf) {
27387444
JK
1016 if (is_promisor_object(&blob->object.oid))
1017 continue;
159e7b08
JK
1018 ret |= report(options, &blob->object,
1019 FSCK_MSG_GITMODULES_MISSING,
1020 "unable to read .gitmodules blob");
1021 continue;
1022 }
1023
1024 if (type == OBJ_BLOB)
1025 ret |= fsck_blob(blob, buf, size, options);
1026 else
1027 ret |= report(options, &blob->object,
1028 FSCK_MSG_GITMODULES_BLOB,
1029 "non-blob found at .gitmodules");
1030 free(buf);
1031 }
1032
1033
1034 oidset_clear(&gitmodules_found);
1035 oidset_clear(&gitmodules_done);
1036 return ret;
1037}