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 behing 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_buf *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 
packfile_byname_search_cmp(const void * path_,const void * p_)196 static int packfile_byname_search_cmp(const void *path_, const void *p_)
197 {
198 	const git_buf *path = (const git_buf *)path_;
199 	const struct git_pack_file *p = (const struct git_pack_file *)p_;
200 
201 	return strncmp(p->pack_name, git_buf_cstr(path), git_buf_len(path));
202 }
203 
packfile_sort__cb(const void * a_,const void * b_)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 
packfile_load__cb(void * data,git_buf * path)234 static int packfile_load__cb(void *data, git_buf *path)
235 {
236 	struct pack_backend *backend = data;
237 	struct git_pack_file *pack;
238 	const char *path_str = git_buf_cstr(path);
239 	git_buf index_prefix = GIT_BUF_INIT;
240 	size_t cmp_len = git_buf_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_buf_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 
pack_entry_find(struct git_pack_entry * e,struct pack_backend * backend,const git_oid * oid)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 
pack_entry_find_prefix(struct git_pack_entry * e,struct pack_backend * backend,const git_oid * short_oid,size_t len)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  */
remove_multi_pack_index(struct pack_backend * backend)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  */
process_multi_pack_index_pack(struct pack_backend * backend,size_t i,const char * packfile_name)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_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT;
408 
409 	error = git_buf_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_buf_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0)
415 		return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
416 
417 	git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), git_buf_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_buf_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_buf_cstr(&pack_path));
429 	git_buf_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  */
refresh_multi_pack_index(struct pack_backend * backend)442 static int refresh_multi_pack_index(struct pack_backend *backend)
443 {
444 	int error;
445 	git_buf midx_path = GIT_BUF_INIT;
446 	const char *packfile_name;
447 	size_t i;
448 
449 	error = git_buf_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_buf_cstr(&midx_path))) {
461 			git_buf_dispose(&midx_path);
462 			return 0;
463 		}
464 		error = remove_multi_pack_index(backend);
465 		if (error < 0) {
466 			git_buf_dispose(&midx_path);
467 			return error;
468 		}
469 	}
470 
471 	error = git_midx_open(&backend->midx, git_buf_cstr(&midx_path));
472 	git_buf_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  ***********************************************************/
pack_backend__refresh(git_odb_backend * backend_)504 static int pack_backend__refresh(git_odb_backend *backend_)
505 {
506 	int error;
507 	struct stat st;
508 	git_buf path = GIT_BUF_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_buf_sets(&path, backend->pack_folder);
527 	error = git_path_direach(&path, 0, packfile_load__cb, backend);
528 
529 	git_buf_dispose(&path);
530 	git_vector_sort(&backend->packs);
531 
532 	return error;
533 }
534 
pack_backend__read_header(size_t * len_p,git_object_t * type_p,struct git_odb_backend * backend,const git_oid * oid)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 
pack_backend__freshen(git_odb_backend * backend,const git_oid * oid)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 
pack_backend__read(void ** buffer_p,size_t * len_p,git_object_t * type_p,git_odb_backend * backend,const git_oid * oid)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 
pack_backend__read_prefix(git_oid * out_oid,void ** buffer_p,size_t * len_p,git_object_t * type_p,git_odb_backend * backend,const git_oid * short_oid,size_t len)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 
pack_backend__exists(git_odb_backend * backend,const git_oid * oid)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 
pack_backend__exists_prefix(git_oid * out,git_odb_backend * backend,const git_oid * short_id,size_t len)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 
pack_backend__foreach(git_odb_backend * _backend,git_odb_foreach_cb cb,void * data)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 
pack_backend__writepack_append(struct git_odb_writepack * _writepack,const void * data,size_t size,git_indexer_progress * stats)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 
pack_backend__writepack_commit(struct git_odb_writepack * _writepack,git_indexer_progress * stats)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 
pack_backend__writepack_free(struct git_odb_writepack * _writepack)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 
pack_backend__writepack(struct git_odb_writepack ** out,git_odb_backend * _backend,git_odb * odb,git_indexer_progress_cb progress_cb,void * progress_payload)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 
get_idx_path(git_buf * idx_path,struct pack_backend * backend,struct git_pack_file * p)745 static int get_idx_path(
746 		git_buf *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_path_prettify(idx_path, p->pack_name, backend->pack_folder);
754 	if (error < 0)
755 		return error;
756 	path_len = git_buf_len(idx_path);
757 	if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_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_buf_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
761 	if (error < 0)
762 		return error;
763 
764 	return 0;
765 }
766 
pack_backend__writemidx(git_odb_backend * _backend)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_buf idx_path = GIT_BUF_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_buf_cstr(&idx_path));
789 		git_buf_dispose(&idx_path);
790 		if (error < 0)
791 			goto cleanup;
792 	}
793 	git_vector_foreach(&backend->packs, i, p) {
794 		git_buf idx_path = GIT_BUF_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_buf_cstr(&idx_path));
799 		git_buf_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 
pack_backend__free(git_odb_backend * _backend)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 
pack_backend__alloc(struct pack_backend ** out,size_t initial_size)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 
git_odb_backend_one_pack(git_odb_backend ** backend_out,const char * idx)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 
git_odb_backend_pack(git_odb_backend ** backend_out,const char * objects_dir)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_buf path = GIT_BUF_INIT;
900 
901 	if (pack_backend__alloc(&backend, 8) < 0)
902 		return -1;
903 
904 	if (!(error = git_buf_joinpath(&path, objects_dir, "pack")) &&
905 		git_path_isdir(git_buf_cstr(&path)))
906 	{
907 		backend->pack_folder = git_buf_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_buf_dispose(&path);
919 
920 	return error;
921 }
922