]> git.proxmox.com Git - libgit2.git/blob - src/odb_pack.c
818cc6125d576b91e07be333db7b75cfbb2042da
[libgit2.git] / src / odb_pack.c
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 <zlib.h>
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 * | <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
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 <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
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 * <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
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 static int packfile_byname_search_cmp(const void *path_, const void *p_)
197 {
198 const git_str *path = (const git_str *)path_;
199 const struct git_pack_file *p = (const struct git_pack_file *)p_;
200
201 return strncmp(p->pack_name, git_str_cstr(path), git_str_len(path));
202 }
203
204 static int packfile_sort__cb(const void *a_, const void *b_)
205 {
206 const struct git_pack_file *a = a_;
207 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 st = a->pack_local - b->pack_local;
217 if (st)
218 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 if (a->mtime < b->mtime)
226 return 1;
227 else if (a->mtime == b->mtime)
228 return 0;
229
230 return -1;
231 }
232
233
234 static int packfile_load__cb(void *data, git_str *path)
235 {
236 struct pack_backend *backend = data;
237 struct git_pack_file *pack;
238 const char *path_str = git_str_cstr(path);
239 git_str index_prefix = GIT_STR_INIT;
240 size_t cmp_len = git_str_len(path);
241 int error;
242
243 if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0)
244 return 0; /* not an index */
245
246 cmp_len -= strlen(".idx");
247 git_str_attach_notowned(&index_prefix, path_str, cmp_len);
248
249 if (git_vector_search2(NULL, &backend->midx_packs, packfile_byname_search_cmp, &index_prefix) == 0)
250 return 0;
251 if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0)
252 return 0;
253
254 error = git_mwindow_get_pack(&pack, path->ptr);
255
256 /* ignore missing .pack file as git does */
257 if (error == GIT_ENOTFOUND) {
258 git_error_clear();
259 return 0;
260 }
261
262 if (!error)
263 error = git_vector_insert(&backend->packs, pack);
264
265 return error;
266
267 }
268
269 static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
270 {
271 struct git_pack_file *last_found = backend->last_found, *p;
272 git_midx_entry midx_entry;
273 size_t i;
274
275 if (backend->midx &&
276 git_midx_entry_find(&midx_entry, backend->midx, oid, GIT_OID_HEXSZ) == 0 &&
277 midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
278 e->offset = midx_entry.offset;
279 git_oid_cpy(&e->sha1, &midx_entry.sha1);
280 e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
281 return 0;
282 }
283
284 if (last_found &&
285 git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
286 return 0;
287
288 git_vector_foreach(&backend->packs, i, p) {
289 if (p == last_found)
290 continue;
291
292 if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
293 backend->last_found = p;
294 return 0;
295 }
296 }
297
298 return git_odb__error_notfound(
299 "failed to find pack entry", oid, GIT_OID_HEXSZ);
300 }
301
302 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 git_oid found_full_oid = {{0}};
311 bool found = false;
312 struct git_pack_file *last_found = backend->last_found, *p;
313 git_midx_entry midx_entry;
314
315 if (backend->midx) {
316 error = git_midx_entry_find(&midx_entry, backend->midx, short_oid, len);
317 if (error == GIT_EAMBIGUOUS)
318 return error;
319 if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
320 e->offset = midx_entry.offset;
321 git_oid_cpy(&e->sha1, &midx_entry.sha1);
322 e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
323 git_oid_cpy(&found_full_oid, &e->sha1);
324 found = true;
325 }
326 }
327
328 if (last_found) {
329 error = git_pack_entry_find(e, last_found, short_oid, len);
330 if (error == GIT_EAMBIGUOUS)
331 return error;
332 if (!error) {
333 if (found && git_oid_cmp(&e->sha1, &found_full_oid))
334 return git_odb__error_ambiguous("found multiple pack entries");
335 git_oid_cpy(&found_full_oid, &e->sha1);
336 found = true;
337 }
338 }
339
340 git_vector_foreach(&backend->packs, i, p) {
341 if (p == last_found)
342 continue;
343
344 error = git_pack_entry_find(e, p, short_oid, len);
345 if (error == GIT_EAMBIGUOUS)
346 return error;
347 if (!error) {
348 if (found && git_oid_cmp(&e->sha1, &found_full_oid))
349 return git_odb__error_ambiguous("found multiple pack entries");
350 git_oid_cpy(&found_full_oid, &e->sha1);
351 found = true;
352 backend->last_found = p;
353 }
354 }
355
356 if (!found)
357 return git_odb__error_notfound("no matching pack entry for prefix",
358 short_oid, len);
359 else
360 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 static int remove_multi_pack_index(struct pack_backend *backend)
375 {
376 size_t i, j = git_vector_length(&backend->packs);
377 struct pack_backend *p;
378 int error = git_vector_size_hint(
379 &backend->packs,
380 j + git_vector_length(&backend->midx_packs));
381 if (error < 0)
382 return error;
383
384 git_vector_foreach(&backend->midx_packs, i, p)
385 git_vector_set(NULL, &backend->packs, j++, p);
386 git_vector_clear(&backend->midx_packs);
387
388 git_midx_free(backend->midx);
389 backend->midx = NULL;
390
391 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 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 git_str pack_path = GIT_STR_INIT, index_prefix = GIT_STR_INIT;
408
409 error = git_str_joinpath(&pack_path, backend->pack_folder, packfile_name);
410 if (error < 0)
411 return error;
412
413 /* This is ensured by midx_parse_packfile_name() */
414 if (git_str_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_str_cstr(&pack_path), ".idx") != 0)
415 return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
416
417 git_str_attach_notowned(&index_prefix, git_str_cstr(&pack_path), git_str_len(&pack_path) - strlen(".idx"));
418
419 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 git_str_dispose(&pack_path);
422 git_vector_set(NULL, &backend->midx_packs, i, git_vector_get(&backend->packs, found_position));
423 git_vector_remove(&backend->packs, found_position);
424 return 0;
425 }
426
427 /* Pack was not found. Allocate a new one. */
428 error = git_mwindow_get_pack(&pack, git_str_cstr(&pack_path));
429 git_str_dispose(&pack_path);
430 if (error < 0)
431 return error;
432
433 git_vector_set(NULL, &backend->midx_packs, i, pack);
434 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 static int refresh_multi_pack_index(struct pack_backend *backend)
443 {
444 int error;
445 git_str midx_path = GIT_STR_INIT;
446 const char *packfile_name;
447 size_t i;
448
449 error = git_str_joinpath(&midx_path, backend->pack_folder, "multi-pack-index");
450 if (error < 0)
451 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 if (backend->midx) {
460 if (!git_midx_needs_refresh(backend->midx, git_str_cstr(&midx_path))) {
461 git_str_dispose(&midx_path);
462 return 0;
463 }
464 error = remove_multi_pack_index(backend);
465 if (error < 0) {
466 git_str_dispose(&midx_path);
467 return error;
468 }
469 }
470
471 error = git_midx_open(&backend->midx, git_str_cstr(&midx_path));
472 git_str_dispose(&midx_path);
473 if (error < 0)
474 return error;
475
476 git_vector_resize_to(&backend->midx_packs, git_vector_length(&backend->midx->packfile_names));
477
478 git_vector_foreach(&backend->midx->packfile_names, i, packfile_name) {
479 error = process_multi_pack_index_pack(backend, i, packfile_name);
480 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 git_vector_resize_to(&backend->midx_packs, i);
489 remove_multi_pack_index(backend);
490 return error;
491 }
492 }
493
494 return 0;
495 }
496
497 /***********************************************************
498 *
499 * PACKED BACKEND PUBLIC API
500 *
501 * Implement the git_odb_backend API calls
502 *
503 ***********************************************************/
504 static int pack_backend__refresh(git_odb_backend *backend_)
505 {
506 int error;
507 struct stat st;
508 git_str path = GIT_STR_INIT;
509 struct pack_backend *backend = (struct pack_backend *)backend_;
510
511 if (backend->pack_folder == NULL)
512 return 0;
513
514 if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
515 return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
516
517 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 git_error_clear();
523 }
524
525 /* reload all packs */
526 git_str_sets(&path, backend->pack_folder);
527 error = git_fs_path_direach(&path, 0, packfile_load__cb, backend);
528
529 git_str_dispose(&path);
530 git_vector_sort(&backend->packs);
531
532 return error;
533 }
534
535 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 GIT_ASSERT_ARG(len_p);
543 GIT_ASSERT_ARG(type_p);
544 GIT_ASSERT_ARG(backend);
545 GIT_ASSERT_ARG(oid);
546
547 if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
548 return error;
549
550 return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
551 }
552
553 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 if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
561 return error;
562
563 now = time(NULL);
564
565 if (e.p->last_freshen > now - FRESHEN_FREQUENCY)
566 return 0;
567
568 if ((error = git_futils_touch(e.p->pack_name, &now)) < 0)
569 return error;
570
571 e.p->last_freshen = now;
572 return 0;
573 }
574
575 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 git_rawobj raw = {NULL};
581 int error;
582
583 if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
584 (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
585 return error;
586
587 *buffer_p = raw.data;
588 *len_p = raw.len;
589 *type_p = raw.type;
590
591 return 0;
592 }
593
594 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 int error = 0;
604
605 if (len < GIT_OID_MINPREFIXLEN)
606 error = git_odb__error_ambiguous("prefix length too short");
607
608 else if (len >= GIT_OID_HEXSZ) {
609 /* We can fall back to regular read method */
610 error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
611 if (!error)
612 git_oid_cpy(out_oid, short_oid);
613 } else {
614 struct git_pack_entry e;
615 git_rawobj raw = {NULL};
616
617 if ((error = pack_entry_find_prefix(
618 &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
619 (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
620 {
621 *buffer_p = raw.data;
622 *len_p = raw.len;
623 *type_p = raw.type;
624 git_oid_cpy(out_oid, &e.sha1);
625 }
626 }
627
628 return error;
629 }
630
631 static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
632 {
633 struct git_pack_entry e;
634 return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
635 }
636
637 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 struct pack_backend *pb = (struct pack_backend *)backend;
642 struct git_pack_entry e = {0};
643
644 error = pack_entry_find_prefix(&e, pb, short_id, len);
645 git_oid_cpy(out, &e.sha1);
646 return error;
647 }
648
649 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 GIT_ASSERT_ARG(_backend);
657 GIT_ASSERT_ARG(cb);
658
659 backend = (struct pack_backend *)_backend;
660
661 /* Make sure we know about the packfiles */
662 if ((error = pack_backend__refresh(_backend)) != 0)
663 return error;
664
665 if (backend->midx && (error = git_midx_foreach_entry(backend->midx, cb, data)) != 0)
666 return error;
667 git_vector_foreach(&backend->packs, i, p) {
668 if ((error = git_pack_foreach_entry(p, cb, data)) != 0)
669 return error;
670 }
671
672 return 0;
673 }
674
675 static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_indexer_progress *stats)
676 {
677 struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
678
679 GIT_ASSERT_ARG(writepack);
680
681 return git_indexer_append(writepack->indexer, data, size, stats);
682 }
683
684 static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_indexer_progress *stats)
685 {
686 struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
687
688 GIT_ASSERT_ARG(writepack);
689
690 return git_indexer_commit(writepack->indexer, stats);
691 }
692
693 static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
694 {
695 struct pack_writepack *writepack;
696
697 if (!_writepack)
698 return;
699
700 writepack = (struct pack_writepack *)_writepack;
701
702 git_indexer_free(writepack->indexer);
703 git__free(writepack);
704 }
705
706 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 git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
713 struct pack_backend *backend;
714 struct pack_writepack *writepack;
715
716 GIT_ASSERT_ARG(out);
717 GIT_ASSERT_ARG(_backend);
718
719 *out = NULL;
720
721 opts.progress_cb = progress_cb;
722 opts.progress_cb_payload = progress_payload;
723
724 backend = (struct pack_backend *)_backend;
725
726 writepack = git__calloc(1, sizeof(struct pack_writepack));
727 GIT_ERROR_CHECK_ALLOC(writepack);
728
729 if (git_indexer_new(&writepack->indexer,
730 backend->pack_folder, 0, odb, &opts) < 0) {
731 git__free(writepack);
732 return -1;
733 }
734
735 writepack->parent.backend = _backend;
736 writepack->parent.append = pack_backend__writepack_append;
737 writepack->parent.commit = pack_backend__writepack_commit;
738 writepack->parent.free = pack_backend__writepack_free;
739
740 *out = (git_odb_writepack *)writepack;
741
742 return 0;
743 }
744
745 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 error = git_fs_path_prettify(idx_path, p->pack_name, backend->pack_folder);
754 if (error < 0)
755 return error;
756 path_len = git_str_len(idx_path);
757 if (path_len <= strlen(".pack") || git__suffixcmp(git_str_cstr(idx_path), ".pack") != 0)
758 return git_odb__error_notfound("packfile does not end in .pack", NULL, 0);
759 path_len -= strlen(".pack");
760 error = git_str_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
761 if (error < 0)
762 return error;
763
764 return 0;
765 }
766
767 static int pack_backend__writemidx(git_odb_backend *_backend)
768 {
769 struct pack_backend *backend;
770 git_midx_writer *w = NULL;
771 struct git_pack_file *p;
772 size_t i;
773 int error = 0;
774
775 GIT_ASSERT_ARG(_backend);
776
777 backend = (struct pack_backend *)_backend;
778
779 error = git_midx_writer_new(&w, backend->pack_folder);
780 if (error < 0)
781 return error;
782
783 git_vector_foreach(&backend->midx_packs, i, p) {
784 git_str idx_path = GIT_STR_INIT;
785 error = get_idx_path(&idx_path, backend, p);
786 if (error < 0)
787 goto cleanup;
788 error = git_midx_writer_add(w, git_str_cstr(&idx_path));
789 git_str_dispose(&idx_path);
790 if (error < 0)
791 goto cleanup;
792 }
793 git_vector_foreach(&backend->packs, i, p) {
794 git_str idx_path = GIT_STR_INIT;
795 error = get_idx_path(&idx_path, backend, p);
796 if (error < 0)
797 goto cleanup;
798 error = git_midx_writer_add(w, git_str_cstr(&idx_path));
799 git_str_dispose(&idx_path);
800 if (error < 0)
801 goto cleanup;
802 }
803
804 /*
805 * Invalidate the previous midx before writing the new one.
806 */
807 error = remove_multi_pack_index(backend);
808 if (error < 0)
809 goto cleanup;
810 error = git_midx_writer_commit(w);
811 if (error < 0)
812 goto cleanup;
813 error = refresh_multi_pack_index(backend);
814
815 cleanup:
816 git_midx_writer_free(w);
817 return error;
818 }
819
820 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 if (!_backend)
827 return;
828
829 backend = (struct pack_backend *)_backend;
830
831 git_vector_foreach(&backend->midx_packs, i, p)
832 git_mwindow_put_pack(p);
833 git_vector_foreach(&backend->packs, i, p)
834 git_mwindow_put_pack(p);
835
836 git_midx_free(backend->midx);
837 git_vector_free(&backend->midx_packs);
838 git_vector_free(&backend->packs);
839 git__free(backend->pack_folder);
840 git__free(backend);
841 }
842
843 static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
844 {
845 struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
846 GIT_ERROR_CHECK_ALLOC(backend);
847
848 if (git_vector_init(&backend->midx_packs, 0, NULL) < 0) {
849 git__free(backend);
850 return -1;
851 }
852 if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
853 git_vector_free(&backend->midx_packs);
854 git__free(backend);
855 return -1;
856 }
857
858 backend->parent.version = GIT_ODB_BACKEND_VERSION;
859
860 backend->parent.read = &pack_backend__read;
861 backend->parent.read_prefix = &pack_backend__read_prefix;
862 backend->parent.read_header = &pack_backend__read_header;
863 backend->parent.exists = &pack_backend__exists;
864 backend->parent.exists_prefix = &pack_backend__exists_prefix;
865 backend->parent.refresh = &pack_backend__refresh;
866 backend->parent.foreach = &pack_backend__foreach;
867 backend->parent.writepack = &pack_backend__writepack;
868 backend->parent.writemidx = &pack_backend__writemidx;
869 backend->parent.freshen = &pack_backend__freshen;
870 backend->parent.free = &pack_backend__free;
871
872 *out = backend;
873 return 0;
874 }
875
876 int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
877 {
878 struct pack_backend *backend = NULL;
879 struct git_pack_file *packfile = NULL;
880
881 if (pack_backend__alloc(&backend, 1) < 0)
882 return -1;
883
884 if (git_mwindow_get_pack(&packfile, idx) < 0 ||
885 git_vector_insert(&backend->packs, packfile) < 0)
886 {
887 pack_backend__free((git_odb_backend *)backend);
888 return -1;
889 }
890
891 *backend_out = (git_odb_backend *)backend;
892 return 0;
893 }
894
895 int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
896 {
897 int error = 0;
898 struct pack_backend *backend = NULL;
899 git_str path = GIT_STR_INIT;
900
901 if (pack_backend__alloc(&backend, 8) < 0)
902 return -1;
903
904 if (!(error = git_str_joinpath(&path, objects_dir, "pack")) &&
905 git_fs_path_isdir(git_str_cstr(&path)))
906 {
907 backend->pack_folder = git_str_detach(&path);
908 error = pack_backend__refresh((git_odb_backend *)backend);
909 }
910
911 if (error < 0) {
912 pack_backend__free((git_odb_backend *)backend);
913 backend = NULL;
914 }
915
916 *backend_out = (git_odb_backend *)backend;
917
918 git_str_dispose(&path);
919
920 return error;
921 }