File Coverage

deps/libgit2/src/indexer.c
Criterion Covered Total %
statement 373 663 56.2
branch 167 378 44.1
condition n/a
subroutine n/a
pod n/a
total 540 1041 51.8


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
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 "indexer.h"
9              
10             #include "git2/indexer.h"
11             #include "git2/object.h"
12              
13             #include "commit.h"
14             #include "tree.h"
15             #include "tag.h"
16             #include "pack.h"
17             #include "mwindow.h"
18             #include "posix.h"
19             #include "pack.h"
20             #include "filebuf.h"
21             #include "oid.h"
22             #include "oidarray.h"
23             #include "oidmap.h"
24             #include "zstream.h"
25             #include "object.h"
26              
27             extern git_mutex git__mwindow_mutex;
28              
29             size_t git_indexer__max_objects = UINT32_MAX;
30              
31             #define UINT31_MAX (0x7FFFFFFF)
32              
33             struct entry {
34             git_oid oid;
35             uint32_t crc;
36             uint32_t offset;
37             uint64_t offset_long;
38             };
39              
40             struct git_indexer {
41             unsigned int parsed_header :1,
42             pack_committed :1,
43             have_stream :1,
44             have_delta :1,
45             do_fsync :1,
46             do_verify :1;
47             struct git_pack_header hdr;
48             struct git_pack_file *pack;
49             unsigned int mode;
50             off64_t off;
51             off64_t entry_start;
52             git_object_t entry_type;
53             git_buf entry_data;
54             git_packfile_stream stream;
55             size_t nr_objects;
56             git_vector objects;
57             git_vector deltas;
58             unsigned int fanout[256];
59             git_hash_ctx hash_ctx;
60             git_oid hash;
61             git_indexer_progress_cb progress_cb;
62             void *progress_payload;
63             char objbuf[8*1024];
64              
65             /* OIDs referenced from pack objects. Used for verification. */
66             git_oidmap *expected_oids;
67              
68             /* Needed to look up objects which we want to inject to fix a thin pack */
69             git_odb *odb;
70              
71             /* Fields for calculating the packfile trailer (hash of everything before it) */
72             char inbuf[GIT_OID_RAWSZ];
73             size_t inbuf_len;
74             git_hash_ctx trailer;
75             };
76              
77             struct delta_info {
78             off64_t delta_off;
79             };
80              
81 5           const git_oid *git_indexer_hash(const git_indexer *idx)
82             {
83 5           return &idx->hash;
84             }
85              
86 5           static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
87             {
88             int error;
89             git_map map;
90              
91 5 50         if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
92 0           return error;
93              
94 5           memcpy(hdr, map.data, sizeof(*hdr));
95 5           p_munmap(&map);
96              
97             /* Verify we recognize this pack file format. */
98 5 50         if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
99 0           git_error_set(GIT_ERROR_INDEXER, "wrong pack signature");
100 0           return -1;
101             }
102              
103 5 50         if (!pack_version_ok(hdr->hdr_version)) {
    0          
104 0           git_error_set(GIT_ERROR_INDEXER, "wrong pack version");
105 0           return -1;
106             }
107              
108 5           return 0;
109             }
110              
111 154           static int objects_cmp(const void *a, const void *b)
112             {
113 154           const struct entry *entrya = a;
114 154           const struct entry *entryb = b;
115              
116 154           return git_oid__cmp(&entrya->oid, &entryb->oid);
117             }
118              
119 0           int git_indexer_options_init(git_indexer_options *opts, unsigned int version)
120             {
121 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
122             opts, version, git_indexer_options, GIT_INDEXER_OPTIONS_INIT);
123 0           return 0;
124             }
125              
126 0           int git_indexer_init_options(git_indexer_options *opts, unsigned int version)
127             {
128 0           return git_indexer_options_init(opts, version);
129             }
130              
131 5           int git_indexer_new(
132             git_indexer **out,
133             const char *prefix,
134             unsigned int mode,
135             git_odb *odb,
136             git_indexer_options *in_opts)
137             {
138 5           git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
139             git_indexer *idx;
140 5           git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT;
141             static const char suff[] = "/pack";
142 5           int error, fd = -1;
143              
144 5 100         if (in_opts)
145 4           memcpy(&opts, in_opts, sizeof(opts));
146              
147 5           idx = git__calloc(1, sizeof(git_indexer));
148 5 50         GIT_ERROR_CHECK_ALLOC(idx);
149 5           idx->odb = odb;
150 5           idx->progress_cb = opts.progress_cb;
151 5           idx->progress_payload = opts.progress_cb_payload;
152 5 50         idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
153 5           git_buf_init(&idx->entry_data, 0);
154              
155 5 50         if ((error = git_hash_ctx_init(&idx->hash_ctx)) < 0 ||
    50          
156 5 50         (error = git_hash_ctx_init(&idx->trailer)) < 0 ||
157 5           (error = git_oidmap_new(&idx->expected_oids)) < 0)
158             goto cleanup;
159              
160 5           idx->do_verify = opts.verify;
161              
162 5 50         if (git_repository__fsync_gitdir)
163 0           idx->do_fsync = 1;
164              
165 5           error = git_buf_joinpath(&path, prefix, suff);
166 5 50         if (error < 0)
167 0           goto cleanup;
168              
169 5           fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode);
170 5           git_buf_dispose(&path);
171 5 50         if (fd < 0)
172 0           goto cleanup;
173              
174 5           error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path));
175 5           git_buf_dispose(&tmp_path);
176              
177 5 50         if (error < 0)
178 0           goto cleanup;
179              
180 5           idx->pack->mwf.fd = fd;
181 5 50         if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
182 0           goto cleanup;
183              
184 5           *out = idx;
185 5           return 0;
186              
187             cleanup:
188 0 0         if (fd != -1)
189 0           p_close(fd);
190              
191 0 0         if (git_buf_len(&tmp_path) > 0)
192 0           p_unlink(git_buf_cstr(&tmp_path));
193              
194 0 0         if (idx->pack != NULL)
195 0           p_unlink(idx->pack->pack_name);
196              
197 0           git_buf_dispose(&path);
198 0           git_buf_dispose(&tmp_path);
199 0           git__free(idx);
200 5           return -1;
201             }
202              
203 0           void git_indexer__set_fsync(git_indexer *idx, int do_fsync)
204             {
205 0           idx->do_fsync = !!do_fsync;
206 0           }
207              
208             /* Try to store the delta so we can try to resolve it later */
209 2           static int store_delta(git_indexer *idx)
210             {
211             struct delta_info *delta;
212              
213 2           delta = git__calloc(1, sizeof(struct delta_info));
214 2 50         GIT_ERROR_CHECK_ALLOC(delta);
215 2           delta->delta_off = idx->entry_start;
216              
217 2 50         if (git_vector_insert(&idx->deltas, delta) < 0)
218 0           return -1;
219              
220 2           return 0;
221             }
222              
223 43           static int hash_header(git_hash_ctx *ctx, off64_t len, git_object_t type)
224             {
225             char buffer[64];
226             size_t hdrlen;
227             int error;
228              
229 43 50         if ((error = git_odb__format_object_header(&hdrlen,
230             buffer, sizeof(buffer), (size_t)len, type)) < 0)
231 0           return error;
232              
233 43           return git_hash_update(ctx, buffer, hdrlen);
234             }
235              
236 43           static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
237             {
238             ssize_t read;
239              
240 43 50         assert(idx && stream);
    50          
241              
242             do {
243 86 50         if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0)
244 0           break;
245              
246 86 50         if (idx->do_verify)
247 0           git_buf_put(&idx->entry_data, idx->objbuf, read);
248              
249 86           git_hash_update(&idx->hash_ctx, idx->objbuf, read);
250 86 100         } while (read > 0);
251              
252 43 50         if (read < 0)
253 0           return (int)read;
254              
255 43           return 0;
256             }
257              
258             /* In order to create the packfile stream, we need to skip over the delta base description */
259 2           static int advance_delta_offset(git_indexer *idx, git_object_t type)
260             {
261 2           git_mwindow *w = NULL;
262              
263 2 50         assert(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA);
    0          
264              
265 2 50         if (type == GIT_OBJECT_REF_DELTA) {
266 2           idx->off += GIT_OID_RAWSZ;
267             } else {
268             off64_t base_off;
269 0           int error = get_delta_base(&base_off, idx->pack, &w, &idx->off, type, idx->entry_start);
270 0           git_mwindow_close(&w);
271 0 0         if (error < 0)
272 0           return error;
273             }
274              
275 2           return 0;
276             }
277              
278             /* Read from the stream and discard any output */
279 4           static int read_object_stream(git_indexer *idx, git_packfile_stream *stream)
280             {
281             ssize_t read;
282              
283 4 50         assert(stream);
284              
285             do {
286 6           read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf));
287 6 100         } while (read > 0);
288              
289 4 100         if (read < 0)
290 2           return (int)read;
291              
292 2           return 0;
293             }
294              
295 45           static int crc_object(uint32_t *crc_out, git_mwindow_file *mwf, off64_t start, off64_t size)
296             {
297             void *ptr;
298             uint32_t crc;
299             unsigned int left, len;
300 45           git_mwindow *w = NULL;
301              
302 45           crc = crc32(0L, Z_NULL, 0);
303 90 100         while (size) {
304 45           ptr = git_mwindow_open(mwf, &w, start, (size_t)size, &left);
305 45 50         if (ptr == NULL)
306 0           return -1;
307              
308 45           len = min(left, (unsigned int)size);
309 45           crc = crc32(crc, ptr, len);
310 45           size -= len;
311 45           start += len;
312 45           git_mwindow_close(&w);
313             }
314              
315 45           *crc_out = htonl(crc);
316 45           return 0;
317             }
318              
319 0           static int add_expected_oid(git_indexer *idx, const git_oid *oid)
320             {
321             /*
322             * If we know about that object because it is stored in our ODB or
323             * because we have already processed it as part of our pack file, we do
324             * not have to expect it.
325             */
326 0 0         if ((!idx->odb || !git_odb_exists(idx->odb, oid)) &&
327 0 0         !git_oidmap_exists(idx->pack->idx_cache, oid) &&
328 0           !git_oidmap_exists(idx->expected_oids, oid)) {
329 0           git_oid *dup = git__malloc(sizeof(*oid));
330 0 0         GIT_ERROR_CHECK_ALLOC(dup);
331 0           git_oid_cpy(dup, oid);
332 0           return git_oidmap_set(idx->expected_oids, dup, dup);
333             }
334              
335 0           return 0;
336             }
337              
338 0           static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj)
339             {
340             git_object *object;
341             git_oid *expected;
342             int error;
343              
344 0 0         if (obj->type != GIT_OBJECT_BLOB &&
    0          
345 0 0         obj->type != GIT_OBJECT_TREE &&
346 0 0         obj->type != GIT_OBJECT_COMMIT &&
347 0           obj->type != GIT_OBJECT_TAG)
348 0           return 0;
349              
350 0 0         if ((error = git_object__from_raw(&object, obj->data, obj->len, obj->type)) < 0)
351 0           goto out;
352              
353 0 0         if ((expected = git_oidmap_get(idx->expected_oids, &object->cached.oid)) != NULL) {
354 0           git_oidmap_delete(idx->expected_oids, &object->cached.oid);
355 0           git__free(expected);
356             }
357              
358             /*
359             * Check whether this is a known object. If so, we can just continue as
360             * we assume that the ODB has a complete graph.
361             */
362 0 0         if (idx->odb && git_odb_exists(idx->odb, &object->cached.oid))
    0          
363 0           return 0;
364              
365 0           switch (obj->type) {
366             case GIT_OBJECT_TREE:
367             {
368 0           git_tree *tree = (git_tree *) object;
369             git_tree_entry *entry;
370             size_t i;
371              
372 0 0         git_array_foreach(tree->entries, i, entry)
    0          
373 0 0         if (add_expected_oid(idx, entry->oid) < 0)
374 0           goto out;
375              
376 0           break;
377             }
378             case GIT_OBJECT_COMMIT:
379             {
380 0           git_commit *commit = (git_commit *) object;
381             git_oid *parent_oid;
382             size_t i;
383              
384 0 0         git_array_foreach(commit->parent_ids, i, parent_oid)
    0          
385 0 0         if (add_expected_oid(idx, parent_oid) < 0)
386 0           goto out;
387              
388 0 0         if (add_expected_oid(idx, &commit->tree_id) < 0)
389 0           goto out;
390              
391 0           break;
392             }
393             case GIT_OBJECT_TAG:
394             {
395 0           git_tag *tag = (git_tag *) object;
396              
397 0 0         if (add_expected_oid(idx, &tag->target) < 0)
398 0           goto out;
399              
400 0           break;
401             }
402             case GIT_OBJECT_BLOB:
403             default:
404 0           break;
405             }
406              
407             out:
408 0           git_object_free(object);
409              
410 0           return error;
411             }
412              
413 43           static int store_object(git_indexer *idx)
414             {
415             int i, error;
416             git_oid oid;
417             struct entry *entry;
418             off64_t entry_size;
419             struct git_pack_entry *pentry;
420 43           off64_t entry_start = idx->entry_start;
421              
422 43           entry = git__calloc(1, sizeof(*entry));
423 43 50         GIT_ERROR_CHECK_ALLOC(entry);
424              
425 43           pentry = git__calloc(1, sizeof(struct git_pack_entry));
426 43 50         GIT_ERROR_CHECK_ALLOC(pentry);
427              
428 43           git_hash_final(&oid, &idx->hash_ctx);
429 43           entry_size = idx->off - entry_start;
430 43 50         if (entry_start > UINT31_MAX) {
431 0           entry->offset = UINT32_MAX;
432 0           entry->offset_long = entry_start;
433             } else {
434 43           entry->offset = (uint32_t)entry_start;
435             }
436              
437 43 50         if (idx->do_verify) {
438 0           git_rawobj rawobj = {
439 0           idx->entry_data.ptr,
440 0           idx->entry_data.size,
441 0           idx->entry_type
442             };
443              
444 0 0         if ((error = check_object_connectivity(idx, &rawobj)) < 0)
445 0           goto on_error;
446             }
447              
448 43           git_oid_cpy(&pentry->sha1, &oid);
449 43           pentry->offset = entry_start;
450              
451 43 50         if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1)) {
452 0           git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", git_oid_tostr_s(&pentry->sha1));
453 0           git__free(pentry);
454 0           goto on_error;
455             }
456              
457 43 50         if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry)) < 0) {
458 0           git__free(pentry);
459 0           git_error_set_oom();
460 0           goto on_error;
461             }
462              
463 43           git_oid_cpy(&entry->oid, &oid);
464              
465 43 50         if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
466 0           goto on_error;
467              
468             /* Add the object to the list */
469 43 50         if (git_vector_insert(&idx->objects, entry) < 0)
470 0           goto on_error;
471              
472 5273 100         for (i = oid.id[0]; i < 256; ++i) {
473 5230           idx->fanout[i]++;
474             }
475              
476 43           return 0;
477              
478             on_error:
479 0           git__free(entry);
480              
481 43           return -1;
482             }
483              
484 0           GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id)
485             {
486 0           return git_oidmap_exists(idx->pack->idx_cache, id);
487             }
488              
489 2           static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, off64_t entry_start)
490             {
491             int i;
492              
493 2 50         if (entry_start > UINT31_MAX) {
494 0           entry->offset = UINT32_MAX;
495 0           entry->offset_long = entry_start;
496             } else {
497 2           entry->offset = (uint32_t)entry_start;
498             }
499              
500 2           pentry->offset = entry_start;
501              
502 4           if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1) ||
503 2           git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry) < 0) {
504 0           git_error_set(GIT_ERROR_INDEXER, "cannot insert object into pack");
505 0           return -1;
506             }
507              
508             /* Add the object to the list */
509 2 50         if (git_vector_insert(&idx->objects, entry) < 0)
510 0           return -1;
511              
512 136 100         for (i = entry->oid.id[0]; i < 256; ++i) {
513 134           idx->fanout[i]++;
514             }
515              
516 2           return 0;
517             }
518              
519 2           static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start)
520             {
521             git_oid oid;
522             size_t entry_size;
523             struct entry *entry;
524 2           struct git_pack_entry *pentry = NULL;
525              
526 2           entry = git__calloc(1, sizeof(*entry));
527 2 50         GIT_ERROR_CHECK_ALLOC(entry);
528              
529 2 50         if (git_odb__hashobj(&oid, obj) < 0) {
530 0           git_error_set(GIT_ERROR_INDEXER, "failed to hash object");
531 0           goto on_error;
532             }
533              
534 2           pentry = git__calloc(1, sizeof(struct git_pack_entry));
535 2 50         GIT_ERROR_CHECK_ALLOC(pentry);
536              
537 2           git_oid_cpy(&pentry->sha1, &oid);
538 2           git_oid_cpy(&entry->oid, &oid);
539 2           entry->crc = crc32(0L, Z_NULL, 0);
540              
541 2           entry_size = (size_t)(idx->off - entry_start);
542 2 50         if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
543 0           goto on_error;
544              
545 2           return save_entry(idx, entry, pentry, entry_start);
546              
547             on_error:
548 0           git__free(pentry);
549 0           git__free(entry);
550 0           git__free(obj->data);
551 2           return -1;
552             }
553              
554 52           static int do_progress_callback(git_indexer *idx, git_indexer_progress *stats)
555             {
556 52 100         if (idx->progress_cb)
557 41           return git_error_set_after_callback_function(
558 41           idx->progress_cb(stats, idx->progress_payload),
559             "indexer progress");
560 11           return 0;
561             }
562              
563             /* Hash everything but the last 20B of input */
564 91           static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
565             {
566             size_t to_expell, to_keep;
567              
568 91 50         if (size == 0)
569 0           return;
570              
571             /* Easy case, dump the buffer and the data minus the last 20 bytes */
572 91 100         if (size >= GIT_OID_RAWSZ) {
573 45           git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
574 45           git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
575              
576 45           data += size - GIT_OID_RAWSZ;
577 45           memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
578 45           idx->inbuf_len = GIT_OID_RAWSZ;
579 45           return;
580             }
581              
582             /* We can just append */
583 46 100         if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
584 8           memcpy(idx->inbuf + idx->inbuf_len, data, size);
585 8           idx->inbuf_len += size;
586 8           return;
587             }
588              
589             /* We need to partially drain the buffer and then append */
590 38           to_keep = GIT_OID_RAWSZ - size;
591 38           to_expell = idx->inbuf_len - to_keep;
592              
593 38           git_hash_update(&idx->trailer, idx->inbuf, to_expell);
594              
595 38           memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
596 38           memcpy(idx->inbuf + to_keep, data, size);
597 38           idx->inbuf_len += size - to_expell;
598             }
599              
600 91           static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
601             {
602 91           git_file fd = idx->pack->mwf.fd;
603             size_t mmap_alignment;
604             size_t page_offset;
605             off64_t page_start;
606             unsigned char *map_data;
607             git_map map;
608             int error;
609              
610 91 50         assert(data && size);
    50          
611              
612 91 50         if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
613 0           return error;
614              
615             /* the offset needs to be at the mmap boundary for the platform */
616 91           page_offset = offset % mmap_alignment;
617 91           page_start = offset - page_offset;
618              
619 91 50         if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
620 0           return error;
621              
622 91           map_data = (unsigned char *)map.data;
623 91           memcpy(map_data + page_offset, data, size);
624 91           p_munmap(&map);
625              
626 91           return 0;
627             }
628              
629 91           static int append_to_pack(git_indexer *idx, const void *data, size_t size)
630             {
631             off64_t new_size;
632             size_t mmap_alignment;
633             size_t page_offset;
634             off64_t page_start;
635 91           off64_t current_size = idx->pack->mwf.size;
636 91           int fd = idx->pack->mwf.fd;
637             int error;
638              
639 91 50         if (!size)
640 0           return 0;
641              
642 91 50         if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
643 0           return error;
644              
645             /* Write a single byte to force the file system to allocate space now or
646             * report an error, since we can't report errors when writing using mmap.
647             * Round the size up to the nearest page so that we only need to perform file
648             * I/O when we add a page, instead of whenever we write even a single byte. */
649 91           new_size = current_size + size;
650 91           page_offset = new_size % mmap_alignment;
651 91           page_start = new_size - page_offset;
652              
653 182           if (p_lseek(fd, page_start + mmap_alignment - 1, SEEK_SET) < 0 ||
654 91           p_write(idx->pack->mwf.fd, data, 1) < 0) {
655 0           git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
656 0           return -1;
657             }
658              
659 91           return write_at(idx, data, idx->pack->mwf.size, size);
660             }
661              
662 131           static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
663             {
664 131           git_packfile_stream *stream = &idx->stream;
665 131           off64_t entry_start = idx->off;
666             size_t entry_size;
667             git_object_t type;
668 131           git_mwindow *w = NULL;
669             int error;
670              
671 131 100         if (idx->pack->mwf.size <= idx->off + 20)
672 84           return GIT_EBUFS;
673              
674 47 100         if (!idx->have_stream) {
675 45           error = git_packfile_unpack_header(&entry_size, &type, &idx->pack->mwf, &w, &idx->off);
676 45 50         if (error == GIT_EBUFS) {
677 0           idx->off = entry_start;
678 0           return error;
679             }
680 45 50         if (error < 0)
681 0           return error;
682              
683 45           git_mwindow_close(&w);
684 45           idx->entry_start = entry_start;
685 45           git_hash_init(&idx->hash_ctx);
686 45           git_buf_clear(&idx->entry_data);
687              
688 45 100         if (type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA) {
    50          
689 2           error = advance_delta_offset(idx, type);
690 2 50         if (error == GIT_EBUFS) {
691 0           idx->off = entry_start;
692 0           return error;
693             }
694 2 50         if (error < 0)
695 0           return error;
696              
697 2           idx->have_delta = 1;
698             } else {
699 43           idx->have_delta = 0;
700              
701 43           error = hash_header(&idx->hash_ctx, entry_size, type);
702 43 50         if (error < 0)
703 0           return error;
704             }
705              
706 45           idx->have_stream = 1;
707 45           idx->entry_type = type;
708              
709 45           error = git_packfile_stream_open(stream, idx->pack, idx->off);
710 45 50         if (error < 0)
711 0           return error;
712             }
713              
714 47 100         if (idx->have_delta) {
715 4           error = read_object_stream(idx, stream);
716             } else {
717 43           error = hash_object_stream(idx, stream);
718             }
719              
720 47           idx->off = stream->curpos;
721 47 100         if (error == GIT_EBUFS)
722 2           return error;
723              
724             /* We want to free the stream reasorces no matter what here */
725 45           idx->have_stream = 0;
726 45           git_packfile_stream_dispose(stream);
727              
728 45 50         if (error < 0)
729 0           return error;
730              
731 45 100         if (idx->have_delta) {
732 2           error = store_delta(idx);
733             } else {
734 43           error = store_object(idx);
735             }
736              
737 45 50         if (error < 0)
738 0           return error;
739              
740 45 100         if (!idx->have_delta) {
741 43           stats->indexed_objects++;
742             }
743 45           stats->received_objects++;
744              
745 45 50         if ((error = do_progress_callback(idx, stats)) != 0)
746 0           return error;
747              
748 131           return 0;
749             }
750              
751 91           int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_indexer_progress *stats)
752             {
753 91           int error = -1;
754 91           struct git_pack_header *hdr = &idx->hdr;
755 91           git_mwindow_file *mwf = &idx->pack->mwf;
756              
757 91 50         assert(idx && data && stats);
    50          
    50          
758              
759 91 50         if ((error = append_to_pack(idx, data, size)) < 0)
760 0           return error;
761              
762 91           hash_partially(idx, data, (int)size);
763              
764             /* Make sure we set the new size of the pack */
765 91           idx->pack->mwf.size += size;
766              
767 91 100         if (!idx->parsed_header) {
768             unsigned int total_objects;
769              
770 5 50         if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
771 0           return 0;
772              
773 5 50         if ((error = parse_header(&idx->hdr, idx->pack)) < 0)
774 0           return error;
775              
776 5           idx->parsed_header = 1;
777 5           idx->nr_objects = ntohl(hdr->hdr_entries);
778 5           idx->off = sizeof(struct git_pack_header);
779              
780 5 50         if (idx->nr_objects <= git_indexer__max_objects) {
781 5           total_objects = (unsigned int)idx->nr_objects;
782             } else {
783 0           git_error_set(GIT_ERROR_INDEXER, "too many objects");
784 0           return -1;
785             }
786              
787 5 50         if (git_oidmap_new(&idx->pack->idx_cache) < 0)
788 0           return -1;
789              
790 5           idx->pack->has_cache = 1;
791 5 50         if (git_vector_init(&idx->objects, total_objects, objects_cmp) < 0)
792 0           return -1;
793              
794 5 50         if (git_vector_init(&idx->deltas, total_objects / 2, NULL) < 0)
795 0           return -1;
796              
797 5           stats->received_objects = 0;
798 5           stats->local_objects = 0;
799 5           stats->total_deltas = 0;
800 5           stats->indexed_deltas = 0;
801 5           stats->indexed_objects = 0;
802 5           stats->total_objects = total_objects;
803              
804 5 50         if ((error = do_progress_callback(idx, stats)) != 0)
805 0           return error;
806             }
807              
808             /* Now that we have data in the pack, let's try to parse it */
809              
810             /* As the file grows any windows we try to use will be out of date */
811 91           git_mwindow_free_all(mwf);
812              
813 136 100         while (stats->indexed_objects < idx->nr_objects) {
814 131 100         if ((error = read_stream_object(idx, stats)) != 0) {
815 86 50         if (error == GIT_EBUFS)
816 86           break;
817             else
818 0           goto on_error;
819             }
820             }
821              
822 91           return 0;
823              
824             on_error:
825 0           git_mwindow_free_all(mwf);
826 0           return error;
827             }
828              
829 10           static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
830             {
831 10           const char prefix[] = "pack-";
832 10           size_t slash = (size_t)path->size;
833              
834             /* search backwards for '/' */
835 330 50         while (slash > 0 && path->ptr[slash - 1] != '/')
    100          
836 320           slash--;
837              
838 10 50         if (git_buf_grow(path, slash + 1 + strlen(prefix) +
839 10           GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
840 0           return -1;
841              
842 10           git_buf_truncate(path, slash);
843 10           git_buf_puts(path, prefix);
844 10           git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
845 10           path->size += GIT_OID_HEXSZ;
846 10           git_buf_puts(path, suffix);
847              
848 10 50         return git_buf_oom(path) ? -1 : 0;
849             }
850              
851             /**
852             * Rewind the packfile by the trailer, as we might need to fix the
853             * packfile by injecting objects at the tail and must overwrite it.
854             */
855 0           static void seek_back_trailer(git_indexer *idx)
856             {
857 0           idx->pack->mwf.size -= GIT_OID_RAWSZ;
858 0           git_mwindow_free_all(&idx->pack->mwf);
859 0           }
860              
861 0           static int inject_object(git_indexer *idx, git_oid *id)
862             {
863             git_odb_object *obj;
864             struct entry *entry;
865 0           struct git_pack_entry *pentry = NULL;
866 0           git_oid foo = {{0}};
867             unsigned char hdr[64];
868 0           git_buf buf = GIT_BUF_INIT;
869             off64_t entry_start;
870             const void *data;
871             size_t len, hdr_len;
872             int error;
873              
874 0           seek_back_trailer(idx);
875 0           entry_start = idx->pack->mwf.size;
876              
877 0 0         if (git_odb_read(&obj, idx->odb, id) < 0) {
878 0           git_error_set(GIT_ERROR_INDEXER, "missing delta bases");
879 0           return -1;
880             }
881              
882 0           data = git_odb_object_data(obj);
883 0           len = git_odb_object_size(obj);
884              
885 0           entry = git__calloc(1, sizeof(*entry));
886 0 0         GIT_ERROR_CHECK_ALLOC(entry);
887              
888 0           entry->crc = crc32(0L, Z_NULL, 0);
889              
890             /* Write out the object header */
891 0           hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
892 0 0         if ((error = append_to_pack(idx, hdr, hdr_len)) < 0)
893 0           goto cleanup;
894              
895 0           idx->pack->mwf.size += hdr_len;
896 0           entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
897              
898 0 0         if ((error = git_zstream_deflatebuf(&buf, data, len)) < 0)
899 0           goto cleanup;
900              
901             /* And then the compressed object */
902 0 0         if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0)
903 0           goto cleanup;
904              
905 0           idx->pack->mwf.size += buf.size;
906 0           entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
907 0           git_buf_dispose(&buf);
908              
909             /* Write a fake trailer so the pack functions play ball */
910              
911 0 0         if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0)
912 0           goto cleanup;
913              
914 0           idx->pack->mwf.size += GIT_OID_RAWSZ;
915              
916 0           pentry = git__calloc(1, sizeof(struct git_pack_entry));
917 0 0         GIT_ERROR_CHECK_ALLOC(pentry);
918              
919 0           git_oid_cpy(&pentry->sha1, id);
920 0           git_oid_cpy(&entry->oid, id);
921 0           idx->off = entry_start + hdr_len + len;
922              
923 0           error = save_entry(idx, entry, pentry, entry_start);
924              
925             cleanup:
926 0 0         if (error) {
927 0           git__free(entry);
928 0           git__free(pentry);
929             }
930              
931 0           git_odb_object_free(obj);
932 0           return error;
933             }
934              
935 0           static int fix_thin_pack(git_indexer *idx, git_indexer_progress *stats)
936             {
937 0           int error, found_ref_delta = 0;
938             unsigned int i;
939             struct delta_info *delta;
940             size_t size;
941             git_object_t type;
942 0           git_mwindow *w = NULL;
943 0           off64_t curpos = 0;
944             unsigned char *base_info;
945 0           unsigned int left = 0;
946             git_oid base;
947              
948 0 0         assert(git_vector_length(&idx->deltas) > 0);
949              
950 0 0         if (idx->odb == NULL) {
951 0           git_error_set(GIT_ERROR_INDEXER, "cannot fix a thin pack without an ODB");
952 0           return -1;
953             }
954              
955             /* Loop until we find the first REF delta */
956 0 0         git_vector_foreach(&idx->deltas, i, delta) {
957 0 0         if (!delta)
958 0           continue;
959              
960 0           curpos = delta->delta_off;
961 0           error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
962 0 0         if (error < 0)
963 0           return error;
964              
965 0 0         if (type == GIT_OBJECT_REF_DELTA) {
966 0           found_ref_delta = 1;
967 0           break;
968             }
969             }
970              
971 0 0         if (!found_ref_delta) {
972 0           git_error_set(GIT_ERROR_INDEXER, "no REF_DELTA found, cannot inject object");
973 0           return -1;
974             }
975              
976             /* curpos now points to the base information, which is an OID */
977 0           base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left);
978 0 0         if (base_info == NULL) {
979 0           git_error_set(GIT_ERROR_INDEXER, "failed to map delta information");
980 0           return -1;
981             }
982              
983 0           git_oid_fromraw(&base, base_info);
984 0           git_mwindow_close(&w);
985              
986 0 0         if (has_entry(idx, &base))
987 0           return 0;
988              
989 0 0         if (inject_object(idx, &base) < 0)
990 0           return -1;
991              
992 0           stats->local_objects++;
993              
994 0           return 0;
995             }
996              
997 5           static int resolve_deltas(git_indexer *idx, git_indexer_progress *stats)
998             {
999             unsigned int i;
1000             int error;
1001             struct delta_info *delta;
1002 5           int progressed = 0, non_null = 0, progress_cb_result;
1003              
1004 7 100         while (idx->deltas.length > 0) {
1005 4           progressed = 0;
1006 4           non_null = 0;
1007 8 100         git_vector_foreach(&idx->deltas, i, delta) {
1008 4           git_rawobj obj = {0};
1009              
1010 4 100         if (!delta)
1011 2           continue;
1012              
1013 2           non_null = 1;
1014 2           idx->off = delta->delta_off;
1015 2 50         if ((error = git_packfile_unpack(&obj, idx->pack, &idx->off)) < 0) {
1016 0 0         if (error == GIT_PASSTHROUGH) {
1017             /* We have not seen the base object, we'll try again later. */
1018 0           continue;
1019             }
1020 0           return -1;
1021             }
1022              
1023 2 50         if (idx->do_verify && check_object_connectivity(idx, &obj) < 0)
    0          
1024             /* TODO: error? continue? */
1025 0           continue;
1026              
1027 2 50         if (hash_and_save(idx, &obj, delta->delta_off) < 0)
1028 0           continue;
1029              
1030 2           git__free(obj.data);
1031 2           stats->indexed_objects++;
1032 2           stats->indexed_deltas++;
1033 2           progressed = 1;
1034 2 50         if ((progress_cb_result = do_progress_callback(idx, stats)) < 0)
1035 0           return progress_cb_result;
1036              
1037             /* remove from the list */
1038 2           git_vector_set(NULL, &idx->deltas, i, NULL);
1039 2           git__free(delta);
1040             }
1041              
1042             /* if none were actually set, we're done */
1043 4 100         if (!non_null)
1044 2           break;
1045              
1046 2 50         if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
    0          
1047 0           return -1;
1048             }
1049             }
1050              
1051 5           return 0;
1052             }
1053              
1054 0           static int update_header_and_rehash(git_indexer *idx, git_indexer_progress *stats)
1055             {
1056             void *ptr;
1057 0           size_t chunk = 1024*1024;
1058 0           off64_t hashed = 0;
1059 0           git_mwindow *w = NULL;
1060             git_mwindow_file *mwf;
1061             unsigned int left;
1062              
1063 0           mwf = &idx->pack->mwf;
1064              
1065 0           git_hash_init(&idx->trailer);
1066              
1067              
1068             /* Update the header to include the numer of local objects we injected */
1069 0           idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
1070 0 0         if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
1071 0           return -1;
1072              
1073             /*
1074             * We now use the same technique as before to determine the
1075             * hash. We keep reading up to the end and let
1076             * hash_partially() keep the existing trailer out of the
1077             * calculation.
1078             */
1079 0           git_mwindow_free_all(mwf);
1080 0           idx->inbuf_len = 0;
1081 0 0         while (hashed < mwf->size) {
1082 0           ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
1083 0 0         if (ptr == NULL)
1084 0           return -1;
1085              
1086 0           hash_partially(idx, ptr, left);
1087 0           hashed += left;
1088              
1089 0           git_mwindow_close(&w);
1090             }
1091              
1092 0           return 0;
1093             }
1094              
1095 5           int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
1096             {
1097 5           git_mwindow *w = NULL;
1098 5           unsigned int i, long_offsets = 0, left;
1099             int error;
1100             struct git_pack_idx_header hdr;
1101 5           git_buf filename = GIT_BUF_INIT;
1102             struct entry *entry;
1103             git_oid trailer_hash, file_hash;
1104 5           git_filebuf index_file = {0};
1105             void *packfile_trailer;
1106              
1107 5 50         if (!idx->parsed_header) {
1108 0           git_error_set(GIT_ERROR_INDEXER, "incomplete pack header");
1109 0           return -1;
1110             }
1111              
1112             /* Test for this before resolve_deltas(), as it plays with idx->off */
1113 5 50         if (idx->off + 20 < idx->pack->mwf.size) {
1114 0           git_error_set(GIT_ERROR_INDEXER, "unexpected data at the end of the pack");
1115 0           return -1;
1116             }
1117 5 50         if (idx->off + 20 > idx->pack->mwf.size) {
1118 0           git_error_set(GIT_ERROR_INDEXER, "missing trailer at the end of the pack");
1119 0           return -1;
1120             }
1121              
1122 5           packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
1123 5 50         if (packfile_trailer == NULL) {
1124 0           git_mwindow_close(&w);
1125 0           goto on_error;
1126             }
1127              
1128             /* Compare the packfile trailer as it was sent to us and what we calculated */
1129 5           git_oid_fromraw(&file_hash, packfile_trailer);
1130 5           git_mwindow_close(&w);
1131              
1132 5           git_hash_final(&trailer_hash, &idx->trailer);
1133 5 50         if (git_oid_cmp(&file_hash, &trailer_hash)) {
1134 0           git_error_set(GIT_ERROR_INDEXER, "packfile trailer mismatch");
1135 0           return -1;
1136             }
1137              
1138             /* Freeze the number of deltas */
1139 5           stats->total_deltas = stats->total_objects - stats->indexed_objects;
1140              
1141 5 50         if ((error = resolve_deltas(idx, stats)) < 0)
1142 0           return error;
1143              
1144 5 50         if (stats->indexed_objects != stats->total_objects) {
1145 0           git_error_set(GIT_ERROR_INDEXER, "early EOF");
1146 0           return -1;
1147             }
1148              
1149 5 50         if (stats->local_objects > 0) {
1150 0 0         if (update_header_and_rehash(idx, stats) < 0)
1151 0           return -1;
1152              
1153 0           git_hash_final(&trailer_hash, &idx->trailer);
1154 0           write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
1155             }
1156              
1157             /*
1158             * Is the resulting graph fully connected or are we still
1159             * missing some objects? In the second case, we can
1160             * bail out due to an incomplete and thus corrupt
1161             * packfile.
1162             */
1163 5 50         if (git_oidmap_size(idx->expected_oids) > 0) {
1164 0           git_error_set(GIT_ERROR_INDEXER, "packfile is missing %"PRIuZ" objects",
1165             git_oidmap_size(idx->expected_oids));
1166 0           return -1;
1167             }
1168              
1169 5           git_vector_sort(&idx->objects);
1170              
1171             /* Use the trailer hash as the pack file name to ensure
1172             * files with different contents have different names */
1173 5           git_oid_cpy(&idx->hash, &trailer_hash);
1174              
1175 5           git_buf_sets(&filename, idx->pack->pack_name);
1176 5           git_buf_shorten(&filename, strlen("pack"));
1177 5           git_buf_puts(&filename, "idx");
1178 5 50         if (git_buf_oom(&filename))
1179 0           return -1;
1180              
1181 5 50         if (git_filebuf_open(&index_file, filename.ptr,
    50          
1182             GIT_FILEBUF_HASH_CONTENTS |
1183 5           (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0),
1184             idx->mode) < 0)
1185 0           goto on_error;
1186              
1187             /* Write out the header */
1188 5           hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
1189 5           hdr.idx_version = htonl(2);
1190 5           git_filebuf_write(&index_file, &hdr, sizeof(hdr));
1191              
1192             /* Write out the fanout table */
1193 1285 100         for (i = 0; i < 256; ++i) {
1194 1280           uint32_t n = htonl(idx->fanout[i]);
1195 1280           git_filebuf_write(&index_file, &n, sizeof(n));
1196             }
1197              
1198             /* Write out the object names (SHA-1 hashes) */
1199 50 100         git_vector_foreach(&idx->objects, i, entry) {
1200 45           git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid));
1201             }
1202              
1203             /* Write out the CRC32 values */
1204 50 100         git_vector_foreach(&idx->objects, i, entry) {
1205 45           git_filebuf_write(&index_file, &entry->crc, sizeof(uint32_t));
1206             }
1207              
1208             /* Write out the offsets */
1209 50 100         git_vector_foreach(&idx->objects, i, entry) {
1210             uint32_t n;
1211              
1212 45 50         if (entry->offset == UINT32_MAX)
1213 0           n = htonl(0x80000000 | long_offsets++);
1214             else
1215 45           n = htonl(entry->offset);
1216              
1217 45           git_filebuf_write(&index_file, &n, sizeof(uint32_t));
1218             }
1219              
1220             /* Write out the long offsets */
1221 50 100         git_vector_foreach(&idx->objects, i, entry) {
1222             uint32_t split[2];
1223              
1224 45 50         if (entry->offset != UINT32_MAX)
1225 45           continue;
1226              
1227 0           split[0] = htonl(entry->offset_long >> 32);
1228 0           split[1] = htonl(entry->offset_long & 0xffffffff);
1229              
1230 0           git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
1231             }
1232              
1233             /* Write out the packfile trailer to the index */
1234 5 50         if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
1235 0           goto on_error;
1236              
1237             /* Write out the hash of the idx */
1238 5 50         if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
1239 0           goto on_error;
1240              
1241 5           git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));
1242              
1243             /* Figure out what the final name should be */
1244 5 50         if (index_path(&filename, idx, ".idx") < 0)
1245 0           goto on_error;
1246              
1247             /* Commit file */
1248 5 50         if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
1249 0           goto on_error;
1250              
1251 5           git_mwindow_free_all(&idx->pack->mwf);
1252              
1253             /* Truncate file to undo rounding up to next page_size in append_to_pack */
1254 5 50         if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) {
1255 0           git_error_set(GIT_ERROR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name);
1256 0           return -1;
1257             }
1258              
1259 5 50         if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) {
    0          
1260 0           git_error_set(GIT_ERROR_OS, "failed to fsync packfile");
1261 0           goto on_error;
1262             }
1263              
1264             /* We need to close the descriptor here so Windows doesn't choke on commit_at */
1265 5 50         if (p_close(idx->pack->mwf.fd) < 0) {
1266 0           git_error_set(GIT_ERROR_OS, "failed to close packfile");
1267 0           goto on_error;
1268             }
1269              
1270 5           idx->pack->mwf.fd = -1;
1271              
1272 5 50         if (index_path(&filename, idx, ".pack") < 0)
1273 0           goto on_error;
1274              
1275             /* And don't forget to rename the packfile to its new place. */
1276 5 50         if (p_rename(idx->pack->pack_name, git_buf_cstr(&filename)) < 0)
1277 0           goto on_error;
1278              
1279             /* And fsync the parent directory if we're asked to. */
1280 5           if (idx->do_fsync &&
1281 0           git_futils_fsync_parent(git_buf_cstr(&filename)) < 0)
1282 0           goto on_error;
1283              
1284 5           idx->pack_committed = 1;
1285              
1286 5           git_buf_dispose(&filename);
1287 5           return 0;
1288              
1289             on_error:
1290 0           git_mwindow_free_all(&idx->pack->mwf);
1291 0           git_filebuf_cleanup(&index_file);
1292 0           git_buf_dispose(&filename);
1293 5           return -1;
1294             }
1295              
1296 5           void git_indexer_free(git_indexer *idx)
1297             {
1298             const git_oid *key;
1299             git_oid *value;
1300             size_t iter;
1301              
1302 5 50         if (idx == NULL)
1303 0           return;
1304              
1305 5 50         if (idx->have_stream)
1306 0           git_packfile_stream_dispose(&idx->stream);
1307              
1308 5           git_vector_free_deep(&idx->objects);
1309              
1310 5 50         if (idx->pack->idx_cache) {
1311             struct git_pack_entry *pentry;
1312 50 100         git_oidmap_foreach_value(idx->pack->idx_cache, pentry, {
1313             git__free(pentry);
1314             });
1315              
1316 5           git_oidmap_free(idx->pack->idx_cache);
1317             }
1318              
1319 5           git_vector_free_deep(&idx->deltas);
1320              
1321 5 50         if (!git_mutex_lock(&git__mwindow_mutex)) {
1322 5 50         if (!idx->pack_committed)
1323 0           git_packfile_close(idx->pack, true);
1324              
1325 5           git_packfile_free(idx->pack);
1326             git_mutex_unlock(&git__mwindow_mutex);
1327             }
1328              
1329 5           iter = 0;
1330 5 50         while (git_oidmap_iterate((void **) &value, idx->expected_oids, &iter, &key) == 0)
1331 0           git__free(value);
1332              
1333 5           git_hash_ctx_cleanup(&idx->trailer);
1334 5           git_hash_ctx_cleanup(&idx->hash_ctx);
1335 5           git_buf_dispose(&idx->entry_data);
1336 5           git_oidmap_free(idx->expected_oids);
1337 5           git__free(idx);
1338             }