]> git.proxmox.com Git - libgit2.git/blob - src/index.c
Merge pull request #223 from carlosmn/valgrind
[libgit2.git] / src / index.c
1 /*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 #include <stddef.h>
27
28 #include "common.h"
29 #include "repository.h"
30 #include "index.h"
31 #include "hash.h"
32 #include "git2/odb.h"
33 #include "git2/blob.h"
34
35 #define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
36 #define short_entry_size(len) entry_size(struct entry_short, len)
37 #define long_entry_size(len) entry_size(struct entry_long, len)
38
39 #define minimal_entry_size (offsetof(struct entry_short, path))
40
41 static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
42 static const size_t INDEX_HEADER_SIZE = 12;
43
44 static const unsigned int INDEX_VERSION_NUMBER = 2;
45 static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
46
47 static const unsigned int INDEX_HEADER_SIG = 0x44495243;
48 static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
49 static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
50
51 struct index_header {
52 uint32_t signature;
53 uint32_t version;
54 uint32_t entry_count;
55 };
56
57 struct index_extension {
58 char signature[4];
59 uint32_t extension_size;
60 };
61
62 struct entry_time {
63 uint32_t seconds;
64 uint32_t nanoseconds;
65 };
66
67 struct entry_short {
68 struct entry_time ctime;
69 struct entry_time mtime;
70 uint32_t dev;
71 uint32_t ino;
72 uint32_t mode;
73 uint32_t uid;
74 uint32_t gid;
75 uint32_t file_size;
76 git_oid oid;
77 uint16_t flags;
78 char path[1]; /* arbitrary length */
79 };
80
81 struct entry_long {
82 struct entry_time ctime;
83 struct entry_time mtime;
84 uint32_t dev;
85 uint32_t ino;
86 uint32_t mode;
87 uint32_t uid;
88 uint32_t gid;
89 uint32_t file_size;
90 git_oid oid;
91 uint16_t flags;
92 uint16_t flags_extended;
93 char path[1]; /* arbitrary length */
94 };
95
96 /* local declarations */
97 static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
98 static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
99 static int read_header(struct index_header *dest, const void *buffer);
100
101 static int read_tree(git_index *index, const char *buffer, size_t buffer_size);
102 static int read_tree_internal(git_index_tree **, const char **, const char *, git_index_tree *);
103
104 static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
105 static int is_index_extended(git_index *index);
106 static void sort_index(git_index *index);
107 static int write_index(git_index *index, git_filebuf *file);
108
109 int index_srch(const void *key, const void *array_member)
110 {
111 const char *filename = (const char *)key;
112 const git_index_entry *entry = *(const git_index_entry **)(array_member);
113
114 return strcmp(filename, entry->path);
115 }
116
117 int index_cmp(const void *a, const void *b)
118 {
119 const git_index_entry *entry_a = *(const git_index_entry **)(a);
120 const git_index_entry *entry_b = *(const git_index_entry **)(b);
121
122 return strcmp(entry_a->path, entry_b->path);
123 }
124
125 int unmerged_srch(const void *key, const void *array_member)
126 {
127 const char *path = (const char *) key;
128 const git_index_entry_unmerged *entry = *(const git_index_entry_unmerged **) (array_member);
129
130 return strcmp(path, entry->path);
131 }
132
133 int unmerged_cmp(const void *a, const void *b)
134 {
135 const git_index_entry_unmerged *info_a = *(const git_index_entry_unmerged **)(a);
136 const git_index_entry_unmerged *info_b = *(const git_index_entry_unmerged **)(b);
137
138 return strcmp(info_a->path, info_b->path);
139 }
140
141 static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path)
142 {
143 git_index *index;
144
145 assert(index_out && index_path);
146
147 index = git__malloc(sizeof(git_index));
148 if (index == NULL)
149 return GIT_ENOMEM;
150
151 memset(index, 0x0, sizeof(git_index));
152
153 index->index_file_path = git__strdup(index_path);
154 if (index->index_file_path == NULL) {
155 free(index);
156 return GIT_ENOMEM;
157 }
158
159 index->repository = owner;
160
161 git_vector_init(&index->entries, 32, index_cmp);
162
163 /* Check if index file is stored on disk already */
164 if (gitfo_exists(index->index_file_path) == 0)
165 index->on_disk = 1;
166
167 *index_out = index;
168 return git_index_read(index);
169 }
170
171 int git_index_open(git_index **index_out, const char *index_path)
172 {
173 assert(index_out && index_path);
174 return index_initialize(index_out, NULL, index_path);
175 }
176
177 /*
178 * Moved from `repository.c`
179 */
180 int git_repository_index(git_index **index_out, git_repository *repo)
181 {
182 assert(index_out && repo);
183
184 if (repo->is_bare)
185 return git__throw(GIT_EBAREINDEX, "Failed to open index. Repository is bare");
186
187 return index_initialize(index_out, repo, repo->path_index);
188 }
189
190 void git_index_free(git_index *index)
191 {
192 if (index == NULL)
193 return;
194
195 git_index_clear(index);
196 git_vector_free(&index->entries);
197 git_vector_free(&index->unmerged);
198
199 free(index->index_file_path);
200 free(index);
201 }
202
203 static void free_tree(git_index_tree *tree)
204 {
205 unsigned int i;
206
207 if (tree == NULL)
208 return;
209
210 for (i = 0; i < tree->children_count; ++i)
211 free_tree(tree->children[i]);
212
213 free(tree->name);
214 free(tree->children);
215 free(tree);
216 }
217
218 void git_index_clear(git_index *index)
219 {
220 unsigned int i;
221
222 assert(index);
223
224 for (i = 0; i < index->entries.length; ++i) {
225 git_index_entry *e;
226 e = git_vector_get(&index->entries, i);
227 free((char *)e->path);
228 free(e);
229 }
230
231 for (i = 0; i < index->unmerged.length; ++i) {
232 git_index_entry_unmerged *e;
233 e = git_vector_get(&index->unmerged, i);
234 free((char *)e->path);
235 free(e);
236 }
237
238 git_vector_clear(&index->entries);
239 git_vector_clear(&index->unmerged);
240 index->last_modified = 0;
241
242 free_tree(index->tree);
243 index->tree = NULL;
244 }
245
246 int git_index_read(git_index *index)
247 {
248 struct stat indexst;
249 int error = GIT_SUCCESS;
250
251 assert(index->index_file_path);
252
253 if (!index->on_disk || gitfo_exists(index->index_file_path) < 0) {
254 git_index_clear(index);
255 index->on_disk = 0;
256 return GIT_SUCCESS;
257 }
258
259 if (gitfo_stat(index->index_file_path, &indexst) < 0)
260 return git__throw(GIT_EOSERR, "Failed to read index. %s does not exist or is corrupted", index->index_file_path);
261
262 if (!S_ISREG(indexst.st_mode))
263 return git__throw(GIT_ENOTFOUND, "Failed to read index. %s is not an index file", index->index_file_path);
264
265 if (indexst.st_mtime != index->last_modified) {
266
267 gitfo_buf buffer;
268
269 if ((error = gitfo_read_file(&buffer, index->index_file_path)) < GIT_SUCCESS)
270 return git__rethrow(error, "Failed to read index");
271
272 git_index_clear(index);
273 error = parse_index(index, buffer.data, buffer.len);
274
275 if (error == GIT_SUCCESS)
276 index->last_modified = indexst.st_mtime;
277
278 gitfo_free_buf(&buffer);
279 }
280
281 if (error < GIT_SUCCESS)
282 return git__rethrow(error, "Failed to read index");
283 return error;
284 }
285
286 int git_index_write(git_index *index)
287 {
288 git_filebuf file;
289 struct stat indexst;
290 int error;
291
292 sort_index(index);
293
294 if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS)
295 return git__rethrow(error, "Failed to write index");
296
297 if ((error = write_index(index, &file)) < GIT_SUCCESS) {
298 git_filebuf_cleanup(&file);
299 return git__rethrow(error, "Failed to write index");
300 }
301
302 if ((error = git_filebuf_commit(&file)) < GIT_SUCCESS)
303 return git__rethrow(error, "Failed to write index");
304
305 if (gitfo_stat(index->index_file_path, &indexst) == 0) {
306 index->last_modified = indexst.st_mtime;
307 index->on_disk = 1;
308 }
309
310 return GIT_SUCCESS;
311 }
312
313 unsigned int git_index_entrycount(git_index *index)
314 {
315 assert(index);
316 return index->entries.length;
317 }
318
319 unsigned int git_index_entrycount_unmerged(git_index *index)
320 {
321 assert(index);
322 return index->unmerged.length;
323 }
324
325 git_index_entry *git_index_get(git_index *index, int n)
326 {
327 assert(index);
328 sort_index(index);
329 return git_vector_get(&index->entries, (unsigned int)n);
330 }
331
332 static void sort_index(git_index *index)
333 {
334 git_vector_sort(&index->entries);
335 }
336
337 static int index_insert(git_index *index, const git_index_entry *source_entry, int replace)
338 {
339 git_index_entry *entry;
340 size_t path_length;
341 int position;
342
343 assert(index && source_entry);
344
345 if (source_entry->path == NULL)
346 return git__throw(GIT_EMISSINGOBJDATA, "Failed to insert into index. Entry has no path");
347
348 entry = git__malloc(sizeof(git_index_entry));
349 if (entry == NULL)
350 return GIT_ENOMEM;
351
352 memcpy(entry, source_entry, sizeof(git_index_entry));
353
354 /* duplicate the path string so we own it */
355 entry->path = git__strdup(entry->path);
356 if (entry->path == NULL)
357 return GIT_ENOMEM;
358
359 /* make sure that the path length flag is correct */
360 path_length = strlen(entry->path);
361
362 entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
363
364 if (path_length < GIT_IDXENTRY_NAMEMASK)
365 entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
366 else
367 entry->flags |= GIT_IDXENTRY_NAMEMASK;;
368
369
370 /* look if an entry with this path already exists */
371 position = git_index_find(index, source_entry->path);
372
373 /* if no entry exists and replace is not set,
374 * add the entry at the end;
375 * the index is no longer sorted */
376 if (!replace || position == GIT_ENOTFOUND) {
377 if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
378 return GIT_ENOMEM;
379
380 /* if a previous entry exists and replace is set,
381 * replace it */
382 } else {
383 git_index_entry **entry_array = (git_index_entry **)index->entries.contents;
384
385 free((char *)entry_array[position]->path);
386 free(entry_array[position]);
387
388 entry_array[position] = entry;
389 }
390
391 return GIT_SUCCESS;
392 }
393
394 static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage)
395 {
396 char full_path[GIT_PATH_MAX];
397 struct stat st;
398 int error;
399
400 if (index->repository == NULL)
401 return git__throw(GIT_EBAREINDEX, "Failed to initialize entry. Repository is bare");
402
403 git__joinpath(full_path, index->repository->path_workdir, rel_path);
404
405 if (gitfo_exists(full_path) < 0)
406 return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. %s does not exist", full_path);
407
408 if (gitfo_stat(full_path, &st) < 0)
409 return git__throw(GIT_EOSERR, "Failed to initialize entry. %s appears to be corrupted", full_path);
410
411 if (stage < 0 || stage > 3)
412 return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage);
413
414 memset(entry, 0x0, sizeof(git_index_entry));
415
416 entry->ctime.seconds = (git_time_t)st.st_ctime;
417 entry->mtime.seconds = (git_time_t)st.st_mtime;
418 /* entry.mtime.nanoseconds = st.st_mtimensec; */
419 /* entry.ctime.nanoseconds = st.st_ctimensec; */
420 entry->dev= st.st_rdev;
421 entry->ino = st.st_ino;
422 entry->mode = st.st_mode;
423 entry->uid = st.st_uid;
424 entry->gid = st.st_gid;
425 entry->file_size = st.st_size;
426
427 /* write the blob to disk and get the oid */
428 if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS)
429 return git__rethrow(error, "Failed to initialize index entry");
430
431 entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
432 entry->path = (char *)rel_path; /* do not duplicate; index_insert already does this */
433 return GIT_SUCCESS;
434 }
435
436 int git_index_add(git_index *index, const char *path, int stage)
437 {
438 int error;
439 git_index_entry entry;
440
441 if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS)
442 return git__rethrow(error, "Failed to add to index");
443
444 return index_insert(index, &entry, 1);
445 }
446
447 int git_index_append(git_index *index, const char *path, int stage)
448 {
449 int error;
450 git_index_entry entry;
451
452 if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS)
453 return git__rethrow(error, "Failed to append to index");
454
455 return index_insert(index, &entry, 0);
456 }
457
458 int git_index_add2(git_index *index, const git_index_entry *source_entry)
459 {
460 return index_insert(index, source_entry, 1);
461 }
462
463 int git_index_append2(git_index *index, const git_index_entry *source_entry)
464 {
465 return index_insert(index, source_entry, 0);
466 }
467
468
469 int git_index_remove(git_index *index, int position)
470 {
471 assert(index);
472 sort_index(index);
473 return git_vector_remove(&index->entries, (unsigned int)position);
474 }
475
476 int git_index_find(git_index *index, const char *path)
477 {
478 sort_index(index);
479 return git_vector_bsearch2(&index->entries, index_srch, path);
480 }
481
482 const git_index_entry_unmerged *git_index_get_unmerged(git_index *index, const char *path)
483 {
484 int pos;
485 assert(index && path);
486
487 if (!index->unmerged.length)
488 return NULL;
489
490 if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < GIT_SUCCESS)
491 return NULL;
492
493 return git_vector_get(&index->unmerged, pos);
494 }
495
496
497 static int read_tree_internal(git_index_tree **out,
498 const char **buffer_in, const char *buffer_end, git_index_tree *parent)
499 {
500 git_index_tree *tree;
501 const char *name_start, *buffer;
502 long count;
503 int error = GIT_SUCCESS;
504
505 if ((tree = git__malloc(sizeof(git_index_tree))) == NULL)
506 return GIT_ENOMEM;
507
508 memset(tree, 0x0, sizeof(git_index_tree));
509 tree->parent = parent;
510
511 buffer = name_start = *buffer_in;
512
513 if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) {
514 error = GIT_EOBJCORRUPTED;
515 goto cleanup;
516 }
517
518 /* NUL-terminated tree name */
519 tree->name = git__strdup(name_start);
520 if (tree->name == NULL) {
521 error = GIT_ENOMEM;
522 goto cleanup;
523 }
524
525 if (++buffer >= buffer_end) {
526 error = GIT_EOBJCORRUPTED;
527 goto cleanup;
528 }
529
530 /* Blank-terminated ASCII decimal number of entries in this tree */
531 if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || count < -1) {
532 error = GIT_EOBJCORRUPTED;
533 goto cleanup;
534 }
535
536 /* Invalidated TREE. Free the tree but report success */
537 if (count == -1) {
538 /* FIXME: return buffer_end or the end position for
539 * this single tree entry */
540 *buffer_in = buffer_end;
541 *out = NULL;
542 free_tree(tree); /* Needs to be done manually */
543 return GIT_SUCCESS;
544 }
545
546 tree->entries = (size_t)count;
547
548 if (*buffer != ' ' || ++buffer >= buffer_end) {
549 error = GIT_EOBJCORRUPTED;
550 goto cleanup;
551 }
552
553 /* Number of children of the tree, newline-terminated */
554 if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
555 count < 0) {
556 error = GIT_EOBJCORRUPTED;
557 goto cleanup;
558 }
559
560 tree->children_count = (size_t)count;
561
562 if (*buffer != '\n' || ++buffer >= buffer_end) {
563 error = GIT_EOBJCORRUPTED;
564 goto cleanup;
565 }
566
567 /* 160-bit SHA-1 for this tree and it's children */
568 if (buffer + GIT_OID_RAWSZ > buffer_end) {
569 error = GIT_EOBJCORRUPTED;
570 goto cleanup;
571 }
572
573 git_oid_mkraw(&tree->oid, (const unsigned char *)buffer);
574 buffer += GIT_OID_RAWSZ;
575
576 /* Parse children: */
577 if (tree->children_count > 0) {
578 unsigned int i;
579 int err;
580
581 tree->children = git__malloc(tree->children_count * sizeof(git_index_tree *));
582 if (tree->children == NULL)
583 goto cleanup;
584
585 for (i = 0; i < tree->children_count; ++i) {
586 err = read_tree_internal(&tree->children[i], &buffer, buffer_end, tree);
587
588 if (err < GIT_SUCCESS)
589 goto cleanup;
590 }
591 }
592
593 *buffer_in = buffer;
594 *out = tree;
595 return GIT_SUCCESS;
596
597 cleanup:
598 free_tree(tree);
599 return error;
600 }
601
602 static int read_tree(git_index *index, const char *buffer, size_t buffer_size)
603 {
604 const char *buffer_end = buffer + buffer_size;
605 int error;
606
607 error = read_tree_internal(&index->tree, &buffer, buffer_end, NULL);
608
609 if (buffer < buffer_end)
610 return GIT_EOBJCORRUPTED;
611
612 return error;
613 }
614
615 static int read_unmerged(git_index *index, const char *buffer, size_t size)
616 {
617 const char *endptr;
618 size_t len;
619 int i;
620
621 git_vector_init(&index->unmerged, 16, unmerged_cmp);
622
623 while (size) {
624 git_index_entry_unmerged *lost;
625
626 len = strlen(buffer) + 1;
627 if (size <= len)
628 return git__throw(GIT_ERROR, "Failed to read unmerged entries");
629
630 if ((lost = git__malloc(sizeof(git_index_entry_unmerged))) == NULL)
631 return GIT_ENOMEM;
632
633 if (git_vector_insert(&index->unmerged, lost) < GIT_SUCCESS)
634 return git__throw(GIT_ERROR, "Failed to read unmerged entries");
635
636 lost->path = git__strdup(buffer);
637 if (!lost->path)
638 return GIT_ENOMEM;
639
640 size -= len;
641 buffer += len;
642
643 for (i = 0; i < 3; i++) {
644 if (git__strtol32((long int *) &lost->mode[i], buffer, &endptr, 8) < GIT_SUCCESS ||
645 !endptr || endptr == buffer || *endptr)
646 return GIT_ERROR;
647
648 len = (endptr + 1) - (char *) buffer;
649 if (size <= len)
650 return git__throw(GIT_ERROR, "Failed to read unmerged entries");
651
652 size -= len;
653 buffer += len;
654 }
655
656 for (i = 0; i < 3; i++) {
657 if (!lost->mode[i])
658 continue;
659 if (size < 20)
660 return git__throw(GIT_ERROR, "Failed to read unmerged entries");
661 git_oid_mkraw(&lost->oid[i], (unsigned char *) buffer);
662 size -= 20;
663 buffer += 20;
664 }
665 }
666
667 return GIT_SUCCESS;
668 }
669
670 static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
671 {
672 size_t path_length, entry_size;
673 uint16_t flags_raw;
674 const char *path_ptr;
675 const struct entry_short *source;
676
677 if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
678 return 0;
679
680 memset(dest, 0x0, sizeof(git_index_entry));
681
682 source = (const struct entry_short *)(buffer);
683
684 dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
685 dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
686 dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
687 dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
688 dest->dev = ntohl(source->dev);
689 dest->ino = ntohl(source->ino);
690 dest->mode = ntohl(source->mode);
691 dest->uid = ntohl(source->uid);
692 dest->gid = ntohl(source->gid);
693 dest->file_size = ntohl(source->file_size);
694 git_oid_cpy(&dest->oid, &source->oid);
695 dest->flags = ntohs(source->flags);
696
697 if (dest->flags & GIT_IDXENTRY_EXTENDED) {
698 struct entry_long *source_l = (struct entry_long *)source;
699 path_ptr = source_l->path;
700
701 flags_raw = ntohs(source_l->flags_extended);
702 memcpy(&dest->flags_extended, &flags_raw, 2);
703 } else
704 path_ptr = source->path;
705
706 path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;
707
708 /* if this is a very long string, we must find its
709 * real length without overflowing */
710 if (path_length == 0xFFF) {
711 const char *path_end;
712
713 path_end = memchr(path_ptr, '\0', buffer_size);
714 if (path_end == NULL)
715 return 0;
716
717 path_length = path_end - path_ptr;
718 }
719
720 if (dest->flags & GIT_IDXENTRY_EXTENDED)
721 entry_size = long_entry_size(path_length);
722 else
723 entry_size = short_entry_size(path_length);
724
725 if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
726 return 0;
727
728 dest->path = git__strdup(path_ptr);
729 assert(dest->path);
730
731 return entry_size;
732 }
733
734 static int read_header(struct index_header *dest, const void *buffer)
735 {
736 const struct index_header *source;
737 source = (const struct index_header *)(buffer);
738
739 dest->signature = ntohl(source->signature);
740 if (dest->signature != INDEX_HEADER_SIG)
741 return GIT_EOBJCORRUPTED;
742
743 dest->version = ntohl(source->version);
744 if (dest->version != INDEX_VERSION_NUMBER_EXT &&
745 dest->version != INDEX_VERSION_NUMBER)
746 return GIT_EOBJCORRUPTED;
747
748 dest->entry_count = ntohl(source->entry_count);
749 return GIT_SUCCESS;
750 }
751
752 static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
753 {
754 const struct index_extension *source;
755 struct index_extension dest;
756 size_t total_size;
757
758 source = (const struct index_extension *)(buffer);
759
760 memcpy(dest.signature, source->signature, 4);
761 dest.extension_size = ntohl(source->extension_size);
762
763 total_size = dest.extension_size + sizeof(struct index_extension);
764
765 if (buffer_size - total_size < INDEX_FOOTER_SIZE)
766 return 0;
767
768 /* optional extension */
769 if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
770 /* tree cache */
771 if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
772 if (read_tree(index, buffer + 8, dest.extension_size) < GIT_SUCCESS)
773 return 0;
774 } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
775 if (read_unmerged(index, buffer + 8, dest.extension_size) < GIT_SUCCESS)
776 return 0;
777 }
778 /* else, unsupported extension. We cannot parse this, but we can skip
779 * it by returning `total_size */
780 } else {
781 /* we cannot handle non-ignorable extensions;
782 * in fact they aren't even defined in the standard */
783 return 0;
784 }
785
786 return total_size;
787 }
788
789 static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
790 {
791 unsigned int i;
792 struct index_header header;
793 git_oid checksum_calculated, checksum_expected;
794
795 #define seek_forward(_increase) { \
796 if (_increase >= buffer_size) \
797 return git__throw(GIT_EOBJCORRUPTED, "Failed to seek forward. Buffer size exceeded"); \
798 buffer += _increase; \
799 buffer_size -= _increase;\
800 }
801
802 if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
803 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer too small");
804
805 /* Precalculate the SHA1 of the files's contents -- we'll match it to
806 * the provided SHA1 in the footer */
807 git_hash_buf(&checksum_calculated, (const void *)buffer, buffer_size - INDEX_FOOTER_SIZE);
808
809 /* Parse header */
810 if (read_header(&header, buffer) < GIT_SUCCESS)
811 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header is corrupted");
812
813 seek_forward(INDEX_HEADER_SIZE);
814
815 git_vector_clear(&index->entries);
816
817 /* Parse all the entries */
818 for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
819 size_t entry_size;
820 git_index_entry *entry;
821
822 entry = git__malloc(sizeof(git_index_entry));
823 if (entry == NULL)
824 return GIT_ENOMEM;
825
826 entry_size = read_entry(entry, buffer, buffer_size);
827
828 /* 0 bytes read means an object corruption */
829 if (entry_size == 0)
830 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Entry size is zero");
831
832 if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
833 return GIT_ENOMEM;
834
835 seek_forward(entry_size);
836 }
837
838 if (i != header.entry_count)
839 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header entries changed while parsing");
840
841 /* There's still space for some extensions! */
842 while (buffer_size > INDEX_FOOTER_SIZE) {
843 size_t extension_size;
844
845 extension_size = read_extension(index, buffer, buffer_size);
846
847 /* see if we have read any bytes from the extension */
848 if (extension_size == 0)
849 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Extension size is zero");
850
851 seek_forward(extension_size);
852 }
853
854 if (buffer_size != INDEX_FOOTER_SIZE)
855 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer size does not match index footer size");
856
857 /* 160-bit SHA-1 over the content of the index file before this checksum. */
858 git_oid_mkraw(&checksum_expected, (const unsigned char *)buffer);
859
860 if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
861 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Calculated checksum does not match expected checksum");
862
863 #undef seek_forward
864
865 /* force sorting in the vector: the entries are
866 * assured to be sorted on the index */
867 index->entries.sorted = 1;
868 return GIT_SUCCESS;
869 }
870
871 static int is_index_extended(git_index *index)
872 {
873 unsigned int i, extended;
874
875 extended = 0;
876
877 for (i = 0; i < index->entries.length; ++i) {
878 git_index_entry *entry;
879 entry = git_vector_get(&index->entries, i);
880 entry->flags &= ~GIT_IDXENTRY_EXTENDED;
881 if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
882 extended++;
883 entry->flags |= GIT_IDXENTRY_EXTENDED;
884 }
885 }
886 return extended;
887 }
888
889 static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
890 {
891 struct entry_short *ondisk;
892 size_t path_len, disk_size;
893 char *path;
894
895 path_len = strlen(entry->path);
896
897 if (entry->flags & GIT_IDXENTRY_EXTENDED)
898 disk_size = long_entry_size(path_len);
899 else
900 disk_size = short_entry_size(path_len);
901
902 if (git_filebuf_reserve(file, (void **)&ondisk, disk_size) < GIT_SUCCESS)
903 return GIT_ENOMEM;
904
905 memset(ondisk, 0x0, disk_size);
906
907 ondisk->ctime.seconds = htonl((unsigned long)entry->ctime.seconds);
908 ondisk->mtime.seconds = htonl((unsigned long)entry->mtime.seconds);
909 ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
910 ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
911 ondisk->dev = htonl(entry->dev);
912 ondisk->ino = htonl(entry->ino);
913 ondisk->mode = htonl(entry->mode);
914 ondisk->uid = htonl(entry->uid);
915 ondisk->gid = htonl(entry->gid);
916 ondisk->file_size = htonl((unsigned long)entry->file_size);
917
918 git_oid_cpy(&ondisk->oid, &entry->oid);
919
920 ondisk->flags = htons(entry->flags);
921
922 if (entry->flags & GIT_IDXENTRY_EXTENDED) {
923 struct entry_long *ondisk_ext;
924 ondisk_ext = (struct entry_long *)ondisk;
925 ondisk_ext->flags_extended = htons(entry->flags_extended);
926 path = ondisk_ext->path;
927 }
928 else
929 path = ondisk->path;
930
931 memcpy(path, entry->path, path_len);
932
933 return GIT_SUCCESS;
934 }
935
936 static int write_entries(git_index *index, git_filebuf *file)
937 {
938 unsigned int i;
939
940 for (i = 0; i < index->entries.length; ++i) {
941 git_index_entry *entry;
942 entry = git_vector_get(&index->entries, i);
943 if (write_disk_entry(file, entry) < GIT_SUCCESS)
944 return GIT_ENOMEM;
945 }
946
947 return GIT_SUCCESS;
948 }
949
950 static int write_index(git_index *index, git_filebuf *file)
951 {
952 int error = GIT_SUCCESS;
953 git_oid hash_final;
954
955 struct index_header header;
956
957 int is_extended;
958
959 assert(index && file);
960
961 is_extended = is_index_extended(index);
962
963 header.signature = htonl(INDEX_HEADER_SIG);
964 header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
965 header.entry_count = htonl(index->entries.length);
966
967 git_filebuf_write(file, &header, sizeof(struct index_header));
968
969 error = write_entries(index, file);
970 if (error < GIT_SUCCESS)
971 return git__rethrow(error, "Failed to write index");
972
973 /* TODO: write extensions (tree cache) */
974
975 /* get out the hash for all the contents we've appended to the file */
976 git_filebuf_hash(&hash_final, file);
977
978 /* write it at the end of the file */
979 git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
980
981 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write index");
982 }