File Coverage

deps/libgit2/src/libgit2/pack.c
Criterion Covered Total %
statement 455 814 55.9
branch 181 490 36.9
condition n/a
subroutine n/a
pod n/a
total 636 1304 48.7


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 "pack.h"
9              
10             #include "delta.h"
11             #include "futils.h"
12             #include "mwindow.h"
13             #include "odb.h"
14             #include "oid.h"
15             #include "oidarray.h"
16              
17             /* Option to bypass checking existence of '.keep' files */
18             bool git_disable_pack_keep_file_checks = false;
19              
20             static int packfile_open_locked(struct git_pack_file *p);
21             static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n);
22             static int packfile_unpack_compressed(
23             git_rawobj *obj,
24             struct git_pack_file *p,
25             git_mwindow **w_curs,
26             off64_t *curpos,
27             size_t size,
28             git_object_t type);
29              
30             /* Can find the offset of an object given
31             * a prefix of an identifier.
32             * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
33             * is ambiguous within the pack.
34             * This method assumes that len is between
35             * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
36             */
37             static int pack_entry_find_offset(
38             off64_t *offset_out,
39             git_oid *found_oid,
40             struct git_pack_file *p,
41             const git_oid *short_oid,
42             size_t len);
43              
44 0           static int packfile_error(const char *message)
45             {
46 0           git_error_set(GIT_ERROR_ODB, "invalid pack file - %s", message);
47 0           return -1;
48             }
49              
50             /********************
51             * Delta base cache
52             ********************/
53              
54 2           static git_pack_cache_entry *new_cache_object(git_rawobj *source)
55             {
56 2           git_pack_cache_entry *e = git__calloc(1, sizeof(git_pack_cache_entry));
57 2 50         if (!e)
58 0           return NULL;
59              
60 2           git_atomic32_inc(&e->refcount);
61 2           memcpy(&e->raw, source, sizeof(git_rawobj));
62              
63 2           return e;
64             }
65              
66 2           static void free_cache_object(void *o)
67             {
68 2           git_pack_cache_entry *e = (git_pack_cache_entry *)o;
69              
70 2 50         if (e != NULL) {
71 2           git__free(e->raw.data);
72 2           git__free(e);
73             }
74 2           }
75              
76 8           static void cache_free(git_pack_cache *cache)
77             {
78             git_pack_cache_entry *entry;
79              
80 8 50         if (cache->entries) {
81 10 100         git_offmap_foreach_value(cache->entries, entry, {
82             free_cache_object(entry);
83             });
84              
85 8           git_offmap_free(cache->entries);
86 8           cache->entries = NULL;
87             }
88 8           }
89              
90 9           static int cache_init(git_pack_cache *cache)
91             {
92 9 50         if (git_offmap_new(&cache->entries) < 0)
93 0           return -1;
94              
95 9           cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT;
96              
97 9 50         if (git_mutex_init(&cache->lock)) {
98 0           git_error_set(GIT_ERROR_OS, "failed to initialize pack cache mutex");
99              
100 0           git__free(cache->entries);
101 0           cache->entries = NULL;
102              
103 0           return -1;
104             }
105              
106 9           return 0;
107             }
108              
109 5           static git_pack_cache_entry *cache_get(git_pack_cache *cache, off64_t offset)
110             {
111             git_pack_cache_entry *entry;
112              
113 5 50         if (git_mutex_lock(&cache->lock) < 0)
114 0           return NULL;
115              
116 5 50         if ((entry = git_offmap_get(cache->entries, offset)) != NULL) {
117 0           git_atomic32_inc(&entry->refcount);
118 0           entry->last_usage = cache->use_ctr++;
119             }
120 5           git_mutex_unlock(&cache->lock);
121              
122 5           return entry;
123             }
124              
125             /* Run with the cache lock held */
126 0           static void free_lowest_entry(git_pack_cache *cache)
127             {
128             off64_t offset;
129             git_pack_cache_entry *entry;
130              
131 0 0         git_offmap_foreach(cache->entries, offset, entry, {
    0          
    0          
132             if (entry && git_atomic32_get(&entry->refcount) == 0) {
133             cache->memory_used -= entry->raw.len;
134             git_offmap_delete(cache->entries, offset);
135             free_cache_object(entry);
136             }
137             });
138 0           }
139              
140 2           static int cache_add(
141             git_pack_cache_entry **cached_out,
142             git_pack_cache *cache,
143             git_rawobj *base,
144             off64_t offset)
145             {
146             git_pack_cache_entry *entry;
147             int exists;
148              
149 2 50         if (base->len > GIT_PACK_CACHE_SIZE_LIMIT)
150 0           return -1;
151              
152 2           entry = new_cache_object(base);
153 2 50         if (entry) {
154 2 50         if (git_mutex_lock(&cache->lock) < 0) {
155 0           git_error_set(GIT_ERROR_OS, "failed to lock cache");
156 0           git__free(entry);
157 0           return -1;
158             }
159             /* Add it to the cache if nobody else has */
160 2           exists = git_offmap_exists(cache->entries, offset);
161 2 50         if (!exists) {
162 2 50         while (cache->memory_used + base->len > cache->memory_limit)
163 0           free_lowest_entry(cache);
164              
165 2           git_offmap_set(cache->entries, offset, entry);
166 2           cache->memory_used += entry->raw.len;
167              
168 2           *cached_out = entry;
169             }
170 2           git_mutex_unlock(&cache->lock);
171             /* Somebody beat us to adding it into the cache */
172 2 50         if (exists) {
173 0           git__free(entry);
174 0           return -1;
175             }
176             }
177              
178 2           return 0;
179             }
180              
181             /***********************************************************
182             *
183             * PACK INDEX METHODS
184             *
185             ***********************************************************/
186              
187 8           static void pack_index_free(struct git_pack_file *p)
188             {
189 8 50         if (p->oids) {
190 0           git__free(p->oids);
191 0           p->oids = NULL;
192             }
193 8 100         if (p->index_map.data) {
194 3           git_futils_mmap_free(&p->index_map);
195 3           p->index_map.data = NULL;
196             }
197 8           }
198              
199             /* Run with the packfile lock held */
200 3           static int pack_index_check_locked(const char *path, struct git_pack_file *p)
201             {
202             struct git_pack_idx_header *hdr;
203             uint32_t version, nr, i, *index;
204             void *idx_map;
205             size_t idx_size;
206             struct stat st;
207             int error;
208             /* TODO: properly open the file without access time using O_NOATIME */
209 3           git_file fd = git_futils_open_ro(path);
210 3 50         if (fd < 0)
211 0           return fd;
212              
213 3 50         if (p_fstat(fd, &st) < 0) {
214 0           p_close(fd);
215 0           git_error_set(GIT_ERROR_OS, "unable to stat pack index '%s'", path);
216 0           return -1;
217             }
218              
219 6           if (!S_ISREG(st.st_mode) ||
220 6 50         !git__is_sizet(st.st_size) ||
221 3           (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
222             {
223 0           p_close(fd);
224 0           git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
225 0           return -1;
226             }
227              
228 3           error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);
229              
230 3           p_close(fd);
231              
232 3 50         if (error < 0)
233 0           return error;
234              
235 3           hdr = idx_map = p->index_map.data;
236              
237 3 50         if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
238 3           version = ntohl(hdr->idx_version);
239              
240 3 50         if (version < 2 || version > 2) {
    50          
241 0           git_futils_mmap_free(&p->index_map);
242 0           return packfile_error("unsupported index version");
243             }
244              
245             } else
246 0           version = 1;
247              
248 3           nr = 0;
249 3           index = idx_map;
250              
251 3 50         if (version > 1)
252 3           index += 2; /* skip index header */
253              
254 771 100         for (i = 0; i < 256; i++) {
255 768           uint32_t n = ntohl(index[i]);
256 768 50         if (n < nr) {
257 0           git_futils_mmap_free(&p->index_map);
258 0           return packfile_error("index is non-monotonic");
259             }
260 768           nr = n;
261             }
262              
263 3 50         if (version == 1) {
264             /*
265             * Total size:
266             * - 256 index entries 4 bytes each
267             * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
268             * - 20-byte SHA1 of the packfile
269             * - 20-byte SHA1 file checksum
270             */
271 0 0         if (idx_size != 4*256 + nr * 24 + 20 + 20) {
272 0           git_futils_mmap_free(&p->index_map);
273 0           return packfile_error("index is corrupted");
274             }
275 3 50         } else if (version == 2) {
276             /*
277             * Minimum size:
278             * - 8 bytes of header
279             * - 256 index entries 4 bytes each
280             * - 20-byte sha1 entry * nr
281             * - 4-byte crc entry * nr
282             * - 4-byte offset entry * nr
283             * - 20-byte SHA1 of the packfile
284             * - 20-byte SHA1 file checksum
285             * And after the 4-byte offset table might be a
286             * variable sized table containing 8-byte entries
287             * for offsets larger than 2^31.
288             */
289 3           unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
290 3           unsigned long max_size = min_size;
291              
292 3 50         if (nr)
293 3           max_size += (nr - 1)*8;
294              
295 3 50         if (idx_size < min_size || idx_size > max_size) {
    50          
296 0           git_futils_mmap_free(&p->index_map);
297 0           return packfile_error("wrong index size");
298             }
299             }
300              
301 3           p->num_objects = nr;
302 3           p->index_version = version;
303 3           return 0;
304             }
305              
306             /* Run with the packfile lock held */
307 6           static int pack_index_open_locked(struct git_pack_file *p)
308             {
309 6           int error = 0;
310             size_t name_len;
311 6           git_str idx_name = GIT_STR_INIT;
312              
313 6 100         if (p->index_version > -1)
314 3           goto cleanup;
315              
316             /* checked by git_pack_file alloc */
317 3           name_len = strlen(p->pack_name);
318 3 50         GIT_ASSERT(name_len > strlen(".pack"));
319              
320 3 50         if ((error = git_str_init(&idx_name, name_len)) < 0)
321 0           goto cleanup;
322              
323 3           git_str_put(&idx_name, p->pack_name, name_len - strlen(".pack"));
324 3           git_str_puts(&idx_name, ".idx");
325 3 50         if (git_str_oom(&idx_name)) {
326 0           error = -1;
327 0           goto cleanup;
328             }
329              
330 3 50         if (p->index_version == -1)
331 3           error = pack_index_check_locked(idx_name.ptr, p);
332              
333             cleanup:
334 6           git_str_dispose(&idx_name);
335              
336 6           return error;
337             }
338              
339 54           static unsigned char *pack_window_open(
340             struct git_pack_file *p,
341             git_mwindow **w_cursor,
342             off64_t offset,
343             unsigned int *left)
344             {
345 54           unsigned char *pack_data = NULL;
346              
347 54 50         if (git_mutex_lock(&p->lock) < 0) {
348 0           git_error_set(GIT_ERROR_THREAD, "unable to lock packfile");
349 0           return NULL;
350             }
351 54 50         if (git_mutex_lock(&p->mwf.lock) < 0) {
352 0           git_mutex_unlock(&p->lock);
353 0           git_error_set(GIT_ERROR_THREAD, "unable to lock packfile");
354 0           return NULL;
355             }
356              
357 54 50         if (p->mwf.fd == -1 && packfile_open_locked(p) < 0)
    0          
358 0           goto cleanup;
359              
360             /* Since packfiles end in a hash of their content and it's
361             * pointless to ask for an offset into the middle of that
362             * hash, and the pack_window_contains function above wouldn't match
363             * don't allow an offset too close to the end of the file.
364             *
365             * Don't allow a negative offset, as that means we've wrapped
366             * around.
367             */
368 54 100         if (offset > (p->mwf.size - 20))
369 2           goto cleanup;
370 52 50         if (offset < 0)
371 0           goto cleanup;
372              
373 52           pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
374              
375             cleanup:
376 54           git_mutex_unlock(&p->mwf.lock);
377 54           git_mutex_unlock(&p->lock);
378 54           return pack_data;
379             }
380              
381             /*
382             * The per-object header is a pretty dense thing, which is
383             * - first byte: low four bits are "size",
384             * then three bits of "type",
385             * with the high bit being "size continues".
386             * - each byte afterwards: low seven bits are size continuation,
387             * with the high bit being "size continues"
388             */
389 53           int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type)
390             {
391             unsigned char *hdr_base;
392             unsigned char c;
393              
394 53 50         GIT_ASSERT_ARG(type >= GIT_OBJECT_COMMIT && type <= GIT_OBJECT_REF_DELTA);
    50          
395              
396             /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
397              
398 53           c = (unsigned char)((type << 4) | (size & 15));
399 53           size >>= 4;
400 53           hdr_base = hdr;
401              
402 98 100         while (size) {
403 45           *hdr++ = c | 0x80;
404 45           c = size & 0x7f;
405 45           size >>= 7;
406             }
407 53           *hdr++ = c;
408              
409 53           *out = (hdr - hdr_base);
410 53           return 0;
411             }
412              
413              
414 52           static int packfile_unpack_header1(
415             unsigned long *usedp,
416             size_t *sizep,
417             git_object_t *type,
418             const unsigned char *buf,
419             unsigned long len)
420             {
421             unsigned shift;
422             unsigned long size, c;
423 52           unsigned long used = 0;
424              
425 52           c = buf[used++];
426 52           *type = (c >> 4) & 7;
427 52           size = c & 15;
428 52           shift = 4;
429 95 100         while (c & 0x80) {
430 43 50         if (len <= used) {
431 0           git_error_set(GIT_ERROR_ODB, "buffer too small");
432 0           return GIT_EBUFS;
433             }
434              
435 43 50         if (bitsizeof(long) <= shift) {
436 0           *usedp = 0;
437 0           git_error_set(GIT_ERROR_ODB, "packfile corrupted");
438 0           return -1;
439             }
440              
441 43           c = buf[used++];
442 43           size += (c & 0x7f) << shift;
443 43           shift += 7;
444             }
445              
446 52           *sizep = (size_t)size;
447 52           *usedp = used;
448 52           return 0;
449             }
450              
451 52           int git_packfile_unpack_header(
452             size_t *size_p,
453             git_object_t *type_p,
454             struct git_pack_file *p,
455             git_mwindow **w_curs,
456             off64_t *curpos)
457             {
458             unsigned char *base;
459             unsigned int left;
460             unsigned long used;
461             int error;
462              
463 52 50         if ((error = git_mutex_lock(&p->lock)) < 0)
464 0           return error;
465 52 50         if ((error = git_mutex_lock(&p->mwf.lock)) < 0) {
466 0           git_mutex_unlock(&p->lock);
467 0           return error;
468             }
469              
470 52 50         if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) {
    0          
471 0           git_mutex_unlock(&p->lock);
472 0           git_mutex_unlock(&p->mwf.lock);
473 0           return error;
474             }
475              
476             /* pack_window_open() assures us we have [base, base + 20) available
477             * as a range that we can look at at. (Its actually the hash
478             * size that is assured.) With our object header encoding
479             * the maximum deflated object size is 2^137, which is just
480             * insane, so we know won't exceed what we have been given.
481             */
482 52           base = git_mwindow_open(&p->mwf, w_curs, *curpos, 20, &left);
483 52           git_mutex_unlock(&p->lock);
484 52           git_mutex_unlock(&p->mwf.lock);
485 52 50         if (base == NULL)
486 0           return GIT_EBUFS;
487              
488 52           error = packfile_unpack_header1(&used, size_p, type_p, base, left);
489 52           git_mwindow_close(w_curs);
490 52 50         if (error == GIT_EBUFS)
491 0           return error;
492 52 50         else if (error < 0)
493 0           return packfile_error("header length is zero");
494              
495 52           *curpos += used;
496 52           return 0;
497             }
498              
499 2           int git_packfile_resolve_header(
500             size_t *size_p,
501             git_object_t *type_p,
502             struct git_pack_file *p,
503             off64_t offset)
504             {
505 2           git_mwindow *w_curs = NULL;
506 2           off64_t curpos = offset;
507             size_t size;
508             git_object_t type;
509             off64_t base_offset;
510             int error;
511              
512 2           error = git_mutex_lock(&p->lock);
513 2 50         if (error < 0) {
514 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
515 0           return error;
516             }
517 2           error = git_mutex_lock(&p->mwf.lock);
518 2 50         if (error < 0) {
519 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
520 0           git_mutex_unlock(&p->lock);
521 0           return error;
522             }
523              
524 2 50         if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) {
    0          
525 0           git_mutex_unlock(&p->mwf.lock);
526 0           git_mutex_unlock(&p->lock);
527 0           return error;
528             }
529 2           git_mutex_unlock(&p->mwf.lock);
530 2           git_mutex_unlock(&p->lock);
531              
532 2           error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
533 2 50         if (error < 0)
534 0           return error;
535              
536 2 50         if (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) {
    50          
537             size_t base_size;
538             git_packfile_stream stream;
539              
540 0           error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, offset);
541 0           git_mwindow_close(&w_curs);
542              
543 0 0         if (error < 0)
544 0           return error;
545              
546 0 0         if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
547 0           return error;
548 0           error = git_delta_read_header_fromstream(&base_size, size_p, &stream);
549 0           git_packfile_stream_dispose(&stream);
550 0 0         if (error < 0)
551 0           return error;
552             } else {
553 2           *size_p = size;
554 2           base_offset = 0;
555             }
556              
557 2 50         while (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) {
    50          
558 0           curpos = base_offset;
559 0           error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
560 0 0         if (error < 0)
561 0           return error;
562 0 0         if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA)
    0          
563 0           break;
564              
565 0           error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, base_offset);
566 0           git_mwindow_close(&w_curs);
567              
568 0 0         if (error < 0)
569 0           return error;
570             }
571 2           *type_p = type;
572              
573 2           return error;
574             }
575              
576             #define SMALL_STACK_SIZE 64
577              
578             /**
579             * Generate the chain of dependencies which we need to get to the
580             * object at `off`. `chain` is used a stack, popping gives the right
581             * order to apply deltas on. If an object is found in the pack's base
582             * cache, we stop calculating there.
583             */
584 3           static int pack_dependency_chain(git_dependency_chain *chain_out,
585             git_pack_cache_entry **cached_out, off64_t *cached_off,
586             struct pack_chain_elem *small_stack, size_t *stack_sz,
587             struct git_pack_file *p, off64_t obj_offset)
588             {
589 3           git_dependency_chain chain = GIT_ARRAY_INIT;
590 3           git_mwindow *w_curs = NULL;
591 3           off64_t curpos = obj_offset, base_offset;
592 3           int error = 0, use_heap = 0;
593             size_t size, elem_pos;
594             git_object_t type;
595              
596 3           elem_pos = 0;
597             while (true) {
598             struct pack_chain_elem *elem;
599 5           git_pack_cache_entry *cached = NULL;
600              
601             /* if we have a base cached, we can stop here instead */
602 5 50         if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
603 0           *cached_out = cached;
604 0           *cached_off = obj_offset;
605 0           break;
606             }
607              
608             /* if we run out of space on the small stack, use the array */
609 5 50         if (elem_pos == SMALL_STACK_SIZE) {
610 0           git_array_init_to_size(chain, elem_pos);
611 0 0         GIT_ERROR_CHECK_ARRAY(chain);
612 0           memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
613 0           chain.size = elem_pos;
614 0           use_heap = 1;
615             }
616              
617 5           curpos = obj_offset;
618 5 50         if (!use_heap) {
619 5           elem = &small_stack[elem_pos];
620             } else {
621 0 0         elem = git_array_alloc(chain);
    0          
622 0 0         if (!elem) {
623 0           error = -1;
624 0           goto on_error;
625             }
626             }
627              
628 5           elem->base_key = obj_offset;
629              
630 5           error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
631 5 50         if (error < 0)
632 0           goto on_error;
633              
634 5           elem->offset = curpos;
635 5           elem->size = size;
636 5           elem->type = type;
637 5           elem->base_key = obj_offset;
638              
639 5 50         if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA)
    100          
640 3           break;
641              
642 2           error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, obj_offset);
643 2           git_mwindow_close(&w_curs);
644              
645 2 50         if (error < 0)
646 0           goto on_error;
647              
648             /* we need to pass the pos *after* the delta-base bit */
649 2           elem->offset = curpos;
650              
651             /* go through the loop again, but with the new object */
652 2           obj_offset = base_offset;
653 2           elem_pos++;
654 2           }
655              
656              
657 3           *stack_sz = elem_pos + 1;
658 3           *chain_out = chain;
659 3           return error;
660              
661             on_error:
662 0           git_array_clear(chain);
663 3           return error;
664             }
665              
666 3           int git_packfile_unpack(
667             git_rawobj *obj,
668             struct git_pack_file *p,
669             off64_t *obj_offset)
670             {
671 3           git_mwindow *w_curs = NULL;
672 3           off64_t curpos = *obj_offset;
673 3           int error, free_base = 0;
674 3           git_dependency_chain chain = GIT_ARRAY_INIT;
675 3           struct pack_chain_elem *elem = NULL, *stack;
676 3           git_pack_cache_entry *cached = NULL;
677             struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
678 3           size_t stack_size = 0, elem_pos, alloclen;
679             git_object_t base_type;
680              
681 3           error = git_mutex_lock(&p->lock);
682 3 50         if (error < 0) {
683 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
684 0           return error;
685             }
686 3           error = git_mutex_lock(&p->mwf.lock);
687 3 50         if (error < 0) {
688 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
689 0           git_mutex_unlock(&p->lock);
690 0           return error;
691             }
692              
693 3 50         if (p->mwf.fd == -1)
694 0           error = packfile_open_locked(p);
695 3           git_mutex_unlock(&p->mwf.lock);
696 3           git_mutex_unlock(&p->lock);
697 3 50         if (error < 0)
698 0           return error;
699              
700             /*
701             * TODO: optionally check the CRC on the packfile
702             */
703              
704 3           error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset);
705 3 50         if (error < 0)
706 0           return error;
707              
708 3           obj->data = NULL;
709 3           obj->len = 0;
710 3           obj->type = GIT_OBJECT_INVALID;
711              
712             /* let's point to the right stack */
713 3 50         stack = chain.ptr ? chain.ptr : small_stack;
714              
715 3           elem_pos = stack_size;
716 3 50         if (cached) {
717 0           memcpy(obj, &cached->raw, sizeof(git_rawobj));
718 0           base_type = obj->type;
719 0           elem_pos--; /* stack_size includes the base, which isn't actually there */
720             } else {
721 3           elem = &stack[--elem_pos];
722 3           base_type = elem->type;
723             }
724              
725 3           switch (base_type) {
726             case GIT_OBJECT_COMMIT:
727             case GIT_OBJECT_TREE:
728             case GIT_OBJECT_BLOB:
729             case GIT_OBJECT_TAG:
730 3 50         if (!cached) {
731 3           curpos = elem->offset;
732 3           error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type);
733 3           git_mwindow_close(&w_curs);
734 3           base_type = elem->type;
735             }
736 3 50         if (error < 0)
737 0           goto cleanup;
738 3           break;
739             case GIT_OBJECT_OFS_DELTA:
740             case GIT_OBJECT_REF_DELTA:
741 0           error = packfile_error("dependency chain ends in a delta");
742 0           goto cleanup;
743             default:
744 0           error = packfile_error("invalid packfile type in header");
745 0           goto cleanup;
746             }
747              
748             /*
749             * Finding the object we want a cached base element is
750             * problematic, as we need to make sure we don't accidentally
751             * give the caller the cached object, which it would then feel
752             * free to free, so we need to copy the data.
753             */
754 3 50         if (cached && stack_size == 1) {
    0          
755 0           void *data = obj->data;
756              
757 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, obj->len, 1);
    0          
758 0           obj->data = git__malloc(alloclen);
759 0 0         GIT_ERROR_CHECK_ALLOC(obj->data);
760              
761 0           memcpy(obj->data, data, obj->len + 1);
762 0           git_atomic32_dec(&cached->refcount);
763 0           goto cleanup;
764             }
765              
766             /* we now apply each consecutive delta until we run out */
767 5 100         while (elem_pos > 0 && !error) {
    50          
768             git_rawobj base, delta;
769              
770             /*
771             * We can now try to add the base to the cache, as
772             * long as it's not already the cached one.
773             */
774 2 50         if (!cached)
775 2           free_base = !!cache_add(&cached, &p->bases, obj, elem->base_key);
776              
777 2           elem = &stack[elem_pos - 1];
778 2           curpos = elem->offset;
779 2           error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
780 2           git_mwindow_close(&w_curs);
781              
782 2 50         if (error < 0) {
783             /* We have transferred ownership of the data to the cache. */
784 0           obj->data = NULL;
785 0           break;
786             }
787              
788             /* the current object becomes the new base, on which we apply the delta */
789 2           base = *obj;
790 2           obj->data = NULL;
791 2           obj->len = 0;
792 2           obj->type = GIT_OBJECT_INVALID;
793              
794 2           error = git_delta_apply(&obj->data, &obj->len, base.data, base.len, delta.data, delta.len);
795 2           obj->type = base_type;
796              
797             /*
798             * We usually don't want to free the base at this
799             * point, as we put it into the cache in the previous
800             * iteration. free_base lets us know that we got the
801             * base object directly from the packfile, so we can free it.
802             */
803 2           git__free(delta.data);
804 2 50         if (free_base) {
805 0           free_base = 0;
806 0           git__free(base.data);
807             }
808              
809 2 50         if (cached) {
810 2           git_atomic32_dec(&cached->refcount);
811 2           cached = NULL;
812             }
813              
814 2 50         if (error < 0)
815 0           break;
816              
817 2           elem_pos--;
818             }
819              
820             cleanup:
821 3 50         if (error < 0) {
822 0           git__free(obj->data);
823 0 0         if (cached)
824 0           git_atomic32_dec(&cached->refcount);
825             }
826              
827 3 50         if (elem)
828 3           *obj_offset = curpos;
829              
830 3           git_array_clear(chain);
831 3           return error;
832             }
833              
834 45           int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, off64_t curpos)
835             {
836 45           memset(obj, 0, sizeof(git_packfile_stream));
837 45           obj->curpos = curpos;
838 45           obj->p = p;
839              
840 45 50         if (git_zstream_init(&obj->zstream, GIT_ZSTREAM_INFLATE) < 0) {
841 0           git_error_set(GIT_ERROR_ZLIB, "failed to init packfile stream");
842 0           return -1;
843             }
844              
845 45           return 0;
846             }
847              
848 92           ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len)
849             {
850             unsigned int window_len;
851             unsigned char *in;
852             int error;
853              
854 92 100         if (obj->done)
855 45           return 0;
856              
857 47 100         if ((in = pack_window_open(obj->p, &obj->mw, obj->curpos, &window_len)) == NULL)
858 2           return GIT_EBUFS;
859              
860 45 50         if ((error = git_zstream_set_input(&obj->zstream, in, window_len)) < 0 ||
    50          
861 45           (error = git_zstream_get_output_chunk(buffer, &len, &obj->zstream)) < 0) {
862 0           git_mwindow_close(&obj->mw);
863 0           git_error_set(GIT_ERROR_ZLIB, "error reading from the zlib stream");
864 0           return -1;
865             }
866              
867 45           git_mwindow_close(&obj->mw);
868              
869 45           obj->curpos += window_len - obj->zstream.in_len;
870              
871 45 50         if (git_zstream_eos(&obj->zstream))
872 45           obj->done = 1;
873              
874             /* If we didn't write anything out but we're not done, we need more data */
875 45 50         if (!len && !git_zstream_eos(&obj->zstream))
    0          
876 0           return GIT_EBUFS;
877              
878 92           return len;
879              
880             }
881              
882 45           void git_packfile_stream_dispose(git_packfile_stream *obj)
883             {
884 45           git_zstream_free(&obj->zstream);
885 45           }
886              
887 5           static int packfile_unpack_compressed(
888             git_rawobj *obj,
889             struct git_pack_file *p,
890             git_mwindow **mwindow,
891             off64_t *position,
892             size_t size,
893             git_object_t type)
894             {
895 5           git_zstream zstream = GIT_ZSTREAM_INIT;
896 5           size_t buffer_len, total = 0;
897 5           char *data = NULL;
898             int error;
899              
900 5 50         GIT_ERROR_CHECK_ALLOC_ADD(&buffer_len, size, 1);
    50          
901 5           data = git__calloc(1, buffer_len);
902 5 50         GIT_ERROR_CHECK_ALLOC(data);
903              
904 5 50         if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0) {
905 0           git_error_set(GIT_ERROR_ZLIB, "failed to init zlib stream on unpack");
906 0           goto out;
907             }
908              
909             do {
910 5           size_t bytes = buffer_len - total;
911             unsigned int window_len, consumed;
912             unsigned char *in;
913              
914 5 50         if ((in = pack_window_open(p, mwindow, *position, &window_len)) == NULL) {
915 0           error = -1;
916 0           goto out;
917             }
918              
919 5 50         if ((error = git_zstream_set_input(&zstream, in, window_len)) < 0 ||
    50          
920 5           (error = git_zstream_get_output_chunk(data + total, &bytes, &zstream)) < 0) {
921 0           git_mwindow_close(mwindow);
922 0           goto out;
923             }
924              
925 5           git_mwindow_close(mwindow);
926              
927 5           consumed = window_len - (unsigned int)zstream.in_len;
928              
929 5 50         if (!bytes && !consumed) {
    0          
930 0           git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream");
931 0           error = -1;
932 0           goto out;
933             }
934              
935 5           *position += consumed;
936 5           total += bytes;
937 5 50         } while (!git_zstream_eos(&zstream));
938              
939 5 50         if (total != size || !git_zstream_eos(&zstream)) {
    50          
940 0           git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream");
941 0           error = -1;
942 0           goto out;
943             }
944              
945 5           obj->type = type;
946 5           obj->len = size;
947 5           obj->data = data;
948              
949             out:
950 5           git_zstream_free(&zstream);
951 5 50         if (error)
952 0           git__free(data);
953              
954 5           return error;
955             }
956              
957             /*
958             * curpos is where the data starts, delta_obj_offset is the where the
959             * header starts
960             */
961 2           int get_delta_base(
962             off64_t *delta_base_out,
963             struct git_pack_file *p,
964             git_mwindow **w_curs,
965             off64_t *curpos,
966             git_object_t type,
967             off64_t delta_obj_offset)
968             {
969 2           unsigned int left = 0;
970             unsigned char *base_info;
971             off64_t base_offset;
972             git_oid unused;
973              
974 2 50         GIT_ASSERT_ARG(delta_base_out);
975              
976 2           base_info = pack_window_open(p, w_curs, *curpos, &left);
977             /* Assumption: the only reason this would fail is because the file is too small */
978 2 50         if (base_info == NULL)
979 0           return GIT_EBUFS;
980             /* pack_window_open() assured us we have [base_info, base_info + 20)
981             * as a range that we can look at without walking off the
982             * end of the mapped window. Its actually the hash size
983             * that is assured. An OFS_DELTA longer than the hash size
984             * is stupid, as then a REF_DELTA would be smaller to store.
985             */
986 2 50         if (type == GIT_OBJECT_OFS_DELTA) {
987 0           unsigned used = 0;
988 0           unsigned char c = base_info[used++];
989 0           size_t unsigned_base_offset = c & 127;
990 0 0         while (c & 128) {
991 0 0         if (left <= used)
992 0           return GIT_EBUFS;
993 0           unsigned_base_offset += 1;
994 0 0         if (!unsigned_base_offset || MSB(unsigned_base_offset, 7))
    0          
995 0           return packfile_error("overflow");
996 0           c = base_info[used++];
997 0           unsigned_base_offset = (unsigned_base_offset << 7) + (c & 127);
998             }
999 0 0         if (unsigned_base_offset == 0 || (size_t)delta_obj_offset <= unsigned_base_offset)
    0          
1000 0           return packfile_error("out of bounds");
1001 0           base_offset = delta_obj_offset - unsigned_base_offset;
1002 0           *curpos += used;
1003 2 50         } else if (type == GIT_OBJECT_REF_DELTA) {
1004             git_oid base_oid;
1005 2           git_oid_fromraw(&base_oid, base_info);
1006              
1007             /* If we have the cooperative cache, search in it first */
1008 2 50         if (p->has_cache) {
1009             struct git_pack_entry *entry;
1010              
1011 2 50         if ((entry = git_oidmap_get(p->idx_cache, &base_oid)) != NULL) {
1012 2 50         if (entry->offset == 0)
1013 2           return packfile_error("delta offset is zero");
1014              
1015 2           *curpos += 20;
1016 2           *delta_base_out = entry->offset;
1017 2           return 0;
1018             } else {
1019             /* If we're building an index, don't try to find the pack
1020             * entry; we just haven't seen it yet. We'll make
1021             * progress again in the next loop.
1022             */
1023 0           return GIT_PASSTHROUGH;
1024             }
1025             }
1026              
1027             /* The base entry _must_ be in the same pack */
1028 0 0         if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, GIT_OID_HEXSZ) < 0)
1029 0           return packfile_error("base entry delta is not in the same pack");
1030 0           *curpos += 20;
1031             } else
1032 0           return packfile_error("unknown object type");
1033              
1034 0 0         if (base_offset == 0)
1035 0           return packfile_error("delta offset is zero");
1036              
1037 0           *delta_base_out = base_offset;
1038 2           return 0;
1039             }
1040              
1041             /***********************************************************
1042             *
1043             * PACKFILE METHODS
1044             *
1045             ***********************************************************/
1046              
1047 8           void git_packfile_free(struct git_pack_file *p, bool unlink_packfile)
1048             {
1049 8           bool locked = true;
1050              
1051 8 50         if (!p)
1052 0           return;
1053              
1054 8           cache_free(&p->bases);
1055              
1056 8 50         if (git_mutex_lock(&p->lock) < 0) {
1057 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile");
1058 0           locked = false;
1059             }
1060 8 100         if (p->mwf.fd >= 0) {
1061 3           git_mwindow_free_all(&p->mwf);
1062 3           p_close(p->mwf.fd);
1063 3           p->mwf.fd = -1;
1064             }
1065 8 50         if (locked)
1066 8           git_mutex_unlock(&p->lock);
1067              
1068 8 50         if (unlink_packfile)
1069 0           p_unlink(p->pack_name);
1070              
1071 8           pack_index_free(p);
1072              
1073 8           git__free(p->bad_object_sha1);
1074              
1075 8           git_mutex_free(&p->bases.lock);
1076 8           git_mutex_free(&p->mwf.lock);
1077 8           git_mutex_free(&p->lock);
1078 8           git__free(p);
1079             }
1080              
1081             /* Run with the packfile and mwf locks held */
1082 3           static int packfile_open_locked(struct git_pack_file *p)
1083             {
1084             struct stat st;
1085             struct git_pack_header hdr;
1086             unsigned char sha1[GIT_OID_RAWSZ];
1087             unsigned char *idx_sha1;
1088              
1089 3 50         if (pack_index_open_locked(p) < 0)
1090 0           return git_odb__error_notfound("failed to open packfile", NULL, 0);
1091              
1092 3 50         if (p->mwf.fd >= 0)
1093 0           return 0;
1094              
1095             /* TODO: open with noatime */
1096 3           p->mwf.fd = git_futils_open_ro(p->pack_name);
1097 3 50         if (p->mwf.fd < 0)
1098 0           goto cleanup;
1099              
1100 3 50         if (p_fstat(p->mwf.fd, &st) < 0) {
1101 0           git_error_set(GIT_ERROR_OS, "could not stat packfile");
1102 0           goto cleanup;
1103             }
1104              
1105             /* If we created the struct before we had the pack we lack size. */
1106 3 50         if (!p->mwf.size) {
1107 0 0         if (!S_ISREG(st.st_mode))
1108 0           goto cleanup;
1109 0           p->mwf.size = (off64_t)st.st_size;
1110 3 50         } else if (p->mwf.size != st.st_size)
1111 0           goto cleanup;
1112              
1113             #if 0
1114             /* We leave these file descriptors open with sliding mmap;
1115             * there is no point keeping them open across exec(), though.
1116             */
1117             fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);
1118             if (fd_flag < 0)
1119             goto cleanup;
1120              
1121             fd_flag |= FD_CLOEXEC;
1122             if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
1123             goto cleanup;
1124             #endif
1125              
1126             /* Verify we recognize this pack file format. */
1127 6           if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||
1128 6 50         hdr.hdr_signature != htonl(PACK_SIGNATURE) ||
1129 3           !pack_version_ok(hdr.hdr_version))
1130             goto cleanup;
1131              
1132             /* Verify the pack matches its index. */
1133 6           if (p->num_objects != ntohl(hdr.hdr_entries) ||
1134 3           p_pread(p->mwf.fd, sha1, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0)
1135             goto cleanup;
1136              
1137 3           idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
1138              
1139 3 50         if (git_oid_raw_cmp(sha1, idx_sha1) != 0)
1140 0           goto cleanup;
1141              
1142 3 50         if (git_mwindow_file_register(&p->mwf) < 0)
1143 0           goto cleanup;
1144              
1145 3           return 0;
1146              
1147             cleanup:
1148 0           git_error_set(GIT_ERROR_OS, "invalid packfile '%s'", p->pack_name);
1149              
1150 0 0         if (p->mwf.fd >= 0)
1151 0           p_close(p->mwf.fd);
1152 0           p->mwf.fd = -1;
1153              
1154 3           return -1;
1155             }
1156              
1157 4           int git_packfile__name(char **out, const char *path)
1158             {
1159             size_t path_len;
1160 4           git_str buf = GIT_STR_INIT;
1161              
1162 4           path_len = strlen(path);
1163              
1164 4 50         if (path_len < strlen(".idx"))
1165 0           return git_odb__error_notfound("invalid packfile path", NULL, 0);
1166              
1167 4 50         if (git_str_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
1168 0           return -1;
1169              
1170 4           *out = git_str_detach(&buf);
1171 4           return 0;
1172             }
1173              
1174 9           int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
1175             {
1176             struct stat st;
1177             struct git_pack_file *p;
1178 9 50         size_t path_len = path ? strlen(path) : 0, alloc_len;
1179              
1180 9           *pack_out = NULL;
1181              
1182 9 50         if (path_len < strlen(".idx"))
1183 0           return git_odb__error_notfound("invalid packfile path", NULL, 0);
1184              
1185 9 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
    50          
1186 9 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
    50          
1187              
1188 9           p = git__calloc(1, alloc_len);
1189 9 50         GIT_ERROR_CHECK_ALLOC(p);
1190              
1191 9           memcpy(p->pack_name, path, path_len + 1);
1192              
1193             /*
1194             * Make sure a corresponding .pack file exists and that
1195             * the index looks sane.
1196             */
1197 9 100         if (git__suffixcmp(path, ".idx") == 0) {
1198 3           size_t root_len = path_len - strlen(".idx");
1199              
1200 3 50         if (!git_disable_pack_keep_file_checks) {
1201 3           memcpy(p->pack_name + root_len, ".keep", sizeof(".keep"));
1202 3 50         if (git_fs_path_exists(p->pack_name) == true)
1203 0           p->pack_keep = 1;
1204             }
1205              
1206 3           memcpy(p->pack_name + root_len, ".pack", sizeof(".pack"));
1207             }
1208              
1209 9 50         if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
    50          
1210 0           git__free(p);
1211 0           return git_odb__error_notfound("packfile not found", NULL, 0);
1212             }
1213              
1214             /* ok, it looks sane as far as we can check without
1215             * actually mapping the pack file.
1216             */
1217 9           p->mwf.fd = -1;
1218 9           p->mwf.size = st.st_size;
1219 9           p->pack_local = 1;
1220 9           p->mtime = (git_time_t)st.st_mtime;
1221 9           p->index_version = -1;
1222              
1223 9 50         if (git_mutex_init(&p->lock) < 0) {
1224 0           git_error_set(GIT_ERROR_OS, "failed to initialize packfile mutex");
1225 0           git__free(p);
1226 0           return -1;
1227             }
1228              
1229 9 50         if (git_mutex_init(&p->mwf.lock) < 0) {
1230 0           git_error_set(GIT_ERROR_OS, "failed to initialize packfile window mutex");
1231 0           git_mutex_free(&p->lock);
1232 0           git__free(p);
1233 0           return -1;
1234             }
1235              
1236 9 50         if (cache_init(&p->bases) < 0) {
1237 0           git_mutex_free(&p->mwf.lock);
1238 0           git_mutex_free(&p->lock);
1239 0           git__free(p);
1240 0           return -1;
1241             }
1242              
1243 9           *pack_out = p;
1244              
1245 9           return 0;
1246             }
1247              
1248             /***********************************************************
1249             *
1250             * PACKFILE ENTRY SEARCH INTERNALS
1251             *
1252             ***********************************************************/
1253              
1254 3           static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n)
1255             {
1256             const unsigned char *index, *end;
1257             uint32_t off32;
1258              
1259 3           index = p->index_map.data;
1260 3           end = index + p->index_map.len;
1261 3           index += 4 * 256;
1262 3 50         if (p->index_version == 1)
1263 0           return ntohl(*((uint32_t *)(index + 24 * n)));
1264              
1265 3           index += 8 + p->num_objects * (20 + 4);
1266 3           off32 = ntohl(*((uint32_t *)(index + 4 * n)));
1267 3 50         if (!(off32 & 0x80000000))
1268 3           return off32;
1269 0           index += p->num_objects * 4 + (off32 & 0x7fffffff) * 8;
1270              
1271             /* Make sure we're not being sent out of bounds */
1272 0 0         if (index >= end - 8)
1273 0           return -1;
1274              
1275 0           return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
1276 0           ntohl(*((uint32_t *)(index + 4)));
1277             }
1278              
1279 0           static int git__memcmp4(const void *a, const void *b) {
1280 0           return memcmp(a, b, 4);
1281             }
1282              
1283 0           int git_pack_foreach_entry(
1284             struct git_pack_file *p,
1285             git_odb_foreach_cb cb,
1286             void *data)
1287             {
1288             const unsigned char *index, *current;
1289             uint32_t i;
1290 0           int error = 0;
1291 0           git_array_oid_t oids = GIT_ARRAY_INIT;
1292             git_oid *oid;
1293              
1294 0 0         if (git_mutex_lock(&p->lock) < 0)
1295 0           return packfile_error("failed to get lock for git_pack_foreach_entry");
1296              
1297 0 0         if ((error = pack_index_open_locked(p)) < 0) {
1298 0           git_mutex_unlock(&p->lock);
1299 0           return error;
1300             }
1301              
1302 0 0         if (!p->index_map.data) {
1303 0           git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
1304 0           git_mutex_unlock(&p->lock);
1305 0           return -1;
1306             }
1307              
1308 0           index = p->index_map.data;
1309              
1310 0 0         if (p->index_version > 1)
1311 0           index += 8;
1312              
1313 0           index += 4 * 256;
1314              
1315 0 0         if (p->oids == NULL) {
1316             git_vector offsets, oids;
1317              
1318 0 0         if ((error = git_vector_init(&oids, p->num_objects, NULL))) {
1319 0           git_mutex_unlock(&p->lock);
1320 0           return error;
1321             }
1322              
1323 0 0         if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4))) {
1324 0           git_mutex_unlock(&p->lock);
1325 0           return error;
1326             }
1327              
1328 0 0         if (p->index_version > 1) {
1329 0           const unsigned char *off = index + 24 * p->num_objects;
1330 0 0         for (i = 0; i < p->num_objects; i++)
1331 0           git_vector_insert(&offsets, (void*)&off[4 * i]);
1332 0           git_vector_sort(&offsets);
1333 0 0         git_vector_foreach(&offsets, i, current)
1334 0           git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
1335             } else {
1336 0 0         for (i = 0; i < p->num_objects; i++)
1337 0           git_vector_insert(&offsets, (void*)&index[24 * i]);
1338 0           git_vector_sort(&offsets);
1339 0 0         git_vector_foreach(&offsets, i, current)
1340 0           git_vector_insert(&oids, (void*)¤t[4]);
1341             }
1342              
1343 0           git_vector_free(&offsets);
1344 0           p->oids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
1345             }
1346              
1347             /*
1348             * We need to copy the OIDs to another array before we
1349             * relinquish the lock to avoid races. We can also take
1350             * this opportunity to put them into normal form.
1351             */
1352 0           git_array_init_to_size(oids, p->num_objects);
1353 0 0         if (!oids.ptr) {
1354 0           git_mutex_unlock(&p->lock);
1355 0           git_array_clear(oids);
1356 0 0         GIT_ERROR_CHECK_ARRAY(oids);
1357             }
1358 0 0         for (i = 0; i < p->num_objects; i++) {
1359 0 0         oid = git_array_alloc(oids);
    0          
1360 0 0         if (!oid) {
1361 0           git_mutex_unlock(&p->lock);
1362 0           git_array_clear(oids);
1363 0 0         GIT_ERROR_CHECK_ALLOC(oid);
1364             }
1365 0           git_oid_fromraw(oid, p->oids[i]);
1366             }
1367              
1368 0           git_mutex_unlock(&p->lock);
1369              
1370 0 0         git_array_foreach(oids, i, oid) {
    0          
1371 0 0         if ((error = cb(oid, data)) != 0) {
1372 0           git_error_set_after_callback(error);
1373 0           break;
1374             }
1375             }
1376              
1377 0           git_array_clear(oids);
1378 0           return error;
1379             }
1380              
1381 0           int git_pack_foreach_entry_offset(
1382             struct git_pack_file *p,
1383             git_pack_foreach_entry_offset_cb cb,
1384             void *data)
1385             {
1386             const unsigned char *index;
1387             off64_t current_offset;
1388             git_oid current_oid;
1389             uint32_t i;
1390 0           int error = 0;
1391              
1392 0 0         if (git_mutex_lock(&p->lock) < 0)
1393 0           return packfile_error("failed to get lock for git_pack_foreach_entry_offset");
1394              
1395 0           index = p->index_map.data;
1396 0 0         if (index == NULL) {
1397 0 0         if ((error = pack_index_open_locked(p)) < 0)
1398 0           goto cleanup;
1399              
1400 0 0         if (!p->index_map.data) {
1401 0           git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
1402 0           goto cleanup;
1403             }
1404              
1405 0           index = p->index_map.data;
1406             }
1407              
1408 0 0         if (p->index_version > 1)
1409 0           index += 8;
1410              
1411 0           index += 4 * 256;
1412              
1413             /* all offsets should have been validated by pack_index_check_locked */
1414 0 0         if (p->index_version > 1) {
1415 0           const unsigned char *offsets = index + 24 * p->num_objects;
1416             const unsigned char *large_offset_ptr;
1417 0           const unsigned char *large_offsets = index + 28 * p->num_objects;
1418 0           const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - 20;
1419 0 0         for (i = 0; i < p->num_objects; i++) {
1420 0           current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i));
1421 0 0         if (current_offset & 0x80000000) {
1422 0           large_offset_ptr = large_offsets + (current_offset & 0x7fffffff) * 8;
1423 0 0         if (large_offset_ptr >= large_offsets_end) {
1424 0           error = packfile_error("invalid large offset");
1425 0           goto cleanup;
1426             }
1427 0           current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) |
1428 0           ntohl(*((uint32_t *)(large_offset_ptr + 4)));
1429             }
1430              
1431 0           git_oid_fromraw(¤t_oid, (index + 20 * i));
1432 0 0         if ((error = cb(¤t_oid, current_offset, data)) != 0) {
1433 0           error = git_error_set_after_callback(error);
1434 0           goto cleanup;
1435             }
1436             }
1437             } else {
1438 0 0         for (i = 0; i < p->num_objects; i++) {
1439 0           current_offset = ntohl(*(const uint32_t *)(index + 24 * i));
1440 0           git_oid_fromraw(¤t_oid, (index + 24 * i + 4));
1441 0 0         if ((error = cb(¤t_oid, current_offset, data)) != 0) {
1442 0           error = git_error_set_after_callback(error);
1443 0           goto cleanup;
1444             }
1445             }
1446             }
1447              
1448             cleanup:
1449 0           git_mutex_unlock(&p->lock);
1450 0           return error;
1451             }
1452              
1453 3           int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
1454             unsigned hi, const unsigned char *oid_prefix)
1455             {
1456 3           const unsigned char *base = oid_lookup_table;
1457              
1458 3 50         while (lo < hi) {
1459 3           unsigned mi = (lo + hi) / 2;
1460 3           int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix);
1461              
1462 3 50         if (!cmp)
1463 3           return mi;
1464              
1465 0 0         if (cmp > 0)
1466 0           hi = mi;
1467             else
1468 0           lo = mi+1;
1469             }
1470              
1471 0           return -((int)lo)-1;
1472             }
1473              
1474 3           static int pack_entry_find_offset(
1475             off64_t *offset_out,
1476             git_oid *found_oid,
1477             struct git_pack_file *p,
1478             const git_oid *short_oid,
1479             size_t len)
1480             {
1481             const uint32_t *level1_ofs;
1482             const unsigned char *index;
1483             unsigned hi, lo, stride;
1484 3           int pos, found = 0;
1485             off64_t offset;
1486 3           const unsigned char *current = 0;
1487 3           int error = 0;
1488              
1489 3           *offset_out = 0;
1490              
1491 3 50         if (git_mutex_lock(&p->lock) < 0)
1492 0           return packfile_error("failed to get lock for pack_entry_find_offset");
1493              
1494 3 50         if ((error = pack_index_open_locked(p)) < 0)
1495 0           goto cleanup;
1496              
1497 3 50         if (!p->index_map.data) {
1498 0           git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
1499 0           goto cleanup;
1500             }
1501              
1502 3           index = p->index_map.data;
1503 3           level1_ofs = p->index_map.data;
1504              
1505 3 50         if (p->index_version > 1) {
1506 3           level1_ofs += 2;
1507 3           index += 8;
1508             }
1509              
1510 3           index += 4 * 256;
1511 3           hi = ntohl(level1_ofs[(int)short_oid->id[0]]);
1512 3 50         lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
1513              
1514 3 50         if (p->index_version > 1) {
1515 3           stride = 20;
1516             } else {
1517 0           stride = 24;
1518 0           index += 4;
1519             }
1520              
1521             #ifdef INDEX_DEBUG_LOOKUP
1522             printf("%02x%02x%02x... lo %u hi %u nr %d\n",
1523             short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
1524             #endif
1525              
1526 3           pos = git_pack__lookup_sha1(index, stride, lo, hi, short_oid->id);
1527              
1528 3 50         if (pos >= 0) {
1529             /* An object matching exactly the oid was found */
1530 3           found = 1;
1531 3           current = index + pos * stride;
1532             } else {
1533             /* No object was found */
1534             /* pos refers to the object with the "closest" oid to short_oid */
1535 0           pos = - 1 - pos;
1536 0 0         if (pos < (int)p->num_objects) {
1537 0           current = index + pos * stride;
1538              
1539 0 0         if (!git_oid_raw_ncmp(short_oid->id, current, len))
1540 0           found = 1;
1541             }
1542             }
1543              
1544 3 50         if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)p->num_objects) {
    50          
    0          
1545             /* Check for ambiguousity */
1546 0           const unsigned char *next = current + stride;
1547              
1548 0 0         if (!git_oid_raw_ncmp(short_oid->id, next, len)) {
1549 0           found = 2;
1550             }
1551             }
1552              
1553 3 50         if (!found) {
1554 0           error = git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
1555 0           goto cleanup;
1556             }
1557 3 50         if (found > 1) {
1558 0           error = git_odb__error_ambiguous("found multiple offsets for pack entry");
1559 0           goto cleanup;
1560             }
1561              
1562 3 50         if ((offset = nth_packed_object_offset_locked(p, pos)) < 0) {
1563 0           git_error_set(GIT_ERROR_ODB, "packfile index is corrupt");
1564 0           error = -1;
1565 0           goto cleanup;
1566             }
1567              
1568 3           *offset_out = offset;
1569 3           git_oid_fromraw(found_oid, current);
1570              
1571             #ifdef INDEX_DEBUG_LOOKUP
1572             {
1573             unsigned char hex_sha1[GIT_OID_HEXSZ + 1];
1574             git_oid_fmt(hex_sha1, found_oid);
1575             hex_sha1[GIT_OID_HEXSZ] = '\0';
1576             printf("found lo=%d %s\n", lo, hex_sha1);
1577             }
1578             #endif
1579              
1580             cleanup:
1581 3           git_mutex_unlock(&p->lock);
1582 3           return error;
1583             }
1584              
1585 3           int git_pack_entry_find(
1586             struct git_pack_entry *e,
1587             struct git_pack_file *p,
1588             const git_oid *short_oid,
1589             size_t len)
1590             {
1591             off64_t offset;
1592             git_oid found_oid;
1593             int error;
1594              
1595 3 50         GIT_ASSERT_ARG(p);
1596              
1597 3 50         if (len == GIT_OID_HEXSZ && p->num_bad_objects) {
    50          
1598             unsigned i;
1599 0 0         for (i = 0; i < p->num_bad_objects; i++)
1600 0 0         if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0)
1601 0           return packfile_error("bad object found in packfile");
1602             }
1603              
1604 3           error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
1605 3 50         if (error < 0)
1606 0           return error;
1607              
1608 3           error = git_mutex_lock(&p->lock);
1609 3 50         if (error < 0) {
1610 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
1611 0           return error;
1612             }
1613 3           error = git_mutex_lock(&p->mwf.lock);
1614 3 50         if (error < 0) {
1615 0           git_mutex_unlock(&p->lock);
1616 0           git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
1617 0           return error;
1618             }
1619              
1620             /* we found a unique entry in the index;
1621             * make sure the packfile backing the index
1622             * still exists on disk */
1623 3 50         if (p->mwf.fd == -1)
1624 3           error = packfile_open_locked(p);
1625 3           git_mutex_unlock(&p->mwf.lock);
1626 3           git_mutex_unlock(&p->lock);
1627 3 50         if (error < 0)
1628 0           return error;
1629              
1630 3           e->offset = offset;
1631 3           e->p = p;
1632              
1633 3           git_oid_cpy(&e->sha1, &found_oid);
1634 3           return 0;
1635             }