File Coverage

deps/libgit2/src/libgit2/odb_pack.c
Criterion Covered Total %
statement 151 371 40.7
branch 52 198 26.2
condition n/a
subroutine n/a
pod n/a
total 203 569 35.6


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 "common.h"
9              
10             #include
11             #include "git2/repository.h"
12             #include "git2/indexer.h"
13             #include "git2/sys/odb_backend.h"
14             #include "delta.h"
15             #include "futils.h"
16             #include "hash.h"
17             #include "midx.h"
18             #include "mwindow.h"
19             #include "odb.h"
20             #include "pack.h"
21              
22             #include "git2/odb_backend.h"
23              
24             /* re-freshen pack files no more than every 2 seconds */
25             #define FRESHEN_FREQUENCY 2
26              
27             struct pack_backend {
28             git_odb_backend parent;
29             git_midx_file *midx;
30             git_vector midx_packs;
31             git_vector packs;
32             struct git_pack_file *last_found;
33             char *pack_folder;
34             };
35              
36             struct pack_writepack {
37             struct git_odb_writepack parent;
38             git_indexer *indexer;
39             };
40              
41             /**
42             * The wonderful tale of a Packed Object lookup query
43             * ===================================================
44             * A riveting and epic story of epicness and ASCII
45             * art, presented by yours truly,
46             * Sir Vicent of Marti
47             *
48             *
49             * Chapter 1: Once upon a time...
50             * Initialization of the Pack Backend
51             * --------------------------------------------------
52             *
53             * # git_odb_backend_pack
54             * | Creates the pack backend structure, initializes the
55             * | callback pointers to our default read() and exist() methods,
56             * | and tries to find the `pack` folder, if it exists. ODBs without a `pack`
57             * | folder are ignored altogether. If there is a `pack` folder, it tries to
58             * | preload all the known packfiles in the ODB.
59             * |
60             * |-# pack_backend__refresh
61             * | The `multi-pack-index` is loaded if it exists and is valid.
62             * | Then we run a `dirent` callback through every file in the pack folder,
63             * | even those present in `multi-pack-index`. The unindexed packfiles are
64             * | then sorted according to a sorting callback.
65             * |
66             * |-# refresh_multi_pack_index
67             * | Detect the presence of the `multi-pack-index` file. If it needs to be
68             * | refreshed, frees the old copy and tries to load the new one, together
69             * | with all the packfiles it indexes. If the process fails, fall back to
70             * | the old behavior, as if the `multi-pack-index` file was not there.
71             * |
72             * |-# packfile_load__cb
73             * | | This callback is called from `dirent` with every single file
74             * | | inside the pack folder. We find the packs by actually locating
75             * | | their index (ends in ".idx"). From that index, we verify that
76             * | | the corresponding packfile exists and is valid, and if so, we
77             * | | add it to the pack list.
78             * | |
79             * | # git_mwindow_get_pack
80             * | Make sure that there's a packfile to back this index, and store
81             * | some very basic information regarding the packfile itself,
82             * | such as the full path, the size, and the modification time.
83             * | We don't actually open the packfile to check for internal consistency.
84             * |
85             * |-# packfile_sort__cb
86             * Sort all the preloaded packs according to some specific criteria:
87             * we prioritize the "newer" packs because it's more likely they
88             * contain the objects we are looking for, and we prioritize local
89             * packs over remote ones.
90             *
91             *
92             *
93             * Chapter 2: To be, or not to be...
94             * A standard packed `exist` query for an OID
95             * --------------------------------------------------
96             *
97             * # pack_backend__exists / pack_backend__exists_prefix
98             * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
99             * | packs that have been loaded for our ODB.
100             * |
101             * |-# pack_entry_find / pack_entry_find_prefix
102             * | If there is a multi-pack-index present, search the SHA1 oid in that
103             * | index first. If it is not found there, iterate through all the unindexed
104             * | packs that have been preloaded (starting by the pack where the latest
105             * | object was found) to try to find the OID in one of them.
106             * |
107             * |-# git_midx_entry_find
108             * | Search for the SHA1 oid in the multi-pack-index. See
109             * |
110             * | for specifics on the multi-pack-index format and how do we find
111             * | entries in it.
112             * |
113             * |-# git_pack_entry_find
114             * | Check the index of an individual unindexed pack to see if the SHA1
115             * | OID can be found. If we can find the offset to that SHA1 inside of the
116             * | index, that means the object is contained inside of the packfile and
117             * | we can stop searching. Before returning, we verify that the
118             * | packfile behind the index we are searching still exists on disk.
119             * |
120             * |-# pack_entry_find_offset
121             * | Mmap the actual index file to disk if it hasn't been opened
122             * | yet, and run a binary search through it to find the OID.
123             * | See
124             * | for specifics on the Packfile Index format and how do we find
125             * | entries in it.
126             * |
127             * |-# pack_index_open
128             * | Guess the name of the index based on the full path to the
129             * | packfile, open it and verify its contents. Only if the index
130             * | has not been opened already.
131             * |
132             * |-# pack_index_check
133             * Mmap the index file and do a quick run through the header
134             * to guess the index version (right now we support v1 and v2),
135             * and to verify that the size of the index makes sense.
136             *
137             *
138             *
139             * Chapter 3: The neverending story...
140             * A standard packed `lookup` query for an OID
141             * --------------------------------------------------
142             *
143             * # pack_backend__read / pack_backend__read_prefix
144             * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
145             * | packs that have been loaded for our ODB. If it does, open the packfile and
146             * | read from it.
147             * |
148             * |-# git_packfile_unpack
149             * Armed with a packfile and the offset within it, we can finally unpack
150             * the object pointed at by the SHA1 oid. This involves mmapping part of
151             * the `.pack` file, and uncompressing the object within it (if it is
152             * stored in the undelfitied representation), or finding a base object and
153             * applying some deltas to its uncompressed representation (if it is stored
154             * in the deltified representation). See
155             *
156             * for specifics on the Packfile format and how do we read from it.
157             *
158             */
159              
160              
161             /***********************************************************
162             *
163             * FORWARD DECLARATIONS
164             *
165             ***********************************************************/
166              
167             static int packfile_sort__cb(const void *a_, const void *b_);
168              
169             static int packfile_load__cb(void *_data, git_str *path);
170              
171             static int packfile_byname_search_cmp(const void *path, const void *pack_entry);
172              
173             static int pack_entry_find(struct git_pack_entry *e,
174             struct pack_backend *backend, const git_oid *oid);
175              
176             /* Can find the offset of an object given
177             * a prefix of an identifier.
178             * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
179             * This method assumes that len is between
180             * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
181             */
182             static int pack_entry_find_prefix(
183             struct git_pack_entry *e,
184             struct pack_backend *backend,
185             const git_oid *short_oid,
186             size_t len);
187              
188              
189              
190             /***********************************************************
191             *
192             * PACK WINDOW MANAGEMENT
193             *
194             ***********************************************************/
195              
196 0           static int packfile_byname_search_cmp(const void *path_, const void *p_)
197             {
198 0           const git_str *path = (const git_str *)path_;
199 0           const struct git_pack_file *p = (const struct git_pack_file *)p_;
200              
201 0           return strncmp(p->pack_name, git_str_cstr(path), git_str_len(path));
202             }
203              
204 0           static int packfile_sort__cb(const void *a_, const void *b_)
205             {
206 0           const struct git_pack_file *a = a_;
207 0           const struct git_pack_file *b = b_;
208             int st;
209              
210             /*
211             * Local packs tend to contain objects specific to our
212             * variant of the project than remote ones. In addition,
213             * remote ones could be on a network mounted filesystem.
214             * Favor local ones for these reasons.
215             */
216 0           st = a->pack_local - b->pack_local;
217 0 0         if (st)
218 0           return -st;
219              
220             /*
221             * Younger packs tend to contain more recent objects,
222             * and more recent objects tend to get accessed more
223             * often.
224             */
225 0 0         if (a->mtime < b->mtime)
226 0           return 1;
227 0 0         else if (a->mtime == b->mtime)
228 0           return 0;
229              
230 0           return -1;
231             }
232              
233              
234 4           static int packfile_load__cb(void *data, git_str *path)
235             {
236 4           struct pack_backend *backend = data;
237             struct git_pack_file *pack;
238 4           const char *path_str = git_str_cstr(path);
239 4           git_str index_prefix = GIT_STR_INIT;
240 4           size_t cmp_len = git_str_len(path);
241             int error;
242              
243 4 50         if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0)
    100          
244 2           return 0; /* not an index */
245              
246 2           cmp_len -= strlen(".idx");
247 2           git_str_attach_notowned(&index_prefix, path_str, cmp_len);
248              
249 2 50         if (git_vector_search2(NULL, &backend->midx_packs, packfile_byname_search_cmp, &index_prefix) == 0)
250 0           return 0;
251 2 50         if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0)
252 0           return 0;
253              
254 2           error = git_mwindow_get_pack(&pack, path->ptr);
255              
256             /* ignore missing .pack file as git does */
257 2 50         if (error == GIT_ENOTFOUND) {
258 0           git_error_clear();
259 0           return 0;
260             }
261              
262 2 50         if (!error)
263 2           error = git_vector_insert(&backend->packs, pack);
264              
265 4           return error;
266              
267             }
268              
269 1586           static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
270             {
271 1586           struct git_pack_file *last_found = backend->last_found, *p;
272             git_midx_entry midx_entry;
273             size_t i;
274              
275 1586           if (backend->midx &&
276 0 0         git_midx_entry_find(&midx_entry, backend->midx, oid, GIT_OID_HEXSZ) == 0 &&
277 0           midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
278 0           e->offset = midx_entry.offset;
279 0           git_oid_cpy(&e->sha1, &midx_entry.sha1);
280 0           e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
281 0           return 0;
282             }
283              
284 1586           if (last_found &&
285 0           git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
286 0           return 0;
287              
288 1586 100         git_vector_foreach(&backend->packs, i, p) {
289 3 50         if (p == last_found)
290 0           continue;
291              
292 3 50         if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
293 3           backend->last_found = p;
294 3           return 0;
295             }
296             }
297              
298 1586           return git_odb__error_notfound(
299             "failed to find pack entry", oid, GIT_OID_HEXSZ);
300             }
301              
302 24           static int pack_entry_find_prefix(
303             struct git_pack_entry *e,
304             struct pack_backend *backend,
305             const git_oid *short_oid,
306             size_t len)
307             {
308             int error;
309             size_t i;
310 24           git_oid found_full_oid = {{0}};
311 24           bool found = false;
312 24           struct git_pack_file *last_found = backend->last_found, *p;
313             git_midx_entry midx_entry;
314              
315 24 50         if (backend->midx) {
316 0           error = git_midx_entry_find(&midx_entry, backend->midx, short_oid, len);
317 0 0         if (error == GIT_EAMBIGUOUS)
318 0           return error;
319 0 0         if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
    0          
320 0           e->offset = midx_entry.offset;
321 0           git_oid_cpy(&e->sha1, &midx_entry.sha1);
322 0           e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
323 0           git_oid_cpy(&found_full_oid, &e->sha1);
324 0           found = true;
325             }
326             }
327              
328 24 50         if (last_found) {
329 0           error = git_pack_entry_find(e, last_found, short_oid, len);
330 0 0         if (error == GIT_EAMBIGUOUS)
331 0           return error;
332 0 0         if (!error) {
333 0 0         if (found && git_oid_cmp(&e->sha1, &found_full_oid))
    0          
334 0           return git_odb__error_ambiguous("found multiple pack entries");
335 0           git_oid_cpy(&found_full_oid, &e->sha1);
336 0           found = true;
337             }
338             }
339              
340 24 50         git_vector_foreach(&backend->packs, i, p) {
341 0 0         if (p == last_found)
342 0           continue;
343              
344 0           error = git_pack_entry_find(e, p, short_oid, len);
345 0 0         if (error == GIT_EAMBIGUOUS)
346 0           return error;
347 0 0         if (!error) {
348 0 0         if (found && git_oid_cmp(&e->sha1, &found_full_oid))
    0          
349 0           return git_odb__error_ambiguous("found multiple pack entries");
350 0           git_oid_cpy(&found_full_oid, &e->sha1);
351 0           found = true;
352 0           backend->last_found = p;
353             }
354             }
355              
356 24 50         if (!found)
357 24           return git_odb__error_notfound("no matching pack entry for prefix",
358             short_oid, len);
359             else
360 24           return 0;
361             }
362              
363             /***********************************************************
364             *
365             * MULTI-PACK-INDEX SUPPORT
366             *
367             * Functions needed to support the multi-pack-index.
368             *
369             ***********************************************************/
370              
371             /*
372             * Remove the multi-pack-index, and move all midx_packs to packs.
373             */
374 0           static int remove_multi_pack_index(struct pack_backend *backend)
375             {
376 0           size_t i, j = git_vector_length(&backend->packs);
377             struct pack_backend *p;
378 0           int error = git_vector_size_hint(
379             &backend->packs,
380 0           j + git_vector_length(&backend->midx_packs));
381 0 0         if (error < 0)
382 0           return error;
383              
384 0 0         git_vector_foreach(&backend->midx_packs, i, p)
385 0           git_vector_set(NULL, &backend->packs, j++, p);
386 0           git_vector_clear(&backend->midx_packs);
387              
388 0           git_midx_free(backend->midx);
389 0           backend->midx = NULL;
390              
391 0           return 0;
392             }
393              
394             /*
395             * Loads a single .pack file referred to by the multi-pack-index. These must
396             * match the order in which they are declared in the multi-pack-index file,
397             * since these files are referred to by their index.
398             */
399 0           static int process_multi_pack_index_pack(
400             struct pack_backend *backend,
401             size_t i,
402             const char *packfile_name)
403             {
404             int error;
405             struct git_pack_file *pack;
406             size_t found_position;
407 0           git_str pack_path = GIT_STR_INIT, index_prefix = GIT_STR_INIT;
408              
409 0           error = git_str_joinpath(&pack_path, backend->pack_folder, packfile_name);
410 0 0         if (error < 0)
411 0           return error;
412              
413             /* This is ensured by midx_parse_packfile_name() */
414 0 0         if (git_str_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_str_cstr(&pack_path), ".idx") != 0)
    0          
415 0           return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
416              
417 0           git_str_attach_notowned(&index_prefix, git_str_cstr(&pack_path), git_str_len(&pack_path) - strlen(".idx"));
418              
419 0 0         if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) {
420             /* Pack was found in the packs list. Moving it to the midx_packs list. */
421 0           git_str_dispose(&pack_path);
422 0           git_vector_set(NULL, &backend->midx_packs, i, git_vector_get(&backend->packs, found_position));
423 0           git_vector_remove(&backend->packs, found_position);
424 0           return 0;
425             }
426              
427             /* Pack was not found. Allocate a new one. */
428 0           error = git_mwindow_get_pack(&pack, git_str_cstr(&pack_path));
429 0           git_str_dispose(&pack_path);
430 0 0         if (error < 0)
431 0           return error;
432              
433 0           git_vector_set(NULL, &backend->midx_packs, i, pack);
434 0           return 0;
435             }
436              
437             /*
438             * Reads the multi-pack-index. If this fails for whatever reason, the
439             * multi-pack-index object is freed, and all the packfiles that are related to
440             * it are moved to the unindexed packfiles vector.
441             */
442 213           static int refresh_multi_pack_index(struct pack_backend *backend)
443             {
444             int error;
445 213           git_str midx_path = GIT_STR_INIT;
446             const char *packfile_name;
447             size_t i;
448              
449 213           error = git_str_joinpath(&midx_path, backend->pack_folder, "multi-pack-index");
450 213 50         if (error < 0)
451 0           return error;
452              
453             /*
454             * Check whether the multi-pack-index has changed. If it has, close any
455             * old multi-pack-index and move all the packfiles to the unindexed
456             * packs. This is done to prevent losing any open packfiles in case
457             * refreshing the new multi-pack-index fails, or the file is deleted.
458             */
459 213 50         if (backend->midx) {
460 0 0         if (!git_midx_needs_refresh(backend->midx, git_str_cstr(&midx_path))) {
461 0           git_str_dispose(&midx_path);
462 0           return 0;
463             }
464 0           error = remove_multi_pack_index(backend);
465 0 0         if (error < 0) {
466 0           git_str_dispose(&midx_path);
467 0           return error;
468             }
469             }
470              
471 213           error = git_midx_open(&backend->midx, git_str_cstr(&midx_path));
472 213           git_str_dispose(&midx_path);
473 213 50         if (error < 0)
474 213           return error;
475              
476 0           git_vector_resize_to(&backend->midx_packs, git_vector_length(&backend->midx->packfile_names));
477              
478 0 0         git_vector_foreach(&backend->midx->packfile_names, i, packfile_name) {
479 0           error = process_multi_pack_index_pack(backend, i, packfile_name);
480 0 0         if (error < 0) {
481             /*
482             * Something failed during reading multi-pack-index.
483             * Restore the state of backend as if the
484             * multi-pack-index was never there, and move all
485             * packfiles that have been processed so far to the
486             * unindexed packs.
487             */
488 0           git_vector_resize_to(&backend->midx_packs, i);
489 0           remove_multi_pack_index(backend);
490 0           return error;
491             }
492             }
493              
494 213           return 0;
495             }
496              
497             /***********************************************************
498             *
499             * PACKED BACKEND PUBLIC API
500             *
501             * Implement the git_odb_backend API calls
502             *
503             ***********************************************************/
504 213           static int pack_backend__refresh(git_odb_backend *backend_)
505             {
506             int error;
507             struct stat st;
508 213           git_str path = GIT_STR_INIT;
509 213           struct pack_backend *backend = (struct pack_backend *)backend_;
510              
511 213 50         if (backend->pack_folder == NULL)
512 0           return 0;
513              
514 213 50         if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
    50          
515 0           return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
516              
517 213 50         if (refresh_multi_pack_index(backend) < 0) {
518             /*
519             * It is okay if this fails. We will just not use the
520             * multi-pack-index in this case.
521             */
522 213           git_error_clear();
523             }
524              
525             /* reload all packs */
526 213           git_str_sets(&path, backend->pack_folder);
527 213           error = git_fs_path_direach(&path, 0, packfile_load__cb, backend);
528              
529 213           git_str_dispose(&path);
530 213           git_vector_sort(&backend->packs);
531              
532 213           return error;
533             }
534              
535 373           static int pack_backend__read_header(
536             size_t *len_p, git_object_t *type_p,
537             struct git_odb_backend *backend, const git_oid *oid)
538             {
539             struct git_pack_entry e;
540             int error;
541              
542 373 50         GIT_ASSERT_ARG(len_p);
543 373 50         GIT_ASSERT_ARG(type_p);
544 373 50         GIT_ASSERT_ARG(backend);
545 373 50         GIT_ASSERT_ARG(oid);
546              
547 373 100         if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
548 371           return error;
549              
550 373           return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
551             }
552              
553 452           static int pack_backend__freshen(
554             git_odb_backend *backend, const git_oid *oid)
555             {
556             struct git_pack_entry e;
557             time_t now;
558             int error;
559              
560 452 50         if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
561 452           return error;
562              
563 0           now = time(NULL);
564              
565 0 0         if (e.p->last_freshen > now - FRESHEN_FREQUENCY)
566 0           return 0;
567              
568 0 0         if ((error = git_futils_touch(e.p->pack_name, &now)) < 0)
569 0           return error;
570              
571 0           e.p->last_freshen = now;
572 452           return 0;
573             }
574              
575 757           static int pack_backend__read(
576             void **buffer_p, size_t *len_p, git_object_t *type_p,
577             git_odb_backend *backend, const git_oid *oid)
578             {
579             struct git_pack_entry e;
580 757           git_rawobj raw = {NULL};
581             int error;
582              
583 757 100         if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
    50          
584 1           (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
585 756           return error;
586              
587 1           *buffer_p = raw.data;
588 1           *len_p = raw.len;
589 1           *type_p = raw.type;
590              
591 757           return 0;
592             }
593              
594 146           static int pack_backend__read_prefix(
595             git_oid *out_oid,
596             void **buffer_p,
597             size_t *len_p,
598             git_object_t *type_p,
599             git_odb_backend *backend,
600             const git_oid *short_oid,
601             size_t len)
602             {
603 146           int error = 0;
604              
605 146 50         if (len < GIT_OID_MINPREFIXLEN)
606 0           error = git_odb__error_ambiguous("prefix length too short");
607              
608 146 100         else if (len >= GIT_OID_HEXSZ) {
609             /* We can fall back to regular read method */
610 122           error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
611 122 50         if (!error)
612 122           git_oid_cpy(out_oid, short_oid);
613             } else {
614             struct git_pack_entry e;
615 24           git_rawobj raw = {NULL};
616              
617 24 50         if ((error = pack_entry_find_prefix(
618 0 0         &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
619 0           (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
620             {
621 0           *buffer_p = raw.data;
622 0           *len_p = raw.len;
623 0           *type_p = raw.type;
624 24           git_oid_cpy(out_oid, &e.sha1);
625             }
626             }
627              
628 146           return error;
629             }
630              
631 4           static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
632             {
633             struct git_pack_entry e;
634 4           return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
635             }
636              
637 0           static int pack_backend__exists_prefix(
638             git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
639             {
640             int error;
641 0           struct pack_backend *pb = (struct pack_backend *)backend;
642 0           struct git_pack_entry e = {0};
643              
644 0           error = pack_entry_find_prefix(&e, pb, short_id, len);
645 0           git_oid_cpy(out, &e.sha1);
646 0           return error;
647             }
648              
649 1           static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
650             {
651             int error;
652             struct git_pack_file *p;
653             struct pack_backend *backend;
654             unsigned int i;
655              
656 1 50         GIT_ASSERT_ARG(_backend);
657 1 50         GIT_ASSERT_ARG(cb);
658              
659 1           backend = (struct pack_backend *)_backend;
660              
661             /* Make sure we know about the packfiles */
662 1 50         if ((error = pack_backend__refresh(_backend)) != 0)
663 0           return error;
664              
665 1 50         if (backend->midx && (error = git_midx_foreach_entry(backend->midx, cb, data)) != 0)
    0          
666 0           return error;
667 1 50         git_vector_foreach(&backend->packs, i, p) {
668 0 0         if ((error = git_pack_foreach_entry(p, cb, data)) != 0)
669 0           return error;
670             }
671              
672 1           return 0;
673             }
674              
675 0           static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_indexer_progress *stats)
676             {
677 0           struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
678              
679 0 0         GIT_ASSERT_ARG(writepack);
680              
681 0           return git_indexer_append(writepack->indexer, data, size, stats);
682             }
683              
684 0           static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_indexer_progress *stats)
685             {
686 0           struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
687              
688 0 0         GIT_ASSERT_ARG(writepack);
689              
690 0           return git_indexer_commit(writepack->indexer, stats);
691             }
692              
693 0           static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
694             {
695             struct pack_writepack *writepack;
696              
697 0 0         if (!_writepack)
698 0           return;
699              
700 0           writepack = (struct pack_writepack *)_writepack;
701              
702 0           git_indexer_free(writepack->indexer);
703 0           git__free(writepack);
704             }
705              
706 0           static int pack_backend__writepack(struct git_odb_writepack **out,
707             git_odb_backend *_backend,
708             git_odb *odb,
709             git_indexer_progress_cb progress_cb,
710             void *progress_payload)
711             {
712 0           git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
713             struct pack_backend *backend;
714             struct pack_writepack *writepack;
715              
716 0 0         GIT_ASSERT_ARG(out);
717 0 0         GIT_ASSERT_ARG(_backend);
718              
719 0           *out = NULL;
720              
721 0           opts.progress_cb = progress_cb;
722 0           opts.progress_cb_payload = progress_payload;
723              
724 0           backend = (struct pack_backend *)_backend;
725              
726 0           writepack = git__calloc(1, sizeof(struct pack_writepack));
727 0 0         GIT_ERROR_CHECK_ALLOC(writepack);
728              
729 0 0         if (git_indexer_new(&writepack->indexer,
730 0           backend->pack_folder, 0, odb, &opts) < 0) {
731 0           git__free(writepack);
732 0           return -1;
733             }
734              
735 0           writepack->parent.backend = _backend;
736 0           writepack->parent.append = pack_backend__writepack_append;
737 0           writepack->parent.commit = pack_backend__writepack_commit;
738 0           writepack->parent.free = pack_backend__writepack_free;
739              
740 0           *out = (git_odb_writepack *)writepack;
741              
742 0           return 0;
743             }
744              
745 0           static int get_idx_path(
746             git_str *idx_path,
747             struct pack_backend *backend,
748             struct git_pack_file *p)
749             {
750             size_t path_len;
751             int error;
752              
753 0           error = git_fs_path_prettify(idx_path, p->pack_name, backend->pack_folder);
754 0 0         if (error < 0)
755 0           return error;
756 0           path_len = git_str_len(idx_path);
757 0 0         if (path_len <= strlen(".pack") || git__suffixcmp(git_str_cstr(idx_path), ".pack") != 0)
    0          
758 0           return git_odb__error_notfound("packfile does not end in .pack", NULL, 0);
759 0           path_len -= strlen(".pack");
760 0           error = git_str_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
761 0 0         if (error < 0)
762 0           return error;
763              
764 0           return 0;
765             }
766              
767 0           static int pack_backend__writemidx(git_odb_backend *_backend)
768             {
769             struct pack_backend *backend;
770 0           git_midx_writer *w = NULL;
771             struct git_pack_file *p;
772             size_t i;
773 0           int error = 0;
774              
775 0 0         GIT_ASSERT_ARG(_backend);
776              
777 0           backend = (struct pack_backend *)_backend;
778              
779 0           error = git_midx_writer_new(&w, backend->pack_folder);
780 0 0         if (error < 0)
781 0           return error;
782              
783 0 0         git_vector_foreach(&backend->midx_packs, i, p) {
784 0           git_str idx_path = GIT_STR_INIT;
785 0           error = get_idx_path(&idx_path, backend, p);
786 0 0         if (error < 0)
787 0           goto cleanup;
788 0           error = git_midx_writer_add(w, git_str_cstr(&idx_path));
789 0           git_str_dispose(&idx_path);
790 0 0         if (error < 0)
791 0           goto cleanup;
792             }
793 0 0         git_vector_foreach(&backend->packs, i, p) {
794 0           git_str idx_path = GIT_STR_INIT;
795 0           error = get_idx_path(&idx_path, backend, p);
796 0 0         if (error < 0)
797 0           goto cleanup;
798 0           error = git_midx_writer_add(w, git_str_cstr(&idx_path));
799 0           git_str_dispose(&idx_path);
800 0 0         if (error < 0)
801 0           goto cleanup;
802             }
803              
804             /*
805             * Invalidate the previous midx before writing the new one.
806             */
807 0           error = remove_multi_pack_index(backend);
808 0 0         if (error < 0)
809 0           goto cleanup;
810 0           error = git_midx_writer_commit(w);
811 0 0         if (error < 0)
812 0           goto cleanup;
813 0           error = refresh_multi_pack_index(backend);
814              
815             cleanup:
816 0           git_midx_writer_free(w);
817 0           return error;
818             }
819              
820 37           static void pack_backend__free(git_odb_backend *_backend)
821             {
822             struct pack_backend *backend;
823             struct git_pack_file *p;
824             size_t i;
825              
826 37 50         if (!_backend)
827 0           return;
828              
829 37           backend = (struct pack_backend *)_backend;
830              
831 37 50         git_vector_foreach(&backend->midx_packs, i, p)
832 0           git_mwindow_put_pack(p);
833 40 100         git_vector_foreach(&backend->packs, i, p)
834 3           git_mwindow_put_pack(p);
835              
836 37           git_midx_free(backend->midx);
837 37           git_vector_free(&backend->midx_packs);
838 37           git_vector_free(&backend->packs);
839 37           git__free(backend->pack_folder);
840 37           git__free(backend);
841             }
842              
843 38           static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
844             {
845 38           struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
846 38 50         GIT_ERROR_CHECK_ALLOC(backend);
847              
848 38 50         if (git_vector_init(&backend->midx_packs, 0, NULL) < 0) {
849 0           git__free(backend);
850 0           return -1;
851             }
852 38 50         if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
853 0           git_vector_free(&backend->midx_packs);
854 0           git__free(backend);
855 0           return -1;
856             }
857              
858 38           backend->parent.version = GIT_ODB_BACKEND_VERSION;
859              
860 38           backend->parent.read = &pack_backend__read;
861 38           backend->parent.read_prefix = &pack_backend__read_prefix;
862 38           backend->parent.read_header = &pack_backend__read_header;
863 38           backend->parent.exists = &pack_backend__exists;
864 38           backend->parent.exists_prefix = &pack_backend__exists_prefix;
865 38           backend->parent.refresh = &pack_backend__refresh;
866 38           backend->parent.foreach = &pack_backend__foreach;
867 38           backend->parent.writepack = &pack_backend__writepack;
868 38           backend->parent.writemidx = &pack_backend__writemidx;
869 38           backend->parent.freshen = &pack_backend__freshen;
870 38           backend->parent.free = &pack_backend__free;
871              
872 38           *out = backend;
873 38           return 0;
874             }
875              
876 2           int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
877             {
878 2           struct pack_backend *backend = NULL;
879 2           struct git_pack_file *packfile = NULL;
880              
881 2 50         if (pack_backend__alloc(&backend, 1) < 0)
882 0           return -1;
883              
884 4           if (git_mwindow_get_pack(&packfile, idx) < 0 ||
885 2           git_vector_insert(&backend->packs, packfile) < 0)
886             {
887 0           pack_backend__free((git_odb_backend *)backend);
888 0           return -1;
889             }
890              
891 2           *backend_out = (git_odb_backend *)backend;
892 2           return 0;
893             }
894              
895 36           int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
896             {
897 36           int error = 0;
898 36           struct pack_backend *backend = NULL;
899 36           git_str path = GIT_STR_INIT;
900              
901 36 50         if (pack_backend__alloc(&backend, 8) < 0)
902 0           return -1;
903              
904 72           if (!(error = git_str_joinpath(&path, objects_dir, "pack")) &&
905 36           git_fs_path_isdir(git_str_cstr(&path)))
906             {
907 36           backend->pack_folder = git_str_detach(&path);
908 36           error = pack_backend__refresh((git_odb_backend *)backend);
909             }
910              
911 36 50         if (error < 0) {
912 0           pack_backend__free((git_odb_backend *)backend);
913 0           backend = NULL;
914             }
915              
916 36           *backend_out = (git_odb_backend *)backend;
917              
918 36           git_str_dispose(&path);
919              
920 36           return error;
921             }