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