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