]> git.proxmox.com Git - libgit2.git/blame - src/index.c
index: Add API for unmerged entries
[libgit2.git] / src / index.c
CommitLineData
68535125
VM
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"
c3a20d5c 29#include "repository.h"
68535125
VM
30#include "index.h"
31#include "hash.h"
44908fe7
VM
32#include "git2/odb.h"
33#include "git2/blob.h"
68535125 34
68535125
VM
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
68535125
VM
41static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
42static const size_t INDEX_HEADER_SIZE = 12;
43
44static const unsigned int INDEX_VERSION_NUMBER = 2;
348c7335
VM
45static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
46
47static const unsigned int INDEX_HEADER_SIG = 0x44495243;
48static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
4c0b6a6d 49static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
68535125
VM
50
51struct index_header {
52 uint32_t signature;
53 uint32_t version;
54 uint32_t entry_count;
55};
56
57struct index_extension {
58 char signature[4];
59 uint32_t extension_size;
60};
61
a44fc1d4
VM
62struct entry_time {
63 uint32_t seconds;
64 uint32_t nanoseconds;
65};
66
68535125 67struct entry_short {
a44fc1d4
VM
68 struct entry_time ctime;
69 struct entry_time mtime;
68535125
VM
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;
683581a3 78 char path[1]; /* arbitrary length */
68535125
VM
79};
80
81struct entry_long {
a44fc1d4
VM
82 struct entry_time ctime;
83 struct entry_time mtime;
68535125
VM
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;
683581a3 93 char path[1]; /* arbitrary length */
68535125
VM
94};
95
96/* local declarations */
97static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
98static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
99static int read_header(struct index_header *dest, const void *buffer);
100
101static int read_tree(git_index *index, const char *buffer, size_t buffer_size);
4c0b6a6d 102static int read_unmerged_internal(git_index *, const char **, size_t buffer_size);
68535125 103static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *);
4c0b6a6d 104static int read_unmerged_internal(git_index *, const char **, size_t buffer_size);
68535125 105
348c7335
VM
106static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
107static void sort_index(git_index *index);
817c2820 108static int write_index(git_index *index, git_filebuf *file);
68535125 109
c4034e63
VM
110int index_srch(const void *key, const void *array_member)
111{
112 const char *filename = (const char *)key;
113 const git_index_entry *entry = *(const git_index_entry **)(array_member);
114
115 return strcmp(filename, entry->path);
116}
117
118int index_cmp(const void *a, const void *b)
119{
120 const git_index_entry *entry_a = *(const git_index_entry **)(a);
121 const git_index_entry *entry_b = *(const git_index_entry **)(b);
122
123 return strcmp(entry_a->path, entry_b->path);
124}
125
4c0b6a6d
JP
126int unmerged_srch(const void *key, const void *array_member)
127{
128 const char *path = (const char *) key;
129 const git_index_entry_unmerged *entry = *(const git_index_entry_unmerged **) (array_member);
130
131 return strcmp(path, entry->path);
132}
133
134int unmerged_cmp(const void *a, const void *b)
135{
136 const git_index_entry_unmerged *info_a = *(const git_index_entry_unmerged **)(a);
137 const git_index_entry_unmerged *info_b = *(const git_index_entry_unmerged **)(b);
138
139 return strcmp(info_a->path, info_b->path);
140}
c4034e63 141
c3a20d5c 142static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path)
68535125
VM
143{
144 git_index *index;
145
1795f879 146 assert(index_out && index_path);
68535125
VM
147
148 index = git__malloc(sizeof(git_index));
149 if (index == NULL)
1795f879 150 return GIT_ENOMEM;
68535125
VM
151
152 memset(index, 0x0, sizeof(git_index));
153
154 index->index_file_path = git__strdup(index_path);
155 if (index->index_file_path == NULL) {
156 free(index);
1795f879 157 return GIT_ENOMEM;
68535125
VM
158 }
159
c3a20d5c 160 index->repository = owner;
6fd195d7 161
86d7e1ca 162 git_vector_init(&index->entries, 32, index_cmp);
4c0b6a6d 163 git_vector_init(&index->unmerged, 32, unmerged_cmp);
348c7335 164
68535125
VM
165 /* Check if index file is stored on disk already */
166 if (gitfo_exists(index->index_file_path) == 0)
167 index->on_disk = 1;
168
1795f879 169 *index_out = index;
3bdc0d4c 170 return git_index_read(index);
68535125
VM
171}
172
c3a20d5c
VM
173int git_index_open_bare(git_index **index_out, const char *index_path)
174{
175 return index_initialize(index_out, NULL, index_path);
176}
177
178int git_index_open_inrepo(git_index **index_out, git_repository *repo)
179{
c3dd69a9
VM
180 if (repo->is_bare)
181 return GIT_EBAREINDEX;
182
c3a20d5c
VM
183 return index_initialize(index_out, repo, repo->path_index);
184}
185
348c7335
VM
186void git_index_free(git_index *index)
187{
971c90be 188 if (index == NULL || index->repository != NULL)
348c7335
VM
189 return;
190
191 git_index_clear(index);
192 git_vector_free(&index->entries);
193
194 free(index->index_file_path);
195 free(index);
196}
197
198static void free_tree(git_index_tree *tree)
199{
200 unsigned int i;
201
202 if (tree == NULL)
203 return;
204
205 for (i = 0; i < tree->children_count; ++i)
206 free_tree(tree->children[i]);
207
208 free(tree->name);
209 free(tree->children);
210 free(tree);
211}
212
68535125
VM
213void git_index_clear(git_index *index)
214{
215 unsigned int i;
6fd195d7
VM
216
217 assert(index);
218
c4034e63
VM
219 for (i = 0; i < index->entries.length; ++i) {
220 git_index_entry *e;
221 e = git_vector_get(&index->entries, i);
222 free(e->path);
223 free(e);
224 }
68535125 225
c4034e63 226 git_vector_clear(&index->entries);
68535125 227 index->last_modified = 0;
68535125 228
348c7335 229 free_tree(index->tree);
68535125
VM
230 index->tree = NULL;
231}
232
68535125
VM
233int git_index_read(git_index *index)
234{
235 struct stat indexst;
6f02c3ba 236 int error = GIT_SUCCESS;
68535125 237
1795f879 238 assert(index->index_file_path);
68535125
VM
239
240 if (!index->on_disk || gitfo_exists(index->index_file_path) < 0) {
241 git_index_clear(index);
242 index->on_disk = 0;
6f02c3ba 243 return GIT_SUCCESS;
68535125
VM
244 }
245
246 if (gitfo_stat(index->index_file_path, &indexst) < 0)
247 return GIT_EOSERR;
248
c3dd69a9
VM
249 if (!S_ISREG(indexst.st_mode))
250 return GIT_ENOTFOUND;
251
68535125
VM
252 if (indexst.st_mtime != index->last_modified) {
253
254 gitfo_buf buffer;
255
6f02c3ba 256 if (gitfo_read_file(&buffer, index->index_file_path) < GIT_SUCCESS)
c3dd69a9 257 return GIT_EOSERR;
68535125
VM
258
259 git_index_clear(index);
348c7335 260 error = parse_index(index, buffer.data, buffer.len);
68535125 261
6f02c3ba 262 if (error == GIT_SUCCESS)
68535125
VM
263 index->last_modified = indexst.st_mtime;
264
265 gitfo_free_buf(&buffer);
266 }
267
268 return error;
269}
270
271int git_index_write(git_index *index)
272{
817c2820 273 git_filebuf file;
68535125 274 struct stat indexst;
348c7335 275 int error;
68535125 276
86d7e1ca 277 sort_index(index);
68535125 278
817c2820 279 if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS)
348c7335 280 return error;
68535125 281
348c7335 282 if ((error = write_index(index, &file)) < GIT_SUCCESS) {
817c2820 283 git_filebuf_cleanup(&file);
348c7335 284 return error;
68535125
VM
285 }
286
817c2820 287 if ((error = git_filebuf_commit(&file)) < GIT_SUCCESS)
348c7335 288 return error;
68535125
VM
289
290 if (gitfo_stat(index->index_file_path, &indexst) == 0) {
291 index->last_modified = indexst.st_mtime;
292 index->on_disk = 1;
293 }
294
6f02c3ba 295 return GIT_SUCCESS;
68535125
VM
296}
297
0be42199
SC
298unsigned int git_index_entrycount(git_index *index)
299{
300 assert(index);
c4034e63 301 return index->entries.length;
0be42199
SC
302}
303
4c0b6a6d
JP
304unsigned int git_index_unmerged_entrycount(git_index *index)
305{
306 assert(index);
307
308 if (!&index->unmerged)
309 return 0;
310
311 return index->unmerged.length;
312}
313
6fd195d7
VM
314git_index_entry *git_index_get(git_index *index, int n)
315{
3f43678e 316 assert(index);
348c7335 317 sort_index(index);
c4034e63 318 return git_vector_get(&index->entries, (unsigned int)n);
6fd195d7
VM
319}
320
c3a20d5c 321int git_index_add(git_index *index, const char *rel_path, int stage)
68535125
VM
322{
323 git_index_entry entry;
c3a20d5c
VM
324 char full_path[GIT_PATH_MAX];
325 struct stat st;
326 int error;
68535125 327
c3a20d5c
VM
328 if (index->repository == NULL)
329 return GIT_EBAREINDEX;
68535125 330
995f9c34 331 git__joinpath(full_path, index->repository->path_workdir, rel_path);
68535125 332
c3a20d5c
VM
333 if (gitfo_exists(full_path) < 0)
334 return GIT_ENOTFOUND;
335
336 if (gitfo_stat(full_path, &st) < 0)
337 return GIT_EOSERR;
68535125
VM
338
339 if (stage < 0 || stage > 3)
340 return GIT_ERROR;
341
c3a20d5c 342 memset(&entry, 0x0, sizeof(git_index_entry));
68535125 343
56d8ca26 344 entry.ctime.seconds = (git_time_t)st.st_ctime;
345 entry.mtime.seconds = (git_time_t)st.st_mtime;
c3a20d5c
VM
346 /* entry.mtime.nanoseconds = st.st_mtimensec; */
347 /* entry.ctime.nanoseconds = st.st_ctimensec; */
348 entry.dev= st.st_rdev;
349 entry.ino = st.st_ino;
350 entry.mode = st.st_mode;
351 entry.uid = st.st_uid;
352 entry.gid = st.st_gid;
f0bde7fa 353 entry.file_size = st.st_size;
c3a20d5c
VM
354
355 /* write the blob to disk and get the oid */
72a3fe42 356 if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS)
c3a20d5c 357 return error;
68535125 358
c3a20d5c
VM
359 entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
360 entry.path = (char *)rel_path; /* do not duplicate; index_insert already does this */
361
362 return git_index_insert(index, &entry);
68535125
VM
363}
364
348c7335 365void sort_index(git_index *index)
68535125 366{
86d7e1ca 367 git_vector_sort(&index->entries);
68535125
VM
368}
369
c3a20d5c 370int git_index_insert(git_index *index, const git_index_entry *source_entry)
68535125 371{
c4034e63 372 git_index_entry *entry;
c3a20d5c
VM
373 size_t path_length;
374 int position;
68535125 375
c3a20d5c 376 assert(index && source_entry);
68535125 377
c3a20d5c
VM
378 if (source_entry->path == NULL)
379 return GIT_EMISSINGOBJDATA;
68535125 380
c4034e63
VM
381 entry = git__malloc(sizeof(git_index_entry));
382 if (entry == NULL)
383 return GIT_ENOMEM;
68535125 384
c4034e63 385 memcpy(entry, source_entry, sizeof(git_index_entry));
c3a20d5c
VM
386
387 /* duplicate the path string so we own it */
c4034e63
VM
388 entry->path = git__strdup(entry->path);
389 if (entry->path == NULL)
c3a20d5c
VM
390 return GIT_ENOMEM;
391
392 /* make sure that the path length flag is correct */
c4034e63 393 path_length = strlen(entry->path);
c3a20d5c 394
c4034e63 395 entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
c3a20d5c
VM
396
397 if (path_length < GIT_IDXENTRY_NAMEMASK)
c4034e63 398 entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
c3a20d5c 399 else
c4034e63 400 entry->flags |= GIT_IDXENTRY_NAMEMASK;;
c3a20d5c 401
68535125 402
c4034e63
VM
403 /* look if an entry with this path already exists */
404 position = git_index_find(index, source_entry->path);
68535125 405
c4034e63
VM
406 /* if no entry exists, add the entry at the end;
407 * the index is no longer sorted */
408 if (position == GIT_ENOTFOUND) {
6f02c3ba 409 if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
c4034e63 410 return GIT_ENOMEM;
68535125 411
c4034e63
VM
412 /* if a previous entry exists, replace it */
413 } else {
414 git_index_entry **entry_array = (git_index_entry **)index->entries.contents;
3f43678e 415
c4034e63
VM
416 free(entry_array[position]->path);
417 free(entry_array[position]);
68535125 418
c4034e63
VM
419 entry_array[position] = entry;
420 }
68535125 421
3f43678e 422 return GIT_SUCCESS;
68535125
VM
423}
424
c4034e63 425int git_index_remove(git_index *index, int position)
68535125 426{
c4034e63 427 assert(index);
348c7335 428 sort_index(index);
c4034e63
VM
429 return git_vector_remove(&index->entries, (unsigned int)position);
430}
68535125 431
c4034e63
VM
432int git_index_find(git_index *index, const char *path)
433{
348c7335 434 sort_index(index);
86d7e1ca 435 return git_vector_bsearch2(&index->entries, index_srch, path);
68535125
VM
436}
437
4c0b6a6d
JP
438int git_index_get_unmerged(git_index_entry_unmerged **entry, git_index *index, const char *path)
439{
440 int pos;
441 assert(index);
442
443 if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < GIT_SUCCESS)
444 return pos;
445
446 if ((*entry = git_vector_get(&index->unmerged, pos)) == NULL) {
447 return GIT_ENOTFOUND;
448 }
449
450 return GIT_SUCCESS;
451}
452
453
68535125
VM
454static git_index_tree *read_tree_internal(
455 const char **buffer_in, const char *buffer_end, git_index_tree *parent)
456{
457 git_index_tree *tree;
458 const char *name_start, *buffer;
c6e65aca 459 long count;
68535125
VM
460
461 if ((tree = git__malloc(sizeof(git_index_tree))) == NULL)
462 return NULL;
463
464 memset(tree, 0x0, sizeof(git_index_tree));
465 tree->parent = parent;
466
467 buffer = name_start = *buffer_in;
468
469 if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
470 goto error_cleanup;
471
472 /* NUL-terminated tree name */
473 tree->name = git__strdup(name_start);
474 if (++buffer >= buffer_end)
475 goto error_cleanup;
476
477 /* Blank-terminated ASCII decimal number of entries in this tree */
c6e65aca
VM
478 if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
479 count < 0)
480 goto error_cleanup;
481
482 tree->entries = (size_t)count;
483
68535125
VM
484 if (*buffer != ' ' || ++buffer >= buffer_end)
485 goto error_cleanup;
486
487 /* Number of children of the tree, newline-terminated */
c6e65aca
VM
488 if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
489 count < 0)
490 goto error_cleanup;
491
492 tree->children_count = (size_t)count;
493
68535125
VM
494 if (*buffer != '\n' || ++buffer >= buffer_end)
495 goto error_cleanup;
496
497 /* 160-bit SHA-1 for this tree and it's children */
498 if (buffer + GIT_OID_RAWSZ > buffer_end)
499 goto error_cleanup;
500
501 git_oid_mkraw(&tree->oid, (const unsigned char *)buffer);
502 buffer += GIT_OID_RAWSZ;
503
504 /* Parse children: */
505 if (tree->children_count > 0) {
506 unsigned int i;
507
508 tree->children = git__malloc(tree->children_count * sizeof(git_index_tree *));
509 if (tree->children == NULL)
510 goto error_cleanup;
511
512 for (i = 0; i < tree->children_count; ++i) {
513 tree->children[i] = read_tree_internal(&buffer, buffer_end, tree);
514
515 if (tree->children[i] == NULL)
516 goto error_cleanup;
517 }
518 }
519
520 *buffer_in = buffer;
521 return tree;
522
523error_cleanup:
348c7335 524 free_tree(tree);
68535125
VM
525 return NULL;
526}
527
528static int read_tree(git_index *index, const char *buffer, size_t buffer_size)
529{
530 const char *buffer_end = buffer + buffer_size;
531
532 index->tree = read_tree_internal(&buffer, buffer_end, NULL);
6f02c3ba 533 return (index->tree != NULL && buffer == buffer_end) ? GIT_SUCCESS : GIT_EOBJCORRUPTED;
68535125
VM
534}
535
4c0b6a6d
JP
536static int read_unmerged_internal(
537 git_index *index, const char **buffer_in, size_t buffer_size)
538{
539 const char *buffer, *endptr;
540 size_t size, len;
541 int i;
542
543 size = buffer_size;
544
545 while (size) {
546 git_index_entry_unmerged *lost;
547
548 buffer = *buffer_in;
549
550 len = strlen(buffer) + 1;
551 if (size <= len)
552 return GIT_ERROR;
553
554 if ((lost = git__malloc(sizeof(git_index_entry_unmerged))) == NULL)
555 return GIT_ERROR;
556
557 if ((lost->path = git__malloc(strlen(buffer))) == NULL)
558 return GIT_ERROR;
559 strcpy(lost->path, buffer);
560
561 if (git_vector_insert(&index->unmerged, lost) < GIT_SUCCESS)
562 return GIT_ERROR;
563
564 size -= len;
565 buffer += len;
566
567 for (i = 0; i < 3; i++) {
568 if (git__strtol32((long int *) &lost->mode[i], buffer, &endptr, 8) < GIT_SUCCESS || !endptr || endptr == buffer || *endptr)
569 return GIT_ERROR;
570 len = (endptr + 1) - (char *) buffer;
571 if (size <= len)
572 return GIT_ERROR;
573 size -= len;
574 buffer += len;
575 }
576
577 for (i = 0; i < 3; i++) {
578 if (!lost->mode[i])
579 continue;
580 if (size < 20)
581 return GIT_ERROR;
582 git_oid_mkraw(&lost->oid[i], (unsigned char *) buffer);
583 size -= 20;
584 buffer += 20;
585 }
586 }
587
588 *buffer_in = buffer;
589 return GIT_SUCCESS;
590}
591
592static int read_unmerged(git_index *index, const char *buffer, size_t buffer_size)
593{
594 read_unmerged_internal(index, &buffer, buffer_size);
595 return (&index->unmerged != NULL) ? GIT_SUCCESS : GIT_EOBJCORRUPTED;
596}
597
68535125
VM
598static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
599{
600 size_t path_length, entry_size;
601 uint16_t flags_raw;
602 const char *path_ptr;
603 const struct entry_short *source;
604
605 if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
606 return 0;
607
91e88941
VM
608 memset(dest, 0x0, sizeof(git_index_entry));
609
68535125
VM
610 source = (const struct entry_short *)(buffer);
611
56d8ca26 612 dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
613 dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
614 dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
615 dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
68535125
VM
616 dest->dev = ntohl(source->dev);
617 dest->ino = ntohl(source->ino);
618 dest->mode = ntohl(source->mode);
619 dest->uid = ntohl(source->uid);
620 dest->gid = ntohl(source->gid);
621 dest->file_size = ntohl(source->file_size);
622 git_oid_cpy(&dest->oid, &source->oid);
623 dest->flags = ntohs(source->flags);
624
625 if (dest->flags & GIT_IDXENTRY_EXTENDED) {
626 struct entry_long *source_l = (struct entry_long *)source;
627 path_ptr = source_l->path;
628
629 flags_raw = ntohs(source_l->flags_extended);
630 memcpy(&dest->flags_extended, &flags_raw, 2);
631 } else
632 path_ptr = source->path;
633
634 path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;
635
636 /* if this is a very long string, we must find its
637 * real length without overflowing */
638 if (path_length == 0xFFF) {
639 const char *path_end;
640
641 path_end = memchr(path_ptr, '\0', buffer_size);
642 if (path_end == NULL)
643 return 0;
644
645 path_length = path_end - path_ptr;
646 }
647
648 if (dest->flags & GIT_IDXENTRY_EXTENDED)
649 entry_size = long_entry_size(path_length);
650 else
651 entry_size = short_entry_size(path_length);
652
653 if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
654 return 0;
655
656 dest->path = git__strdup(path_ptr);
c3a20d5c 657 assert(dest->path);
68535125
VM
658
659 return entry_size;
660}
661
662static int read_header(struct index_header *dest, const void *buffer)
663{
664 const struct index_header *source;
665 source = (const struct index_header *)(buffer);
666
348c7335
VM
667 dest->signature = ntohl(source->signature);
668 if (dest->signature != INDEX_HEADER_SIG)
68535125
VM
669 return GIT_EOBJCORRUPTED;
670
671 dest->version = ntohl(source->version);
348c7335
VM
672 if (dest->version != INDEX_VERSION_NUMBER_EXT &&
673 dest->version != INDEX_VERSION_NUMBER)
68535125
VM
674 return GIT_EOBJCORRUPTED;
675
676 dest->entry_count = ntohl(source->entry_count);
6f02c3ba 677 return GIT_SUCCESS;
68535125
VM
678}
679
680static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
681{
682 const struct index_extension *source;
683 struct index_extension dest;
684 size_t total_size;
685
686 source = (const struct index_extension *)(buffer);
687
688 memcpy(dest.signature, source->signature, 4);
689 dest.extension_size = ntohl(source->extension_size);
690
691 total_size = dest.extension_size + sizeof(struct index_extension);
692
693 if (buffer_size - total_size < INDEX_FOOTER_SIZE)
694 return 0;
695
696 /* optional extension */
697 if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
698 /* tree cache */
699 if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
700
6f02c3ba 701 if (read_tree(index, buffer + 8, dest.extension_size) < GIT_SUCCESS)
68535125 702 return 0;
4c0b6a6d
JP
703
704 } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
705
706 if (read_unmerged(index, buffer + 8, dest.extension_size) < GIT_SUCCESS)
707 return 0;
708 } else {
709 ;
68535125
VM
710 }
711 } else {
712 /* we cannot handle non-ignorable extensions;
713 * in fact they aren't even defined in the standard */
714 return 0;
715 }
716
717 return total_size;
718}
719
348c7335 720static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
68535125
VM
721{
722 unsigned int i;
723 struct index_header header;
724 git_oid checksum_calculated, checksum_expected;
725
726#define seek_forward(_increase) { \
727 if (_increase >= buffer_size) \
728 return GIT_EOBJCORRUPTED; \
729 buffer += _increase; \
730 buffer_size -= _increase;\
731}
732
733 if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
734 return GIT_EOBJCORRUPTED;
735
736 /* Precalculate the SHA1 of the files's contents -- we'll match it to
737 * the provided SHA1 in the footer */
738 git_hash_buf(&checksum_calculated, (const void *)buffer, buffer_size - INDEX_FOOTER_SIZE);
739
740 /* Parse header */
6f02c3ba 741 if (read_header(&header, buffer) < GIT_SUCCESS)
68535125
VM
742 return GIT_EOBJCORRUPTED;
743
744 seek_forward(INDEX_HEADER_SIZE);
745
c4034e63 746 git_vector_clear(&index->entries);
68535125
VM
747
748 /* Parse all the entries */
c4034e63 749 for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
68535125 750 size_t entry_size;
c4034e63
VM
751 git_index_entry *entry;
752
753 entry = git__malloc(sizeof(git_index_entry));
754 if (entry == NULL)
755 return GIT_ENOMEM;
756
757 entry_size = read_entry(entry, buffer, buffer_size);
68535125
VM
758
759 /* 0 bytes read means an object corruption */
760 if (entry_size == 0)
761 return GIT_EOBJCORRUPTED;
762
6f02c3ba 763 if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
c4034e63
VM
764 return GIT_ENOMEM;
765
68535125
VM
766 seek_forward(entry_size);
767 }
768
c4034e63 769 if (i != header.entry_count)
c3a20d5c
VM
770 return GIT_EOBJCORRUPTED;
771
68535125
VM
772 /* There's still space for some extensions! */
773 while (buffer_size > INDEX_FOOTER_SIZE) {
774 size_t extension_size;
775
776 extension_size = read_extension(index, buffer, buffer_size);
777
778 /* see if we have read any bytes from the extension */
779 if (extension_size == 0)
780 return GIT_EOBJCORRUPTED;
781
782 seek_forward(extension_size);
783 }
784
785 if (buffer_size != INDEX_FOOTER_SIZE)
786 return GIT_EOBJCORRUPTED;
787
788 /* 160-bit SHA-1 over the content of the index file before this checksum. */
789 git_oid_mkraw(&checksum_expected, (const unsigned char *)buffer);
790
791 if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
792 return GIT_EOBJCORRUPTED;
793
794#undef seek_forward
795
86d7e1ca
VM
796 /* force sorting in the vector: the entries are
797 * assured to be sorted on the index */
798 index->entries.sorted = 1;
6f02c3ba 799 return GIT_SUCCESS;
68535125
VM
800}
801
817c2820 802static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
68535125 803{
348c7335 804 struct entry_short *ondisk;
817c2820 805 size_t path_len, disk_size;
348c7335 806 char *path;
68535125 807
348c7335 808 path_len = strlen(entry->path);
68535125 809
348c7335 810 if (entry->flags & GIT_IDXENTRY_EXTENDED)
817c2820 811 disk_size = long_entry_size(path_len);
348c7335 812 else
817c2820 813 disk_size = short_entry_size(path_len);
68535125 814
817c2820
VM
815 if (git_filebuf_reserve(file, (void **)&ondisk, disk_size) < GIT_SUCCESS)
816 return GIT_ENOMEM;
817
818 memset(ondisk, 0x0, disk_size);
68535125 819
084c1935
VM
820 ondisk->ctime.seconds = htonl((unsigned long)entry->ctime.seconds);
821 ondisk->mtime.seconds = htonl((unsigned long)entry->mtime.seconds);
348c7335
VM
822 ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
823 ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
824 ondisk->dev = htonl(entry->dev);
825 ondisk->ino = htonl(entry->ino);
826 ondisk->mode = htonl(entry->mode);
827 ondisk->uid = htonl(entry->uid);
828 ondisk->gid = htonl(entry->gid);
084c1935 829 ondisk->file_size = htonl((unsigned long)entry->file_size);
348c7335
VM
830
831 git_oid_cpy(&ondisk->oid, &entry->oid);
832
833 ondisk->flags = htons(entry->flags);
834
835 if (entry->flags & GIT_IDXENTRY_EXTENDED) {
836 struct entry_long *ondisk_ext;
837 ondisk_ext = (struct entry_long *)ondisk;
838 ondisk_ext->flags_extended = htons(entry->flags_extended);
839 path = ondisk_ext->path;
840 }
841 else
842 path = ondisk->path;
843
844 memcpy(path, entry->path, path_len);
68535125 845
e822508a 846 return GIT_SUCCESS;
68535125
VM
847}
848
817c2820 849static int write_entries(git_index *index, git_filebuf *file)
348c7335
VM
850{
851 unsigned int i;
68535125 852
c4034e63 853 for (i = 0; i < index->entries.length; ++i) {
68535125 854 git_index_entry *entry;
c4034e63 855 entry = git_vector_get(&index->entries, i);
817c2820 856 if (write_disk_entry(file, entry) < GIT_SUCCESS)
348c7335 857 return GIT_ENOMEM;
68535125
VM
858 }
859
348c7335
VM
860 return GIT_SUCCESS;
861}
862
817c2820 863static int write_index(git_index *index, git_filebuf *file)
348c7335
VM
864{
865 int error = GIT_SUCCESS;
348c7335
VM
866 git_oid hash_final;
867
868 struct index_header header;
869
870 int is_extended = 1;
871
817c2820 872 assert(index && file);
348c7335
VM
873
874 header.signature = htonl(INDEX_HEADER_SIG);
875 header.version = htonl(is_extended ? INDEX_VERSION_NUMBER : INDEX_VERSION_NUMBER_EXT);
876 header.entry_count = htonl(index->entries.length);
877
817c2820 878 git_filebuf_write(file, &header, sizeof(struct index_header));
348c7335 879
817c2820 880 error = write_entries(index, file);
348c7335 881 if (error < GIT_SUCCESS)
817c2820 882 return error;
68535125
VM
883
884 /* TODO: write extensions (tree cache) */
885
817c2820
VM
886 /* get out the hash for all the contents we've appended to the file */
887 git_filebuf_hash(&hash_final, file);
888
889 /* write it at the end of the file */
890 git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
68535125
VM
891
892 return error;
893}