Blame src/odb_pack.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include <zlib.h>
Packit Service 20376f
#include "git2/repository.h"
Packit Service 20376f
#include "git2/indexer.h"
Packit Service 20376f
#include "git2/sys/odb_backend.h"
Packit Service 20376f
#include "fileops.h"
Packit Service 20376f
#include "hash.h"
Packit Service 20376f
#include "odb.h"
Packit Service 20376f
#include "delta.h"
Packit Service 20376f
#include "sha1_lookup.h"
Packit Service 20376f
#include "mwindow.h"
Packit Service 20376f
#include "pack.h"
Packit Service 20376f
Packit Service 20376f
#include "git2/odb_backend.h"
Packit Service 20376f
Packit Service 20376f
/* re-freshen pack files no more than every 2 seconds */
Packit Service 20376f
#define FRESHEN_FREQUENCY 2
Packit Service 20376f
Packit Service 20376f
struct pack_backend {
Packit Service 20376f
	git_odb_backend parent;
Packit Service 20376f
	git_vector packs;
Packit Service 20376f
	struct git_pack_file *last_found;
Packit Service 20376f
	char *pack_folder;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
struct pack_writepack {
Packit Service 20376f
	struct git_odb_writepack parent;
Packit Service 20376f
	git_indexer *indexer;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * The wonderful tale of a Packed Object lookup query
Packit Service 20376f
 * ===================================================
Packit Service 20376f
 *	A riveting and epic story of epicness and ASCII
Packit Service 20376f
 *			art, presented by yours truly,
Packit Service 20376f
 *				Sir Vicent of Marti
Packit Service 20376f
 *
Packit Service 20376f
 *
Packit Service 20376f
 *	Chapter 1: Once upon a time...
Packit Service 20376f
 *	Initialization of the Pack Backend
Packit Service 20376f
 *	--------------------------------------------------
Packit Service 20376f
 *
Packit Service 20376f
 *	# git_odb_backend_pack
Packit Service 20376f
 *	| Creates the pack backend structure, initializes the
Packit Service 20376f
 *	| callback pointers to our default read() and exist() methods,
Packit Service 20376f
 *	| and tries to preload all the known packfiles in the ODB.
Packit Service 20376f
 * |
Packit Service 20376f
 *	|-# packfile_load_all
Packit Service 20376f
 *	 | Tries to find the `pack` folder, if it exists. ODBs without
Packit Service 20376f
 *	 | a pack folder are ignored altogether. If there's a `pack` folder
Packit Service 20376f
 *	 | we run a `dirent` callback through every file in the pack folder
Packit Service 20376f
 *	 | to find our packfiles. The packfiles are then sorted according
Packit Service 20376f
 *	 | to a sorting callback.
Packit Service 20376f
 * 	 |
Packit Service 20376f
 *	 |-# packfile_load__cb
Packit Service 20376f
 *	 | | This callback is called from `dirent` with every single file
Packit Service 20376f
 *	 | | inside the pack folder. We find the packs by actually locating
Packit Service 20376f
 *	 | | their index (ends in ".idx"). From that index, we verify that
Packit Service 20376f
 *	 | | the corresponding packfile exists and is valid, and if so, we
Packit Service 20376f
 *	| | add it to the pack list.
Packit Service 20376f
 *	 | |
Packit Service 20376f
 *	 | |-# packfile_check
Packit Service 20376f
 *	 |		Make sure that there's a packfile to back this index, and store
Packit Service 20376f
 *	 |		some very basic information regarding the packfile itself,
Packit Service 20376f
 *	 |		such as the full path, the size, and the modification time.
Packit Service 20376f
 *	 |		We don't actually open the packfile to check for internal consistency.
Packit Service 20376f
 *	|
Packit Service 20376f
 *	|-# packfile_sort__cb
Packit Service 20376f
 *		Sort all the preloaded packs according to some specific criteria:
Packit Service 20376f
 *		we prioritize the "newer" packs because it's more likely they
Packit Service 20376f
 *		contain the objects we are looking for, and we prioritize local
Packit Service 20376f
 *		packs over remote ones.
Packit Service 20376f
 *
Packit Service 20376f
 *
Packit Service 20376f
 *
Packit Service 20376f
 *	Chapter 2: To be, or not to be...
Packit Service 20376f
 *	A standard packed `exist` query for an OID
Packit Service 20376f
 *	--------------------------------------------------
Packit Service 20376f
 *
Packit Service 20376f
 * # pack_backend__exists
Packit Service 20376f
 * | Check if the given SHA1 oid exists in any of the packs
Packit Service 20376f
 * | that have been loaded for our ODB.
Packit Service 20376f
 * |
Packit Service 20376f
 * |-# pack_entry_find
Packit Service 20376f
 *	| Iterate through all the packs that have been preloaded
Packit Service 20376f
 *	| (starting by the pack where the latest object was found)
Packit Service 20376f
 *	| to try to find the OID in one of them.
Packit Service 20376f
 *	|
Packit Service 20376f
 *	|-# pack_entry_find1
Packit Service 20376f
 *		| Check the index of an individual pack to see if the SHA1
Packit Service 20376f
 *		| OID can be found. If we can find the offset to that SHA1
Packit Service 20376f
 *		| inside of the index, that means the object is contained
Packit Service 20376f
 *		| inside of the packfile and we can stop searching.
Packit Service 20376f
 *		| Before returning, we verify that the packfile behing the
Packit Service 20376f
 *		| index we are searching still exists on disk.
Packit Service 20376f
 *		|
Packit Service 20376f
 *		|-# pack_entry_find_offset
Packit Service 20376f
 *		| | Mmap the actual index file to disk if it hasn't been opened
Packit Service 20376f
 *		| | yet, and run a binary search through it to find the OID.
Packit Service 20376f
 *		| | See <http://book.git-scm.com/7_the_packfile.html> for specifics
Packit Service 20376f
 *		| | on the Packfile Index format and how do we find entries in it.
Packit Service 20376f
 *		| |
Packit Service 20376f
 *		| |-# pack_index_open
Packit Service 20376f
 *		|	| Guess the name of the index based on the full path to the
Packit Service 20376f
 *		|	| packfile, open it and verify its contents. Only if the index
Packit Service 20376f
 *		|	| has not been opened already.
Packit Service 20376f
 *		|	|
Packit Service 20376f
 *		|	|-# pack_index_check
Packit Service 20376f
 *		|		Mmap the index file and do a quick run through the header
Packit Service 20376f
 *		|		to guess the index version (right now we support v1 and v2),
Packit Service 20376f
 *		|		and to verify that the size of the index makes sense.
Packit Service 20376f
 *		|
Packit Service 20376f
 *		|-# packfile_open
Packit Service 20376f
 *			See `packfile_open` in Chapter 3
Packit Service 20376f
 *
Packit Service 20376f
 *
Packit Service 20376f
 *
Packit Service 20376f
 *	Chapter 3: The neverending story...
Packit Service 20376f
 *	A standard packed `lookup` query for an OID
Packit Service 20376f
 *	--------------------------------------------------
Packit Service 20376f
 *	TODO
Packit Service 20376f
 *
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
/***********************************************************
Packit Service 20376f
 *
Packit Service 20376f
 * FORWARD DECLARATIONS
Packit Service 20376f
 *
Packit Service 20376f
 ***********************************************************/
Packit Service 20376f
Packit Service 20376f
static int packfile_sort__cb(const void *a_, const void *b_);
Packit Service 20376f
Packit Service 20376f
static int packfile_load__cb(void *_data, git_buf *path);
Packit Service 20376f
Packit Service 20376f
static int pack_entry_find(struct git_pack_entry *e,
Packit Service 20376f
	struct pack_backend *backend, const git_oid *oid);
Packit Service 20376f
Packit Service 20376f
/* Can find the offset of an object given
Packit Service 20376f
 * a prefix of an identifier.
Packit Service 20376f
 * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
Packit Service 20376f
 * This method assumes that len is between
Packit Service 20376f
 * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
Packit Service 20376f
 */
Packit Service 20376f
static int pack_entry_find_prefix(
Packit Service 20376f
	struct git_pack_entry *e,
Packit Service 20376f
	struct pack_backend *backend,
Packit Service 20376f
	const git_oid *short_oid,
Packit Service 20376f
	size_t len);
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
/***********************************************************
Packit Service 20376f
 *
Packit Service 20376f
 * PACK WINDOW MANAGEMENT
Packit Service 20376f
 *
Packit Service 20376f
 ***********************************************************/
Packit Service 20376f
Packit Service 20376f
static int packfile_sort__cb(const void *a_, const void *b_)
Packit Service 20376f
{
Packit Service 20376f
	const struct git_pack_file *a = a_;
Packit Service 20376f
	const struct git_pack_file *b = b_;
Packit Service 20376f
	int st;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Local packs tend to contain objects specific to our
Packit Service 20376f
	 * variant of the project than remote ones. In addition,
Packit Service 20376f
	 * remote ones could be on a network mounted filesystem.
Packit Service 20376f
	 * Favor local ones for these reasons.
Packit Service 20376f
	 */
Packit Service 20376f
	st = a->pack_local - b->pack_local;
Packit Service 20376f
	if (st)
Packit Service 20376f
		return -st;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Younger packs tend to contain more recent objects,
Packit Service 20376f
	 * and more recent objects tend to get accessed more
Packit Service 20376f
	 * often.
Packit Service 20376f
	 */
Packit Service 20376f
	if (a->mtime < b->mtime)
Packit Service 20376f
		return 1;
Packit Service 20376f
	else if (a->mtime == b->mtime)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
static int packfile_load__cb(void *data, git_buf *path)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_backend *backend = data;
Packit Service 20376f
	struct git_pack_file *pack;
Packit Service 20376f
	const char *path_str = git_buf_cstr(path);
Packit Service 20376f
	size_t i, cmp_len = git_buf_len(path);
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0)
Packit Service 20376f
		return 0; /* not an index */
Packit Service 20376f
Packit Service 20376f
	cmp_len -= strlen(".idx");
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < backend->packs.length; ++i) {
Packit Service 20376f
		struct git_pack_file *p = git_vector_get(&backend->packs, i);
Packit Service 20376f
Packit Service 20376f
		if (strncmp(p->pack_name, path_str, cmp_len) == 0)
Packit Service 20376f
			return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = git_mwindow_get_pack(&pack, path->ptr);
Packit Service 20376f
Packit Service 20376f
	/* ignore missing .pack file as git does */
Packit Service 20376f
	if (error == GIT_ENOTFOUND) {
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!error)
Packit Service 20376f
		error = git_vector_insert(&backend->packs, pack);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_entry_find_inner(
Packit Service 20376f
	struct git_pack_entry *e,
Packit Service 20376f
	struct pack_backend *backend,
Packit Service 20376f
	const git_oid *oid,
Packit Service 20376f
	struct git_pack_file *last_found)
Packit Service 20376f
{
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	if (last_found &&
Packit Service 20376f
		git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < backend->packs.length; ++i) {
Packit Service 20376f
		struct git_pack_file *p;
Packit Service 20376f
Packit Service 20376f
		p = git_vector_get(&backend->packs, i);
Packit Service 20376f
		if (p == last_found)
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
Packit Service 20376f
			backend->last_found = p;
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
Packit Service 20376f
{
Packit Service 20376f
	struct git_pack_file *last_found = backend->last_found;
Packit Service 20376f
Packit Service 20376f
	if (backend->last_found &&
Packit Service 20376f
		git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (!pack_entry_find_inner(e, backend, oid, last_found))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	return git_odb__error_notfound(
Packit Service 20376f
		"failed to find pack entry", oid, GIT_OID_HEXSZ);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_entry_find_prefix(
Packit Service 20376f
	struct git_pack_entry *e,
Packit Service 20376f
	struct pack_backend *backend,
Packit Service 20376f
	const git_oid *short_oid,
Packit Service 20376f
	size_t len)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	git_oid found_full_oid = {{0}};
Packit Service 20376f
	bool found = false;
Packit Service 20376f
	struct git_pack_file *last_found = backend->last_found;
Packit Service 20376f
Packit Service 20376f
	if (last_found) {
Packit Service 20376f
		error = git_pack_entry_find(e, last_found, short_oid, len);
Packit Service 20376f
		if (error == GIT_EAMBIGUOUS)
Packit Service 20376f
			return error;
Packit Service 20376f
		if (!error) {
Packit Service 20376f
			git_oid_cpy(&found_full_oid, &e->sha1);
Packit Service 20376f
			found = true;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < backend->packs.length; ++i) {
Packit Service 20376f
		struct git_pack_file *p;
Packit Service 20376f
Packit Service 20376f
		p = git_vector_get(&backend->packs, i);
Packit Service 20376f
		if (p == last_found)
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		error = git_pack_entry_find(e, p, short_oid, len);
Packit Service 20376f
		if (error == GIT_EAMBIGUOUS)
Packit Service 20376f
			return error;
Packit Service 20376f
		if (!error) {
Packit Service 20376f
			if (found && git_oid_cmp(&e->sha1, &found_full_oid))
Packit Service 20376f
				return git_odb__error_ambiguous("found multiple pack entries");
Packit Service 20376f
			git_oid_cpy(&found_full_oid, &e->sha1);
Packit Service 20376f
			found = true;
Packit Service 20376f
			backend->last_found = p;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!found)
Packit Service 20376f
		return git_odb__error_notfound("no matching pack entry for prefix",
Packit Service 20376f
			short_oid, len);
Packit Service 20376f
	else
Packit Service 20376f
		return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
/***********************************************************
Packit Service 20376f
 *
Packit Service 20376f
 * PACKED BACKEND PUBLIC API
Packit Service 20376f
 *
Packit Service 20376f
 * Implement the git_odb_backend API calls
Packit Service 20376f
 *
Packit Service 20376f
 ***********************************************************/
Packit Service 20376f
static int pack_backend__refresh(git_odb_backend *backend_)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	struct stat st;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
	struct pack_backend *backend = (struct pack_backend *)backend_;
Packit Service 20376f
Packit Service 20376f
	if (backend->pack_folder == NULL)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
Packit Service 20376f
		return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
Packit Service 20376f
Packit Service 20376f
	git_buf_sets(&path, backend->pack_folder);
Packit Service 20376f
Packit Service 20376f
	/* reload all packs */
Packit Service 20376f
	error = git_path_direach(&path, 0, packfile_load__cb, backend);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
	git_vector_sort(&backend->packs);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__read_header(
Packit Service 20376f
	size_t *len_p, git_otype *type_p,
Packit Service 20376f
	struct git_odb_backend *backend, const git_oid *oid)
Packit Service 20376f
{
Packit Service 20376f
	struct git_pack_entry e;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(len_p && type_p && backend && oid);
Packit Service 20376f
Packit Service 20376f
	if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__freshen(
Packit Service 20376f
	git_odb_backend *backend, const git_oid *oid)
Packit Service 20376f
{
Packit Service 20376f
	struct git_pack_entry e;
Packit Service 20376f
	time_t now;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	now = time(NULL);
Packit Service 20376f
Packit Service 20376f
	if (e.p->last_freshen > now - FRESHEN_FREQUENCY)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_futils_touch(e.p->pack_name, &now)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	e.p->last_freshen = now;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__read(
Packit Service 20376f
	void **buffer_p, size_t *len_p, git_otype *type_p,
Packit Service 20376f
	git_odb_backend *backend, const git_oid *oid)
Packit Service 20376f
{
Packit Service 20376f
	struct git_pack_entry e;
Packit Service 20376f
	git_rawobj raw = {NULL};
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
Packit Service 20376f
		(error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	*buffer_p = raw.data;
Packit Service 20376f
	*len_p = raw.len;
Packit Service 20376f
	*type_p = raw.type;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__read_prefix(
Packit Service 20376f
	git_oid *out_oid,
Packit Service 20376f
	void **buffer_p,
Packit Service 20376f
	size_t *len_p,
Packit Service 20376f
	git_otype *type_p,
Packit Service 20376f
	git_odb_backend *backend,
Packit Service 20376f
	const git_oid *short_oid,
Packit Service 20376f
	size_t len)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if (len < GIT_OID_MINPREFIXLEN)
Packit Service 20376f
		error = git_odb__error_ambiguous("prefix length too short");
Packit Service 20376f
Packit Service 20376f
	else if (len >= GIT_OID_HEXSZ) {
Packit Service 20376f
		/* We can fall back to regular read method */
Packit Service 20376f
		error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
Packit Service 20376f
		if (!error)
Packit Service 20376f
			git_oid_cpy(out_oid, short_oid);
Packit Service 20376f
	} else {
Packit Service 20376f
		struct git_pack_entry e;
Packit Service 20376f
		git_rawobj raw = {NULL};
Packit Service 20376f
Packit Service 20376f
		if ((error = pack_entry_find_prefix(
Packit Service 20376f
				&e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
Packit Service 20376f
			(error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
Packit Service 20376f
		{
Packit Service 20376f
			*buffer_p = raw.data;
Packit Service 20376f
			*len_p = raw.len;
Packit Service 20376f
			*type_p = raw.type;
Packit Service 20376f
			git_oid_cpy(out_oid, &e.sha1);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
Packit Service 20376f
{
Packit Service 20376f
	struct git_pack_entry e;
Packit Service 20376f
	return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__exists_prefix(
Packit Service 20376f
	git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	struct pack_backend *pb = (struct pack_backend *)backend;
Packit Service 20376f
	struct git_pack_entry e = {0};
Packit Service 20376f
Packit Service 20376f
	error = pack_entry_find_prefix(&e, pb, short_id, len);
Packit Service 20376f
	git_oid_cpy(out, &e.sha1);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	struct git_pack_file *p;
Packit Service 20376f
	struct pack_backend *backend;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
Packit Service 20376f
	assert(_backend && cb);
Packit Service 20376f
	backend = (struct pack_backend *)_backend;
Packit Service 20376f
Packit Service 20376f
	/* Make sure we know about the packfiles */
Packit Service 20376f
	if ((error = pack_backend__refresh(_backend)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&backend->packs, i, p) {
Packit Service 20376f
		if ((error = git_pack_foreach_entry(p, cb, data)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
Packit Service 20376f
Packit Service 20376f
	assert(writepack);
Packit Service 20376f
Packit Service 20376f
	return git_indexer_append(writepack->indexer, data, size, stats);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_transfer_progress *stats)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
Packit Service 20376f
Packit Service 20376f
	assert(writepack);
Packit Service 20376f
Packit Service 20376f
	return git_indexer_commit(writepack->indexer, stats);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
Packit Service 20376f
Packit Service 20376f
	assert(writepack);
Packit Service 20376f
Packit Service 20376f
	git_indexer_free(writepack->indexer);
Packit Service 20376f
	git__free(writepack);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__writepack(struct git_odb_writepack **out,
Packit Service 20376f
	git_odb_backend *_backend,
Packit Service 20376f
        git_odb *odb,
Packit Service 20376f
	git_transfer_progress_cb progress_cb,
Packit Service 20376f
	void *progress_payload)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_backend *backend;
Packit Service 20376f
	struct pack_writepack *writepack;
Packit Service 20376f
Packit Service 20376f
	assert(out && _backend);
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	backend = (struct pack_backend *)_backend;
Packit Service 20376f
Packit Service 20376f
	writepack = git__calloc(1, sizeof(struct pack_writepack));
Packit Service 20376f
	GITERR_CHECK_ALLOC(writepack);
Packit Service 20376f
Packit Service 20376f
	if (git_indexer_new(&writepack->indexer,
Packit Service 20376f
		backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) {
Packit Service 20376f
		git__free(writepack);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	writepack->parent.backend = _backend;
Packit Service 20376f
	writepack->parent.append = pack_backend__writepack_append;
Packit Service 20376f
	writepack->parent.commit = pack_backend__writepack_commit;
Packit Service 20376f
	writepack->parent.free = pack_backend__writepack_free;
Packit Service 20376f
Packit Service 20376f
	*out = (git_odb_writepack *)writepack;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void pack_backend__free(git_odb_backend *_backend)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_backend *backend;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	assert(_backend);
Packit Service 20376f
Packit Service 20376f
	backend = (struct pack_backend *)_backend;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < backend->packs.length; ++i) {
Packit Service 20376f
		struct git_pack_file *p = git_vector_get(&backend->packs, i);
Packit Service 20376f
		git_mwindow_put_pack(p);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_free(&backend->packs);
Packit Service 20376f
	git__free(backend->pack_folder);
Packit Service 20376f
	git__free(backend);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
Packit Service 20376f
	GITERR_CHECK_ALLOC(backend);
Packit Service 20376f
Packit Service 20376f
	if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
Packit Service 20376f
		git__free(backend);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	backend->parent.version = GIT_ODB_BACKEND_VERSION;
Packit Service 20376f
Packit Service 20376f
	backend->parent.read = &pack_backend__read;
Packit Service 20376f
	backend->parent.read_prefix = &pack_backend__read_prefix;
Packit Service 20376f
	backend->parent.read_header = &pack_backend__read_header;
Packit Service 20376f
	backend->parent.exists = &pack_backend__exists;
Packit Service 20376f
	backend->parent.exists_prefix = &pack_backend__exists_prefix;
Packit Service 20376f
	backend->parent.refresh = &pack_backend__refresh;
Packit Service 20376f
	backend->parent.foreach = &pack_backend__foreach;
Packit Service 20376f
	backend->parent.writepack = &pack_backend__writepack;
Packit Service 20376f
	backend->parent.freshen = &pack_backend__freshen;
Packit Service 20376f
	backend->parent.free = &pack_backend__free;
Packit Service 20376f
Packit Service 20376f
	*out = backend;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
Packit Service 20376f
{
Packit Service 20376f
	struct pack_backend *backend = NULL;
Packit Service 20376f
	struct git_pack_file *packfile = NULL;
Packit Service 20376f
Packit Service 20376f
	if (pack_backend__alloc(&backend, 1) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (git_mwindow_get_pack(&packfile, idx) < 0 ||
Packit Service 20376f
		git_vector_insert(&backend->packs, packfile) < 0)
Packit Service 20376f
	{
Packit Service 20376f
		pack_backend__free((git_odb_backend *)backend);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*backend_out = (git_odb_backend *)backend;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	struct pack_backend *backend = NULL;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	if (pack_backend__alloc(&backend, 8) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (!(error = git_buf_joinpath(&path, objects_dir, "pack")) &&
Packit Service 20376f
		git_path_isdir(git_buf_cstr(&path)))
Packit Service 20376f
	{
Packit Service 20376f
		backend->pack_folder = git_buf_detach(&path);
Packit Service 20376f
		error = pack_backend__refresh((git_odb_backend *)backend);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error < 0) {
Packit Service 20376f
		pack_backend__free((git_odb_backend *)backend);
Packit Service 20376f
		backend = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*backend_out = (git_odb_backend *)backend;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}