Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/git/odb.h129
-rw-r--r--src/odb.c1792
-rw-r--r--src/odb.h61
-rw-r--r--src/odb_loose.c658
-rw-r--r--src/odb_pack.c1207
-rw-r--r--src/repository.c2
-rw-r--r--src/tag.c2
-rw-r--r--tests/t0102-objtype.c74
-rw-r--r--tests/t0103-objhash.c28
-rw-r--r--tests/t0202-readloose.c28
-rw-r--r--tests/t0203-readloose.c28
-rw-r--r--tests/t0204-readpack.c4
-rw-r--r--tests/t0205-readheader.c4
-rw-r--r--tests/t0301-write.c28
-rw-r--r--tests/test_helpers.c2
15 files changed, 2187 insertions, 1860 deletions
diff --git a/src/git/odb.h b/src/git/odb.h
index b4812424c..584a848c5 100644
--- a/src/git/odb.h
+++ b/src/git/odb.h
@@ -17,17 +17,53 @@ GIT_BEGIN_DECL
/** An open object database handle. */
typedef struct git_odb git_odb;
+/** A custom backend in an ODB */
+typedef struct git_odb_backend git_odb_backend;
+
/**
- * Open an object database for read/write access.
+ * Create a new object database with no backends.
+ *
+ * Before the ODB can be used for read/writing, a custom database
+ * backend must be manually added using `git_odb_add_backend()`
+ *
+ * @param out location to store the database pointer, if opened.
+ * Set to NULL if the open failed.
+ * @return GIT_SUCCESS if the database was created; otherwise an error
+ * code describing why the open was not possible.
+ */
+GIT_EXTERN(int) git_odb_new(git_odb **out);
+
+/**
+ * Create a new object database and automatically add
+ * the two default backends:
+ *
+ * - git_odb_backend_loose: read and write loose object files
+ * from disk, assuming `objects_dir` as the Objects folder
+ *
+ * - git_odb_backend_pack: read objects from packfiles,
+ * assuming `objects_dir` as the Objects folder which
+ * contains a 'pack/' folder with the corresponding data
+ *
* @param out location to store the database pointer, if opened.
* Set to NULL if the open failed.
- * @param objects_dir path of the database's "objects" directory.
+ * @param objects_dir path of the backends' "objects" directory.
* @return GIT_SUCCESS if the database opened; otherwise an error
* code describing why the open was not possible.
*/
GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
/**
+ * Add a custom backend to an existing Object DB
+ *
+ * Read <odb_backends.h> for more information.
+ *
+ * @param odb database to add the backend to
+ * @paramm backend pointer to a git_odb_backend instance
+ * @return 0 on sucess; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend);
+
+/**
* Close an open object database.
* @param db database pointer to close. If NULL no action is taken.
*/
@@ -76,7 +112,7 @@ GIT_EXTERN(int) git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id);
* are filled. The 'data' pointer will always be NULL.
*
* The raw object pointed by 'out' doesn't need to be manually
- * closed with git_obj_close().
+ * closed with git_rawobj_close().
*
* @param out object descriptor to populate upon reading.
* @param db database to search for the object in.
@@ -88,44 +124,45 @@ GIT_EXTERN(int) git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id);
GIT_EXTERN(int) git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id);
/**
- * Read an object from the database using only pack files.
- *
- * If GIT_ENOTFOUND then out->data is set to NULL.
+ * Write an object to the database.
*
- * @param out object descriptor to populate upon reading.
- * @param db database to search for the object in.
- * @param id identity of the object to read.
+ * @param id identity of the object written.
+ * @param db database to which the object should be written.
+ * @param obj object descriptor for the object to write.
* @return
- * - GIT_SUCCESS if the object was read.
- * - GIT_ENOTFOUND if the object is not in the database.
+ * - GIT_SUCCESS if the object was written;
+ * - GIT_ERROR otherwise.
*/
-GIT_EXTERN(int) git_odb__read_packed(git_rawobj *out, git_odb *db, const git_oid *id);
+GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj);
/**
- * Read an object from the database using only loose object files.
- *
- * If GIT_ENOTFOUND then out->data is set to NULL.
+ * Determine if the given object can be found in the object database.
*
- * @param out object descriptor to populate upon reading.
- * @param db database to search for the object in.
- * @param id identity of the object to read.
+ * @param db database to be searched for the given object.
+ * @param id the object to search for.
* @return
- * - GIT_SUCCESS if the object was read.
- * - GIT_ENOTFOUND if the object is not in the database.
+ * - true, if the object was found
+ * - false, otherwise
*/
-GIT_EXTERN(int) git_odb__read_loose(git_rawobj *out, git_odb *db, const git_oid *id);
+GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
+
+
+
+
/**
- * Write an object to the database.
+ * Determine the object-ID (sha1 hash) of the given git_rawobj.
*
- * @param id identity of the object written.
- * @param db database to which the object should be written.
- * @param obj object descriptor for the object to write.
+ * The input obj must be a valid loose object type and the data
+ * pointer must not be NULL, unless the len field is also zero.
+ *
+ * @param id the resulting object-ID.
+ * @param obj the object whose hash is to be determined.
* @return
- * - GIT_SUCCESS if the object was written;
- * - GIT_ERROR otherwise.
+ * - GIT_SUCCESS if the object-ID was correctly determined.
+ * - GIT_ERROR if the given object is malformed.
*/
-GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj);
+GIT_EXTERN(int) git_rawobj_hash(git_oid *id, git_rawobj *obj);
/**
* Release all memory used by the obj structure.
@@ -136,12 +173,15 @@ GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj);
*
* @param obj object descriptor to free.
*/
-GIT_INLINE(void) git_obj_close(git_rawobj *obj)
+GIT_INLINE(void) git_rawobj_close(git_rawobj *obj)
{
free(obj->data);
obj->data = NULL;
}
+
+
+
/**
* Convert an object type to it's string representation.
*
@@ -151,7 +191,7 @@ GIT_INLINE(void) git_obj_close(git_rawobj *obj)
* @param type object type to convert.
* @return the corresponding string representation.
*/
-GIT_EXTERN(const char *) git_obj_type_to_string(git_otype type);
+GIT_EXTERN(const char *) git_otype_tostring(git_otype type);
/**
* Convert a string object type representation to it's git_otype.
@@ -159,7 +199,7 @@ GIT_EXTERN(const char *) git_obj_type_to_string(git_otype type);
* @param str the string to convert.
* @return the corresponding git_otype.
*/
-GIT_EXTERN(git_otype) git_obj_string_to_type(const char *str);
+GIT_EXTERN(git_otype) git_otype_fromstring(const char *str);
/**
* Determine if the given git_otype is a valid loose object type.
@@ -168,32 +208,7 @@ GIT_EXTERN(git_otype) git_obj_string_to_type(const char *str);
* @return true if the type represents a valid loose object type,
* false otherwise.
*/
-GIT_EXTERN(int) git_obj__loose_object_type(git_otype type);
-
-/**
- * Determine the object-ID (sha1 hash) of the given git_rawobj.
- *
- * The input obj must be a valid loose object type and the data
- * pointer must not be NULL, unless the len field is also zero.
- *
- * @param id the resulting object-ID.
- * @param obj the object whose hash is to be determined.
- * @return
- * - GIT_SUCCESS if the object-ID was correctly determined.
- * - GIT_ERROR if the given object is malformed.
- */
-GIT_EXTERN(int) git_obj_hash(git_oid *id, git_rawobj *obj);
-
-/**
- * Determine if the given object can be found in the object database.
- *
- * @param db database to be searched for the given object.
- * @param id the object to search for.
- * @return
- * - true, if the object was found
- * - false, otherwise
- */
-GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
+GIT_EXTERN(int) git_otype_is_loose(git_otype type);
/** @} */
GIT_END_DECL
diff --git a/src/odb.c b/src/odb.c
index fd8c27197..1dbda1a3c 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -24,121 +24,12 @@
*/
#include "common.h"
-#include "git/odb.h"
#include "git/zlib.h"
#include "fileops.h"
#include "hash.h"
#include "odb.h"
#include "delta-apply.h"
-#define GIT_PACK_NAME_MAX (5 + 40 + 1)
-
-#define OBJ_LOCATION_NOTFOUND GIT_ENOTFOUND
-#define OBJ_LOCATION_INPACK 1
-#define OBJ_LOCATION_LOOSE 2
-
-typedef struct {
- uint32_t n;
- unsigned char *oid;
- off_t offset;
- off_t size;
-} index_entry;
-
-
-struct git_pack {
- git_odb *db;
- git_lck lock;
-
- /** Functions to access idx_map. */
- int (*idx_search)(
- uint32_t *,
- struct git_pack *,
- const git_oid *);
- int (*idx_search_offset)(
- uint32_t *,
- struct git_pack *,
- off_t);
- int (*idx_get)(
- index_entry *,
- struct git_pack *,
- uint32_t n);
-
- /** The .idx file, mapped into memory. */
- git_file idx_fd;
- git_map idx_map;
- uint32_t *im_fanout;
- unsigned char *im_oid;
- uint32_t *im_crc;
- uint32_t *im_offset32;
- uint32_t *im_offset64;
- uint32_t *im_off_idx;
- uint32_t *im_off_next;
-
- /** Number of objects in this pack. */
- uint32_t obj_cnt;
-
- /** File descriptor for the .pack file. */
- git_file pack_fd;
-
- /** Memory map of the pack's contents */
- git_map pack_map;
-
- /** The size of the .pack file. */
- off_t pack_size;
-
- /** The mtime of the .pack file. */
- time_t pack_mtime;
-
- /** Number of git_packlist we appear in. */
- unsigned int refcnt;
-
- /** Number of active users of the idx_map data. */
- unsigned int idxcnt;
- unsigned
- invalid:1 /* the pack is unable to be read by libgit2 */
- ;
-
- /** Name of the pack file(s), without extension ("pack-abc"). */
- char pack_name[GIT_PACK_NAME_MAX];
-};
-typedef struct git_pack git_pack;
-
-typedef struct {
- size_t n_packs;
- unsigned int refcnt;
- git_pack *packs[GIT_FLEX_ARRAY];
-} git_packlist;
-
-struct git_odb {
- git_lck lock;
-
- /** Path to the "objects" directory. */
- char *objects_dir;
-
- /** Known pack files from ${objects_dir}/packs. */
- git_packlist *packlist;
-
- /** Alternate databases to search. */
- git_odb **alternates;
- size_t n_alternates;
-
- /** loose object zlib compression level. */
- int object_zlib_level;
- /** loose object file fsync flag. */
- int fsync_object_files;
-};
-
-typedef struct { /* object header data */
- git_otype type; /* object type */
- size_t size; /* object size */
-} obj_hdr;
-
-typedef struct { /* '.pack' file header */
- uint32_t sig; /* PACK_SIG */
- uint32_t ver; /* pack version */
- uint32_t cnt; /* object count */
-} pack_hdr;
-
static struct {
const char *str; /* type name string */
int loose; /* valid loose object type flag */
@@ -153,40 +44,20 @@ static struct {
{ "REF_DELTA", 0 } /* 7 = GIT_OBJ_REF_DELTA */
};
-typedef union obj_location {
- char loose_path[GIT_PATH_MAX];
- struct {
- git_pack *ptr;
- uint32_t n;
- } pack;
-} obj_location;
-
-
/***********************************************************
*
* MISCELANEOUS HELPER FUNCTIONS
*
***********************************************************/
-GIT_INLINE(uint32_t) decode32(void *b)
-{
- return ntohl(*((uint32_t *)b));
-}
-
-GIT_INLINE(uint64_t) decode64(void *b)
-{
- uint32_t *p = b;
- return (((uint64_t)ntohl(p[0])) << 32) | ntohl(p[1]);
-}
-
-const char *git_obj_type_to_string(git_otype type)
+const char *git_otype_tostring(git_otype type)
{
if (type < 0 || ((size_t) type) >= ARRAY_SIZE(obj_type_table))
return "";
return obj_type_table[type].str;
}
-git_otype git_obj_string_to_type(const char *str)
+git_otype git_otype_fromstring(const char *str)
{
size_t i;
@@ -200,7 +71,7 @@ git_otype git_obj_string_to_type(const char *str)
return GIT_OBJ_BAD;
}
-int git_obj__loose_object_type(git_otype type)
+int git_otype_is_loose(git_otype type)
{
if (type < 0 || ((size_t) type) >= ARRAY_SIZE(obj_type_table))
return 0;
@@ -209,7 +80,7 @@ int git_obj__loose_object_type(git_otype type)
static int format_object_header(char *hdr, size_t n, git_rawobj *obj)
{
- const char *type_str = git_obj_type_to_string(obj->type);
+ const char *type_str = git_otype_tostring(obj->type);
int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj->len);
assert(len > 0); /* otherwise snprintf() is broken */
@@ -220,14 +91,14 @@ static int format_object_header(char *hdr, size_t n, git_rawobj *obj)
return len+1;
}
-static int hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj)
+int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj)
{
git_buf_vec vec[2];
int hdrlen;
assert(id && hdr && len && obj);
- if (!git_obj__loose_object_type(obj->type))
+ if (!git_otype_is_loose(obj->type))
return GIT_ERROR;
if (!obj->data && obj->len != 0)
@@ -248,221 +119,28 @@ static int hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj)
return GIT_SUCCESS;
}
-int git_obj_hash(git_oid *id, git_rawobj *obj)
+int git_rawobj_hash(git_oid *id, git_rawobj *obj)
{
char hdr[64];
int hdrlen;
assert(id && obj);
- return hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj);
-}
-
-static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id)
-{
- size_t len = strlen(dir);
-
- /* check length: 43 = 40 hex sha1 chars + 2 * '/' + '\0' */
- if (len+43 > n)
- return len+43;
-
- /* the object dir: eg $GIT_DIR/objects */
- strcpy(name, dir);
- if (name[len-1] != '/')
- name[len++] = '/';
-
- /* loose object filename: aa/aaa... (41 bytes) */
- git_oid_pathfmt(&name[len], id);
- name[len+41] = '\0';
-
- return 0;
-}
-
-static int is_zlib_compressed_data(unsigned char *data)
-{
- unsigned int w;
-
- w = ((unsigned int)(data[0]) << 8) + data[1];
- return data[0] == 0x78 && !(w % 31);
-}
-
-static size_t get_binary_object_header(obj_hdr *hdr, gitfo_buf *obj)
-{
- unsigned char c;
- unsigned char *data = obj->data;
- size_t shift, size, used = 0;
-
- if (obj->len == 0)
- return 0;
-
- c = data[used++];
- hdr->type = (c >> 4) & 7;
-
- size = c & 15;
- shift = 4;
- while (c & 0x80) {
- if (obj->len <= used)
- return 0;
- if (sizeof(size_t) * 8 <= shift)
- return 0;
- c = data[used++];
- size += (c & 0x7f) << shift;
- shift += 7;
- }
- hdr->size = size;
-
- return used;
-}
-
-static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
-{
- char c, typename[10];
- size_t size, used = 0;
-
- /*
- * type name string followed by space.
- */
- while ((c = data[used]) != ' ') {
- typename[used++] = c;
- if (used >= sizeof(typename))
- return 0;
- }
- typename[used] = 0;
- if (used == 0)
- return 0;
- hdr->type = git_obj_string_to_type(typename);
- used++; /* consume the space */
-
- /*
- * length follows immediately in decimal (without
- * leading zeros).
- */
- size = data[used++] - '0';
- if (size > 9)
- return 0;
- if (size) {
- while ((c = data[used]) != '\0') {
- size_t d = c - '0';
- if (d > 9)
- break;
- used++;
- size = size * 10 + d;
- }
- }
- hdr->size = size;
-
- /*
- * the length must be followed by a zero byte
- */
- if (data[used++] != '\0')
- return 0;
-
- return used;
+ return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj);
}
-
-
-
-
-
-/***********************************************************
- *
- * ZLIB RELATED FUNCTIONS
- *
- ***********************************************************/
-
-static void init_stream(z_stream *s, void *out, size_t len)
-{
- memset(s, 0, sizeof(*s));
- s->next_out = out;
- s->avail_out = len;
-}
-
-static void set_stream_input(z_stream *s, void *in, size_t len)
-{
- s->next_in = in;
- s->avail_in = len;
-}
-
-static void set_stream_output(z_stream *s, void *out, size_t len)
-{
- s->next_out = out;
- s->avail_out = len;
-}
-
-static int start_inflate(z_stream *s, gitfo_buf *obj, void *out, size_t len)
-{
- int status;
-
- init_stream(s, out, len);
- set_stream_input(s, obj->data, obj->len);
-
- if ((status = inflateInit(s)) < Z_OK)
- return status;
-
- return inflate(s, 0);
-}
-
-static int finish_inflate(z_stream *s)
+int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
{
+ z_stream zs;
int status = Z_OK;
- while (status == Z_OK)
- status = inflate(s, Z_FINISH);
-
- inflateEnd(s);
-
- if ((status != Z_STREAM_END) || (s->avail_in != 0))
- return GIT_ERROR;
-
- return GIT_SUCCESS;
-}
-
-static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
-{
- unsigned char *buf, *head = hb;
- size_t tail;
-
- /*
- * allocate a buffer to hold the inflated data and copy the
- * initial sequence of inflated data from the tail of the
- * head buffer, if any.
- */
- if ((buf = git__malloc(hdr->size + 1)) == NULL) {
- inflateEnd(s);
- return NULL;
- }
- tail = s->total_out - used;
- if (used > 0 && tail > 0) {
- if (tail > hdr->size)
- tail = hdr->size;
- memcpy(buf, head + used, tail);
- }
- used = tail;
+ memset(&zs, 0x0, sizeof(zs));
- /*
- * inflate the remainder of the object data, if any
- */
- if (hdr->size < used)
- inflateEnd(s);
- else {
- set_stream_output(s, buf + used, hdr->size - used);
- if (finish_inflate(s)) {
- free(buf);
- return NULL;
- }
- }
+ zs.next_out = out;
+ zs.avail_out = outlen;
- return buf;
-}
-
-static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
-{
- z_stream zs;
- int status = Z_OK;
-
- init_stream(&zs, out, outlen);
- set_stream_input(&zs, in, inlen);
+ zs.next_in = in;
+ zs.avail_in = inlen;
if (inflateInit(&zs) < Z_OK)
return GIT_ERROR;
@@ -481,1411 +159,164 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
return GIT_SUCCESS;
}
-/*
- * At one point, there was a loose object format that was intended to
- * mimic the format used in pack-files. This was to allow easy copying
- * of loose object data into packs. This format is no longer used, but
- * we must still read it.
- */
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj)
-{
- unsigned char *in, *buf;
- obj_hdr hdr;
- size_t len, used;
-
- /*
- * read the object header, which is an (uncompressed)
- * binary encoding of the object type and size.
- */
- if ((used = get_binary_object_header(&hdr, obj)) == 0)
- return GIT_ERROR;
-
- if (!git_obj__loose_object_type(hdr.type))
- return GIT_ERROR;
-
- /*
- * allocate a buffer and inflate the data into it
- */
- buf = git__malloc(hdr.size + 1);
- if (!buf)
- return GIT_ERROR;
-
- in = ((unsigned char *)obj->data) + used;
- len = obj->len - used;
- if (inflate_buffer(in, len, buf, hdr.size)) {
- free(buf);
- return GIT_ERROR;
- }
- buf[hdr.size] = '\0';
-
- out->data = buf;
- out->len = hdr.size;
- out->type = hdr.type;
-
- return GIT_SUCCESS;
-}
-
-static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj)
-{
- unsigned char head[64], *buf;
- z_stream zs;
- int z_status;
- obj_hdr hdr;
- size_t used;
-
- /*
- * check for a pack-like loose object
- */
- if (!is_zlib_compressed_data(obj->data))
- return inflate_packlike_loose_disk_obj(out, obj);
-
- /*
- * inflate the initial part of the io buffer in order
- * to parse the object header (type and size).
- */
- if ((z_status = start_inflate(&zs, obj, head, sizeof(head))) < Z_OK)
- return GIT_ERROR;
-
- if ((used = get_object_header(&hdr, head)) == 0)
- return GIT_ERROR;
-
- if (!git_obj__loose_object_type(hdr.type))
- return GIT_ERROR;
-
- /*
- * allocate a buffer and inflate the object data into it
- * (including the initial sequence in the head buffer).
- */
- if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
- return GIT_ERROR;
- buf[hdr.size] = '\0';
-
- out->data = buf;
- out->len = hdr.size;
- out->type = hdr.type;
-
- return GIT_SUCCESS;
-}
-
-static int make_temp_file(git_file *fd, char *tmp, size_t n, char *file)
-{
- char *template = "/tmp_obj_XXXXXX";
- size_t tmplen = strlen(template);
- int dirlen;
-
- if ((dirlen = git__dirname(tmp, n, file)) < 0)
- return GIT_ERROR;
-
- if ((dirlen + tmplen) >= n)
- return GIT_ERROR;
-
- strcpy(tmp + dirlen, (dirlen) ? template : template + 1);
-
- *fd = gitfo_mkstemp(tmp);
- if (*fd < 0 && dirlen) {
- /* create directory if it doesn't exist */
- tmp[dirlen] = '\0';
- if ((gitfo_exists(tmp) < 0) && gitfo_mkdir(tmp, 0755))
- return GIT_ERROR;
- /* try again */
- strcpy(tmp + dirlen, template);
- *fd = gitfo_mkstemp(tmp);
- }
- if (*fd < 0)
- return GIT_ERROR;
-
- return GIT_SUCCESS;
-}
-
-static int deflate_buf(z_stream *s, void *in, size_t len, int flush)
-{
- int status = Z_OK;
-
- set_stream_input(s, in, len);
- while (status == Z_OK) {
- status = deflate(s, flush);
- if (s->avail_in == 0)
- break;
- }
- return status;
-}
-
-static int deflate_obj(gitfo_buf *buf, char *hdr, int hdrlen, git_rawobj *obj, int level)
-{
- z_stream zs;
- int status;
- size_t size;
-
- assert(buf && !buf->data && hdr && obj);
- assert(level == Z_DEFAULT_COMPRESSION || (level >= 0 && level <= 9));
-
- buf->data = NULL;
- buf->len = 0;
- init_stream(&zs, NULL, 0);
-
- if (deflateInit(&zs, level) < Z_OK)
- return GIT_ERROR;
-
- size = deflateBound(&zs, hdrlen + obj->len);
-
- if ((buf->data = git__malloc(size)) == NULL) {
- deflateEnd(&zs);
- return GIT_ERROR;
- }
-
- set_stream_output(&zs, buf->data, size);
-
- /* compress the header */
- status = deflate_buf(&zs, hdr, hdrlen, Z_NO_FLUSH);
-
- /* if header compressed OK, compress the object */
- if (status == Z_OK)
- status = deflate_buf(&zs, obj->data, obj->len, Z_FINISH);
-
- if (status != Z_STREAM_END) {
- deflateEnd(&zs);
- free(buf->data);
- buf->data = NULL;
- return GIT_ERROR;
- }
-
- buf->len = zs.total_out;
- deflateEnd(&zs);
-
- return GIT_SUCCESS;
-}
-
-
-
/***********************************************************
*
- * PACKFILE INDEX FUNCTIONS
- *
- * Get index formation for packfile indexes v1 and v2
- *
- ***********************************************************/
-
-static int pack_openidx_map(git_pack *p)
-{
- char pb[GIT_PATH_MAX];
- off_t len;
-
- if (git__fmt(pb, sizeof(pb), "%s/pack/%s.idx",
- p->db->objects_dir,
- p->pack_name) < 0)
- return GIT_ERROR;
-
- if ((p->idx_fd = gitfo_open(pb, O_RDONLY)) < 0)
- return GIT_ERROR;
-
- if ((len = gitfo_size(p->idx_fd)) < 0
- || !git__is_sizet(len)
- || gitfo_map_ro(&p->idx_map, p->idx_fd, 0, (size_t)len)) {
- gitfo_close(p->idx_fd);
- return GIT_ERROR;
- }
-
- return GIT_SUCCESS;
-}
-
-typedef struct {
- off_t offset;
- uint32_t n;
-} offset_idx_info;
-
-static int cmp_offset_idx_info(const void *lhs, const void *rhs)
-{
- const offset_idx_info *a = lhs;
- const offset_idx_info *b = rhs;
- return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
-}
-
-static int make_offset_index(git_pack *p, offset_idx_info *data)
-{
- off_t min_off = 3 * 4, max_off = p->pack_size - GIT_OID_RAWSZ;
- uint32_t *idx, *next;
- uint32_t j;
-
- qsort(data, p->obj_cnt, sizeof(*data), cmp_offset_idx_info);
-
- if (data[0].offset < min_off || data[p->obj_cnt].offset > max_off)
- return GIT_ERROR;
-
- if ((idx = git__malloc(sizeof(*idx) * (p->obj_cnt+1))) == NULL)
- return GIT_ERROR;
- if ((next = git__malloc(sizeof(*next) * p->obj_cnt)) == NULL) {
- free(idx);
- return GIT_ERROR;
- }
-
- for (j = 0; j < p->obj_cnt+1; j++)
- idx[j] = data[j].n;
-
- for (j = 0; j < p->obj_cnt; j++) {
- assert(idx[j] < p->obj_cnt);
- assert(idx[j+1] < p->obj_cnt+1);
-
- next[idx[j]] = idx[j+1];
- }
-
- p->im_off_idx = idx;
- p->im_off_next = next;
- return GIT_SUCCESS;
-}
-
-static int idxv1_search(uint32_t *out, git_pack *p, const git_oid *id)
-{
- unsigned char *data = p->im_oid;
- uint32_t lo = id->id[0] ? p->im_fanout[id->id[0] - 1] : 0;
- uint32_t hi = p->im_fanout[id->id[0]];
-
- do {
- uint32_t mid = (lo + hi) >> 1;
- uint32_t pos = 24 * mid;
- int cmp = memcmp(id->id, data + pos + 4, 20);
- if (cmp < 0)
- hi = mid;
- else if (!cmp) {
- *out = mid;
- return GIT_SUCCESS;
- } else
- lo = mid + 1;
- } while (lo < hi);
- return GIT_ENOTFOUND;
-}
-
-static int idxv1_search_offset(uint32_t *out, git_pack *p, off_t offset)
-{
- if (offset > 0 && offset < (p->pack_size - GIT_OID_RAWSZ)) {
- uint32_t lo = 0, hi = p->obj_cnt+1;
- unsigned char *data = p->im_oid;
- uint32_t *idx = p->im_off_idx;
- do {
- uint32_t mid = (lo + hi) >> 1;
- uint32_t n = idx[mid];
- uint32_t pos = n * (GIT_OID_RAWSZ + 4);
- off_t here = decode32(data + pos);
- if (offset < here)
- hi = mid;
- else if (offset == here) {
- *out = n;
- return GIT_SUCCESS;
- } else
- lo = mid + 1;
- } while (lo < hi);
- }
- return GIT_ENOTFOUND;
-}
-
-static int idxv1_get(index_entry *e, git_pack *p, uint32_t n)
-{
- unsigned char *data = p->im_oid;
- uint32_t *next = p->im_off_next;
-
- if (n < p->obj_cnt) {
- uint32_t pos = n * (GIT_OID_RAWSZ + 4);
- off_t next_off = p->pack_size - GIT_OID_RAWSZ;
- e->n = n;
- e->oid = data + pos + 4;
- e->offset = decode32(data + pos);
- if (next[n] < p->obj_cnt) {
- pos = next[n] * (GIT_OID_RAWSZ + 4);
- next_off = decode32(data + pos);
- }
- e->size = next_off - e->offset;
- return GIT_SUCCESS;
- }
- return GIT_ENOTFOUND;
-}
-
-static int pack_openidx_v1(git_pack *p)
-{
- uint32_t *src_fanout = p->idx_map.data;
- uint32_t *im_fanout;
- offset_idx_info *info;
- size_t expsz;
- uint32_t j;
-
-
- if ((im_fanout = git__malloc(sizeof(*im_fanout) * 256)) == NULL)
- return GIT_ERROR;
-
- im_fanout[0] = decode32(&src_fanout[0]);
- for (j = 1; j < 256; j++) {
- im_fanout[j] = decode32(&src_fanout[j]);
- if (im_fanout[j] < im_fanout[j - 1]) {
- free(im_fanout);
- return GIT_ERROR;
- }
- }
- p->obj_cnt = im_fanout[255];
-
- expsz = 4 * 256 + 24 * p->obj_cnt + 2 * 20;
- if (expsz != p->idx_map.len) {
- free(im_fanout);
- return GIT_ERROR;
- }
-
- p->idx_search = idxv1_search;
- p->idx_search_offset = idxv1_search_offset;
- p->idx_get = idxv1_get;
- p->im_fanout = im_fanout;
- p->im_oid = (unsigned char *)(src_fanout + 256);
-
- if ((info = git__malloc(sizeof(*info) * (p->obj_cnt+1))) == NULL) {
- free(im_fanout);
- return GIT_ERROR;
- }
-
- for (j = 0; j < p->obj_cnt; j++) {
- uint32_t pos = j * (GIT_OID_RAWSZ + 4);
- info[j].offset = decode32(p->im_oid + pos);
- info[j].n = j;
- }
- info[p->obj_cnt].offset = p->pack_size - GIT_OID_RAWSZ;
- info[p->obj_cnt].n = p->obj_cnt;
-
- if (make_offset_index(p, info)) {
- free(im_fanout);
- free(info);
- return GIT_ERROR;
- }
- free(info);
-
- return GIT_SUCCESS;
-}
-
-static int idxv2_search(uint32_t *out, git_pack *p, const git_oid *id)
-{
- unsigned char *data = p->im_oid;
- uint32_t lo = id->id[0] ? p->im_fanout[id->id[0] - 1] : 0;
- uint32_t hi = p->im_fanout[id->id[0]];
-
- do {
- uint32_t mid = (lo + hi) >> 1;
- uint32_t pos = 20 * mid;
- int cmp = memcmp(id->id, data + pos, 20);
- if (cmp < 0)
- hi = mid;
- else if (!cmp) {
- *out = mid;
- return GIT_SUCCESS;
- } else
- lo = mid + 1;
- } while (lo < hi);
- return GIT_ENOTFOUND;
-}
-
-static int idxv2_search_offset(uint32_t *out, git_pack *p, off_t offset)
-{
- if (offset > 0 && offset < (p->pack_size - GIT_OID_RAWSZ)) {
- uint32_t lo = 0, hi = p->obj_cnt+1;
- uint32_t *idx = p->im_off_idx;
- do {
- uint32_t mid = (lo + hi) >> 1;
- uint32_t n = idx[mid];
- uint32_t o32 = decode32(p->im_offset32 + n);
- off_t here = o32;
-
- if (o32 & 0x80000000) {
- uint32_t o64_idx = (o32 & ~0x80000000);
- here = decode64(p->im_offset64 + 2*o64_idx);
- }
-
- if (offset < here)
- hi = mid;
- else if (offset == here) {
- *out = n;
- return GIT_SUCCESS;
- } else
- lo = mid + 1;
- } while (lo < hi);
- }
- return GIT_ENOTFOUND;
-}
-
-static int idxv2_get(index_entry *e, git_pack *p, uint32_t n)
-{
- unsigned char *data = p->im_oid;
- uint32_t *next = p->im_off_next;
-
- if (n < p->obj_cnt) {
- uint32_t o32 = decode32(p->im_offset32 + n);
- off_t next_off = p->pack_size - GIT_OID_RAWSZ;
- e->n = n;
- e->oid = data + n * GIT_OID_RAWSZ;
- e->offset = o32;
- if (o32 & 0x80000000) {
- uint32_t o64_idx = (o32 & ~0x80000000);
- e->offset = decode64(p->im_offset64 + 2*o64_idx);
- }
- if (next[n] < p->obj_cnt) {
- o32 = decode32(p->im_offset32 + next[n]);
- next_off = o32;
- if (o32 & 0x80000000) {
- uint32_t o64_idx = (o32 & ~0x80000000);
- next_off = decode64(p->im_offset64 + 2*o64_idx);
- }
- }
- e->size = next_off - e->offset;
- return GIT_SUCCESS;
- }
- return GIT_ENOTFOUND;
-}
-
-static int pack_openidx_v2(git_pack *p)
-{
- unsigned char *data = p->idx_map.data;
- uint32_t *src_fanout = (uint32_t *)(data + 8);
- uint32_t *im_fanout;
- offset_idx_info *info;
- size_t sz, o64_sz, o64_len;
- uint32_t j;
-
- if ((im_fanout = git__malloc(sizeof(*im_fanout) * 256)) == NULL)
- return GIT_ERROR;
-
- im_fanout[0] = decode32(&src_fanout[0]);
- for (j = 1; j < 256; j++) {
- im_fanout[j] = decode32(&src_fanout[j]);
- if (im_fanout[j] < im_fanout[j - 1]) {
- free(im_fanout);
- return GIT_ERROR;
- }
- }
- p->obj_cnt = im_fanout[255];
-
- /* minimum size of .idx file (with empty 64-bit offsets table): */
- sz = 4 + 4 + 256 * 4 + p->obj_cnt * (20 + 4 + 4) + 2 * 20;
- if (p->idx_map.len < sz) {
- free(im_fanout);
- return GIT_ERROR;
- }
-
- p->idx_search = idxv2_search;
- p->idx_search_offset = idxv2_search_offset;
- p->idx_get = idxv2_get;
- p->im_fanout = im_fanout;
- p->im_oid = (unsigned char *)(src_fanout + 256);
- p->im_crc = (uint32_t *)(p->im_oid + 20 * p->obj_cnt);
- p->im_offset32 = p->im_crc + p->obj_cnt;
- p->im_offset64 = p->im_offset32 + p->obj_cnt;
-
- if ((info = git__malloc(sizeof(*info) * (p->obj_cnt+1))) == NULL) {
- free(im_fanout);
- return GIT_ERROR;
- }
-
- /* check 64-bit offset table index values are within bounds */
- o64_sz = p->idx_map.len - sz;
- o64_len = o64_sz / 8;
- for (j = 0; j < p->obj_cnt; j++) {
- uint32_t o32 = decode32(p->im_offset32 + j);
- off_t offset = o32;
- if (o32 & 0x80000000) {
- uint32_t o64_idx = (o32 & ~0x80000000);
- if (o64_idx >= o64_len) {
- free(im_fanout);
- free(info);
- return GIT_ERROR;
- }
- offset = decode64(p->im_offset64 + 2*o64_idx);
- }
- info[j].offset = offset;
- info[j].n = j;
- }
- info[p->obj_cnt].offset = p->pack_size - GIT_OID_RAWSZ;
- info[p->obj_cnt].n = p->obj_cnt;
-
- if (make_offset_index(p, info)) {
- free(im_fanout);
- free(info);
- return GIT_ERROR;
- }
- free(info);
-
- return GIT_SUCCESS;
-}
-
-
-
-
-
-
-/***********************************************************
+ * OBJECT DATABASE PUBLIC API
*
- * PACKFILE FUNCTIONS
+ * Public calls for the ODB functionality
*
- * Locate, open and access the contents of a packfile
- *
***********************************************************/
-static int pack_stat(git_pack *p)
+int backend_sort_cmp(const void *a, const void *b)
{
- char pb[GIT_PATH_MAX];
- struct stat sb;
-
- if (git__fmt(pb, sizeof(pb), "%s/pack/%s.pack",
- p->db->objects_dir,
- p->pack_name) < 0)
- return GIT_ERROR;
-
- if (gitfo_stat(pb, &sb) || !S_ISREG(sb.st_mode))
- return GIT_ERROR;
+ const git_odb_backend *backend_a = *(const git_odb_backend **)(a);
+ const git_odb_backend *backend_b = *(const git_odb_backend **)(b);
- if (sb.st_size < (3 * 4 + GIT_OID_RAWSZ))
- return GIT_ERROR;
-
- p->pack_size = sb.st_size;
- p->pack_mtime = sb.st_mtime;
-
- return GIT_SUCCESS;
+ return (backend_b->priority - backend_a->priority);
}
-static int pack_openidx(git_pack *p)
+int git_odb_new(git_odb **out)
{
- gitlck_lock(&p->lock);
-
- if (p->invalid) {
- gitlck_unlock(&p->lock);
- return GIT_ERROR;
- }
+ git_odb *db = git__calloc(1, sizeof(*db));
+ if (!db)
+ return GIT_ENOMEM;
- if (++p->idxcnt == 1 && !p->idx_search) {
- int status, version;
- uint32_t *data;
-
- if (pack_stat(p) || pack_openidx_map(p)) {
- p->invalid = 1;
- p->idxcnt--;
- gitlck_unlock(&p->lock);
- return GIT_ERROR;
- }
- data = p->idx_map.data;
- status = GIT_SUCCESS;
- version = 1;
-
- if (decode32(&data[0]) == PACK_TOC)
- version = decode32(&data[1]);
-
- switch (version) {
- case 1:
- status = pack_openidx_v1(p);
- break;
- case 2:
- status = pack_openidx_v2(p);
- break;
- default:
- status = GIT_ERROR;
- }
-
- if (status != GIT_SUCCESS) {
- gitfo_free_map(&p->idx_map);
- p->invalid = 1;
- p->idxcnt--;
- gitlck_unlock(&p->lock);
- return status;
- }
+ if (git_vector_init(&db->backends, 4, backend_sort_cmp, NULL) < 0) {
+ free(db);
+ return GIT_ENOMEM;
}
- gitlck_unlock(&p->lock);
- return GIT_SUCCESS;
-}
-
-static void pack_decidx(git_pack *p)
-{
- gitlck_lock(&p->lock);
- p->idxcnt--;
- gitlck_unlock(&p->lock);
-}
-
-static int read_pack_hdr(pack_hdr *out, git_file fd)
-{
- pack_hdr hdr;
-
- if (gitfo_read(fd, &hdr, sizeof(hdr)))
- return GIT_ERROR;
-
- out->sig = decode32(&hdr.sig);
- out->ver = decode32(&hdr.ver);
- out->cnt = decode32(&hdr.cnt);
-
- return GIT_SUCCESS;
-}
-
-static int check_pack_hdr(git_pack *p)
-{
- pack_hdr hdr;
-
- if (read_pack_hdr(&hdr, p->pack_fd))
- return GIT_ERROR;
-
- if (hdr.sig != PACK_SIG
- || (hdr.ver != 2 && hdr.ver != 3)
- || hdr.cnt != p->obj_cnt)
- return GIT_ERROR;
-
- return GIT_SUCCESS;
-}
-
-static int check_pack_sha1(git_pack *p)
-{
- unsigned char *data = p->idx_map.data;
- off_t pack_sha1_off = p->pack_size - GIT_OID_RAWSZ;
- size_t idx_pack_sha1_off = p->idx_map.len - 2 * GIT_OID_RAWSZ;
- git_oid pack_id, idx_pack_id;
-
- if (gitfo_lseek(p->pack_fd, pack_sha1_off, SEEK_SET) == -1)
- return GIT_ERROR;
-
- if (gitfo_read(p->pack_fd, pack_id.id, sizeof(pack_id.id)))
- return GIT_ERROR;
-
- git_oid_mkraw(&idx_pack_id, data + idx_pack_sha1_off);
-
- if (git_oid_cmp(&pack_id, &idx_pack_id))
- return GIT_ERROR;
-
+ *out = db;
return GIT_SUCCESS;
}
-static int open_pack(git_pack *p)
+int git_odb_add_backend(git_odb *odb, git_odb_backend *backend)
{
- char pb[GIT_PATH_MAX];
- struct stat sb;
+ assert(odb && backend);
- if (p->pack_fd != -1)
- return GIT_SUCCESS;
+ if (backend->odb != NULL && backend->odb != odb)
+ return GIT_EBUSY;
- if (git__fmt(pb, sizeof(pb), "%s/pack/%s.pack",
- p->db->objects_dir,
- p->pack_name) < 0)
- return GIT_ERROR;
-
- if (pack_openidx(p))
- return GIT_ERROR;
+ backend->odb = odb;
- if ((p->pack_fd = gitfo_open(pb, O_RDONLY)) < 0)
- goto error_cleanup;
-
- if (gitfo_fstat(p->pack_fd, &sb)
- || !S_ISREG(sb.st_mode) || p->pack_size != sb.st_size
- || check_pack_hdr(p) || check_pack_sha1(p))
- goto error_cleanup;
-
- if (!git__is_sizet(p->pack_size) ||
- gitfo_map_ro(&p->pack_map, p->pack_fd, 0, (size_t)p->pack_size) < 0)
- goto error_cleanup;
+ if (git_vector_insert(&odb->backends, backend) < 0)
+ return GIT_ENOMEM;
- pack_decidx(p);
+ git_vector_sort(&odb->backends);
return GIT_SUCCESS;
-
-error_cleanup:
- gitfo_close(p->pack_fd);
- p->pack_fd = -1;
- pack_decidx(p);
- return GIT_ERROR;
-}
-
-static void pack_dec(git_pack *p)
-{
- int need_free;
-
- gitlck_lock(&p->lock);
- need_free = !--p->refcnt;
- gitlck_unlock(&p->lock);
-
- if (need_free) {
- if (p->idx_search) {
- gitfo_free_map(&p->idx_map);
- gitfo_close(p->idx_fd);
- free(p->im_fanout);
- free(p->im_off_idx);
- free(p->im_off_next);
- if (p->pack_fd != -1) {
- gitfo_close(p->pack_fd);
- gitfo_free_map(&p->pack_map);
- }
- }
-
- gitlck_free(&p->lock);
- free(p);
- }
-}
-
-static void packlist_dec(git_odb *db, git_packlist *pl)
-{
- int need_free;
-
- assert(db && pl);
-
- gitlck_lock(&db->lock);
- need_free = !--pl->refcnt;
- gitlck_unlock(&db->lock);
-
- if (need_free) {
- size_t j;
- for (j = 0; j < pl->n_packs; j++)
- pack_dec(pl->packs[j]);
- free(pl);
- }
-}
-
-static git_pack *alloc_pack(const char *pack_name)
-{
- git_pack *p = git__calloc(1, sizeof(*p));
- if (!p)
- return NULL;
-
- gitlck_init(&p->lock);
- strcpy(p->pack_name, pack_name);
- p->refcnt = 1;
- p->pack_fd = -1;
- return p;
-}
-
-struct scanned_pack {
- struct scanned_pack *next;
- git_pack *pack;
-};
-
-static int scan_one_pack(void *state, char *name)
-{
- struct scanned_pack **ret = state, *r;
- char *s = strrchr(name, '/'), *d;
-
- if (git__prefixcmp(s + 1, "pack-")
- || git__suffixcmp(s, ".pack")
- || strlen(s + 1) != GIT_PACK_NAME_MAX + 4)
- return 0;
-
- d = strrchr(s + 1, '.');
- strcpy(d + 1, "idx"); /* "pack-abc.pack" -> "pack-abc.idx" */
- if (gitfo_exists(name))
- return 0;
-
- if ((r = git__malloc(sizeof(*r))) == NULL)
- return GIT_ERROR;
-
- *d = '\0'; /* "pack-abc.pack" -_> "pack-abc" */
- if ((r->pack = alloc_pack(s + 1)) == NULL) {
- free(r);
- return GIT_ERROR;
- }
-
- r->next = *ret;
- *ret = r;
- return 0;
-}
-
-static git_packlist *scan_packs(git_odb *db)
-{
- char pb[GIT_PATH_MAX];
- struct scanned_pack *state = NULL, *c;
- size_t cnt;
- git_packlist *new_list;
-
- if (git__fmt(pb, sizeof(pb), "%s/pack", db->objects_dir) < 0)
- return NULL;
- gitfo_dirent(pb, sizeof(pb), scan_one_pack, &state);
-
- /* TODO - merge old entries into the new array */
- for (cnt = 0, c = state; c; c = c->next)
- cnt++;
- new_list = git__malloc(sizeof(*new_list)
- + (sizeof(new_list->packs[0]) * cnt));
- if (!new_list)
- goto fail;
-
- for (cnt = 0, c = state; c; ) {
- struct scanned_pack *n = c->next;
- c->pack->db = db;
- new_list->packs[cnt++] = c->pack;
- free(c);
- c = n;
- }
- new_list->n_packs = cnt;
- new_list->refcnt = 2;
- db->packlist = new_list;
- return new_list;
-
-fail:
- while (state) {
- struct scanned_pack *n = state->next;
- pack_dec(state->pack);
- free(state);
- state = n;
- }
- return NULL;
-}
-
-static git_packlist *packlist_get(git_odb *db)
-{
- git_packlist *pl;
-
- gitlck_lock(&db->lock);
- if ((pl = db->packlist) != NULL)
- pl->refcnt++;
- else
- pl = scan_packs(db);
- gitlck_unlock(&db->lock);
- return pl;
-}
-
-static int search_packs(git_pack **p, uint32_t *n, git_odb *db, const git_oid *id)
-{
- git_packlist *pl = packlist_get(db);
- size_t j;
-
- if (!pl)
- return GIT_ENOTFOUND;
-
- for (j = 0; j < pl->n_packs; j++) {
-
- git_pack *pack = pl->packs[j];
- uint32_t pos;
- int res;
-
- if (pack_openidx(pack))
- continue;
- res = pack->idx_search(&pos, pack, id);
- pack_decidx(pack);
-
- if (!res) {
- packlist_dec(db, pl);
- if (p)
- *p = pack;
- if (n)
- *n = pos;
- return GIT_SUCCESS;
- }
-
- }
-
- packlist_dec(db, pl);
- return GIT_ENOTFOUND;
-}
-
-
-
-
-
-
-
-
-/***********************************************************
- *
- * PACKFILE READING FUNCTIONS
- *
- * Read the contents of a packfile
- *
- ***********************************************************/
-
-static int unpack_object(git_rawobj *out, git_pack *p, index_entry *e);
-
-static int unpack_object_delta(git_rawobj *out, git_pack *p,
- index_entry *base_entry,
- uint8_t *delta_buffer,
- size_t delta_deflated_size,
- size_t delta_inflated_size)
-{
- int res = 0;
- uint8_t *delta = NULL;
- git_rawobj base_obj;
-
- base_obj.data = NULL;
- base_obj.type = GIT_OBJ_BAD;
- base_obj.len = 0;
-
- if ((res = unpack_object(&base_obj, p, base_entry)) < 0)
- goto cleanup;
-
- delta = git__malloc(delta_inflated_size + 1);
-
- if ((res = inflate_buffer(delta_buffer, delta_deflated_size,
- delta, delta_inflated_size)) < 0)
- goto cleanup;
-
- res = git__delta_apply(out, base_obj.data, base_obj.len, delta, delta_inflated_size);
-
- out->type = base_obj.type;
-
-cleanup:
- free(delta);
- git_obj_close(&base_obj);
- return res;
-}
-
-static int unpack_object(git_rawobj *out, git_pack *p, index_entry *e)
-{
- git_otype object_type;
- size_t inflated_size, deflated_size, shift;
- uint8_t *buffer, byte;
-
- assert(out && p && e && git__is_sizet(e->size));
-
- if (open_pack(p))
- return GIT_ERROR;
-
- buffer = (uint8_t *)p->pack_map.data + e->offset;
- deflated_size = (size_t)e->size;
-
- if (deflated_size == 0)
- deflated_size = (size_t)(p->pack_size - e->offset);
-
- byte = *buffer++ & 0xFF;
- deflated_size--;
- object_type = (byte >> 4) & 0x7;
- inflated_size = byte & 0xF;
- shift = 4;
-
- while (byte & 0x80) {
- byte = *buffer++ & 0xFF;
- deflated_size--;
- inflated_size += (byte & 0x7F) << shift;
- shift += 7;
- }
-
- switch (object_type) {
- case GIT_OBJ_COMMIT:
- case GIT_OBJ_TREE:
- case GIT_OBJ_BLOB:
- case GIT_OBJ_TAG: {
-
- /* Handle a normal zlib stream */
- out->len = inflated_size;
- out->type = object_type;
- out->data = git__malloc(inflated_size + 1);
-
- if (inflate_buffer(buffer, deflated_size, out->data, out->len) < 0) {
- free(out->data);
- out->data = NULL;
- return GIT_ERROR;
- }
-
- return GIT_SUCCESS;
- }
-
- case GIT_OBJ_OFS_DELTA: {
-
- off_t delta_offset;
- index_entry entry;
-
- byte = *buffer++ & 0xFF;
- delta_offset = byte & 0x7F;
-
- while (byte & 0x80) {
- delta_offset += 1;
- byte = *buffer++ & 0xFF;
- delta_offset <<= 7;
- delta_offset += (byte & 0x7F);
- }
-
- entry.n = 0;
- entry.oid = NULL;
- entry.offset = e->offset - delta_offset;
- entry.size = 0;
-
- if (unpack_object_delta(out, p, &entry,
- buffer, deflated_size, inflated_size) < 0)
- return GIT_ERROR;
-
- return GIT_SUCCESS;
- }
-
- case GIT_OBJ_REF_DELTA: {
-
- git_oid base_id;
- uint32_t n;
- index_entry entry;
- int res = GIT_ERROR;
-
- git_oid_mkraw(&base_id, buffer);
-
- if (!p->idx_search(&n, p, &base_id) &&
- !p->idx_get(&entry, p, n)) {
-
- res = unpack_object_delta(out, p, &entry,
- buffer + GIT_OID_RAWSZ, deflated_size, inflated_size);
- }
-
- return res;
- }
-
- default:
- return GIT_EOBJCORRUPTED;
- }
-}
-
-
-
-
-
-/***********************************************************
- *
- * ODB OBJECT READING & WRITING
- *
- * Backend for the public API; read headers and full objects
- * from the ODB. Write raw data to the ODB.
- *
- ***********************************************************/
-
-static int open_alternates(git_odb *db)
-{
- unsigned n = 0;
-
- gitlck_lock(&db->lock);
- if (db->alternates) {
- gitlck_unlock(&db->lock);
- return 1;
- }
-
- /*
- * FIXME: broken, makes no sense.
- * n is always 0, the alternates array is always
- * empty!
- */
-
- db->alternates = git__malloc(sizeof(*db->alternates) * (n + 1));
- if (!db->alternates) {
- gitlck_unlock(&db->lock);
- return -1;
- }
-
- db->alternates[n] = NULL;
- db->n_alternates = n;
- gitlck_unlock(&db->lock);
- return 0;
}
-static int locate_object(obj_location *location, git_odb *db, const git_oid *id)
-{
- memset(location, 0x0, sizeof(obj_location));
-
- do {
- if (object_file_name(location->loose_path, GIT_PATH_MAX, db->objects_dir, id) == GIT_SUCCESS &&
- gitfo_exists(location->loose_path) == 0)
- return OBJ_LOCATION_LOOSE;
-
- if (search_packs(&location->pack.ptr, &location->pack.n, db, id) == GIT_SUCCESS)
- return OBJ_LOCATION_INPACK;
-
- } while (open_alternates(db) == GIT_SUCCESS);
-
- return GIT_ENOTFOUND;
-}
-
-static int read_packed(git_rawobj *out, const obj_location *loc)
-{
- index_entry e;
- int res;
-
- assert(out && loc);
-
- if (pack_openidx(loc->pack.ptr))
- return GIT_EPACKCORRUPTED;
-
- res = loc->pack.ptr->idx_get(&e, loc->pack.ptr, loc->pack.n);
-
- if (!res)
- res = unpack_object(out, loc->pack.ptr, &e);
-
- pack_decidx(loc->pack.ptr);
-
- return res;
-}
-
-static int read_header_packed(git_rawobj *out, const obj_location *loc)
-{
- git_pack *pack;
- index_entry e;
- int error = GIT_SUCCESS, shift;
- uint8_t *buffer, byte;
-
- assert(out && loc);
-
- pack = loc->pack.ptr;
-
- if (pack_openidx(pack))
- return GIT_EPACKCORRUPTED;
-
- if (pack->idx_get(&e, pack, loc->pack.n) < 0 ||
- open_pack(pack) < 0) {
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
-
- buffer = (uint8_t *)pack->pack_map.data + e.offset;
-
- byte = *buffer++ & 0xFF;
- out->type = (byte >> 4) & 0x7;
- out->len = byte & 0xF;
- shift = 4;
-
- while (byte & 0x80) {
- byte = *buffer++ & 0xFF;
- out->len += (byte & 0x7F) << shift;
- shift += 7;
- }
-
- /*
- * FIXME: if the object is not packed as a whole,
- * we need to do a full load and apply the deltas before
- * being able to read the header.
- *
- * I don't think there are any workarounds for this.'
- */
-
- if (out->type == GIT_OBJ_OFS_DELTA || out->type == GIT_OBJ_REF_DELTA) {
- error = unpack_object(out, pack, &e);
- git_obj_close(out);
- }
-
-cleanup:
- pack_decidx(loc->pack.ptr);
- return error;
-}
-
-static int read_loose(git_rawobj *out, git_odb *db, const obj_location *loc)
+int git_odb_open(git_odb **out, const char *objects_dir)
{
+ git_odb *db;
+ git_odb_backend *loose, *packed;
int error;
- gitfo_buf obj = GITFO_BUF_INIT;
-
- assert(out && db && loc);
-
- out->data = NULL;
- out->len = 0;
- out->type = GIT_OBJ_BAD;
-
- if (gitfo_read_file(&obj, loc->loose_path) < 0)
- return GIT_ENOTFOUND;
-
- error = inflate_disk_obj(out, &obj);
- gitfo_free_buf(&obj);
-
- return error;
-}
-
-static int read_header_loose(git_rawobj *out, git_odb *db, const obj_location *loc)
-{
- int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes;
- git_file fd;
- z_stream zs;
- obj_hdr header_obj;
- unsigned char raw_buffer[16], inflated_buffer[64];
-
- assert(out && db && loc);
-
- out->data = NULL;
-
- if ((fd = gitfo_open(loc->loose_path, O_RDONLY)) < 0)
- return GIT_ENOTFOUND;
-
- init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
- if (inflateInit(&zs) < Z_OK) {
- error = GIT_EZLIB;
- goto cleanup;
- }
-
- do {
- if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
- set_stream_input(&zs, raw_buffer, read_bytes);
- z_return = inflate(&zs, 0);
- }
- } while (z_return == Z_OK);
-
- if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
- || get_object_header(&header_obj, inflated_buffer) == 0
- || git_obj__loose_object_type(header_obj.type) == 0) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
-
- out->len = header_obj.size;
- out->type = header_obj.type;
-
-cleanup:
- finish_inflate(&zs);
- gitfo_close(fd);
- return error;
-}
-
-static int write_obj(gitfo_buf *buf, git_oid *id, git_odb *db)
-{
- char file[GIT_PATH_MAX];
- char temp[GIT_PATH_MAX];
- git_file fd;
-
- if (object_file_name(file, sizeof(file), db->objects_dir, id))
- return GIT_EOSERR;
-
- if (make_temp_file(&fd, temp, sizeof(temp), file) < 0)
- return GIT_EOSERR;
+ if ((error = git_odb_new(&db)) < 0)
+ return error;
- if (gitfo_write(fd, buf->data, buf->len) < 0) {
- gitfo_close(fd);
- gitfo_unlink(temp);
- return GIT_EOSERR;
+ /* add the loose object backend */
+ if (git_odb_backend_loose(&loose, objects_dir) == 0) {
+ error = git_odb_add_backend(db, loose);
+ if (error < 0)
+ goto cleanup;
}
- if (db->fsync_object_files)
- gitfo_fsync(fd);
- gitfo_close(fd);
- gitfo_chmod(temp, 0444);
-
- if (gitfo_move_file(temp, file) < 0) {
- gitfo_unlink(temp);
- return GIT_EOSERR;
+ /* add the packed file backend */
+ if (git_odb_backend_pack(&packed, objects_dir) == 0) {
+ error = git_odb_add_backend(db, packed);
+ if (error < 0)
+ goto cleanup;
}
- return GIT_SUCCESS;
-}
-
-
-
-
-
-
-/***********************************************************
- *
- * OBJECT DATABASE PUBLIC API
- *
- * Public calls for the ODB functionality
- *
- ***********************************************************/
-
-int git_odb_open(git_odb **out, const char *objects_dir)
-{
- git_odb *db = git__calloc(1, sizeof(*db));
- if (!db)
- return GIT_ENOMEM;
-
- db->objects_dir = git__strdup(objects_dir);
- if (!db->objects_dir) {
- free(db);
- return GIT_ENOMEM;
- }
-
- gitlck_init(&db->lock);
-
- db->object_zlib_level = Z_BEST_SPEED;
- db->fsync_object_files = 0;
+ /* TODO: add altenernates as new backends;
+ * how elevant is that? very elegant. */
*out = db;
return GIT_SUCCESS;
+
+cleanup:
+ git_odb_close(db);
+ return error;
}
void git_odb_close(git_odb *db)
{
- git_packlist *pl;
-
- if (!db)
- return;
+ unsigned int i;
- gitlck_lock(&db->lock);
+ assert(db);
- pl = db->packlist;
- db->packlist = NULL;
+ for (i = 0; i < db->backends.length; ++i) {
+ git_odb_backend *b = git_vector_get(&db->backends, i);
- if (db->alternates) {
- git_odb **alt;
- for (alt = db->alternates; *alt; alt++)
- git_odb_close(*alt);
- free(db->alternates);
+ if (b->free) b->free(b);
+ else free(b);
}
- free(db->objects_dir);
-
- gitlck_unlock(&db->lock);
- if (pl)
- packlist_dec(db, pl);
- gitlck_free(&db->lock);
+ git_vector_free(&db->backends);
free(db);
}
-int git_odb__read_packed(git_rawobj *out, git_odb *db, const git_oid *id)
+int git_odb_exists(git_odb *db, const git_oid *id)
{
- obj_location loc;
+ unsigned int i;
+ int found = 0;
- if (locate_object(&loc, db, id) != OBJ_LOCATION_INPACK)
- return GIT_ENOTFOUND;
+ assert(db && id);
- return read_packed(out, &loc);
-}
+ for (i = 0; i < db->backends.length && !found; ++i) {
+ git_odb_backend *b = git_vector_get(&db->backends, i);
-int git_odb__read_loose(git_rawobj *out, git_odb *db, const git_oid *id)
-{
- obj_location loc;
-
- if (locate_object(&loc, db, id) != OBJ_LOCATION_LOOSE)
- return GIT_ENOTFOUND;
+ if (b->exists != NULL)
+ found = b->exists(b, id);
+ }
- return read_loose(out, db, &loc);
-}
-
-int git_odb_exists(git_odb *db, const git_oid *id)
-{
- obj_location loc;
- assert(db && id);
- return locate_object(&loc, db, id) != OBJ_LOCATION_NOTFOUND;
+ return found;
}
int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id)
{
- obj_location loc;
- int found, error = 0;
-
- assert(out && db);
+ unsigned int i;
+ int error = GIT_ENOTFOUND;
- found = locate_object(&loc, db, id);
+ assert(out && db && id);
- switch (found) {
- case OBJ_LOCATION_LOOSE:
- error = read_header_loose(out, db, &loc);
- break;
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ git_odb_backend *b = git_vector_get(&db->backends, i);
- case OBJ_LOCATION_INPACK:
- error = read_header_packed(out, &loc);
- break;
+ if (b->read_header != NULL)
+ error = b->read_header(out, b, id);
+ }
- case OBJ_LOCATION_NOTFOUND:
- error = GIT_ENOTFOUND;
- break;
+ /*
+ * no backend could read only the header.
+ * try reading the whole object and freeing the contents
+ */
+ if (error < 0) {
+ error = git_odb_read(out, db, id);
+ git_rawobj_close(out);
}
return error;
}
-int git_odb_read(
- git_rawobj *out,
- git_odb *db,
- const git_oid *id)
+int git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id)
{
- obj_location loc;
- int found, error = 0;
+ unsigned int i;
+ int error = GIT_ENOTFOUND;
- assert(out && db);
+ assert(out && db && id);
- found = locate_object(&loc, db, id);
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ git_odb_backend *b = git_vector_get(&db->backends, i);
- switch (found) {
- case OBJ_LOCATION_LOOSE:
- error = read_loose(out, db, &loc);
- break;
-
- case OBJ_LOCATION_INPACK:
- error = read_packed(out, &loc);
- break;
-
- case OBJ_LOCATION_NOTFOUND:
- error = GIT_ENOTFOUND;
- break;
+ assert(b->read != NULL);
+ error = b->read(out, b, id);
}
return error;
@@ -1893,29 +324,18 @@ int git_odb_read(
int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj)
{
- char hdr[64];
- int hdrlen;
- gitfo_buf buf = GITFO_BUF_INIT;
- int error;
-
- assert(id && db && obj);
+ unsigned int i;
+ int error = GIT_ERROR;
- if ((error = hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj)) < 0)
- return error;
+ assert(obj && db && id);
- if (git_odb_exists(db, id))
- return GIT_SUCCESS;
-
- if ((error = deflate_obj(&buf, hdr, hdrlen, obj, db->object_zlib_level)) < 0)
- return error;
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ git_odb_backend *b = git_vector_get(&db->backends, i);
- if ((error = write_obj(&buf, id, db)) < 0) {
- gitfo_free_buf(&buf);
- return error;
+ if (b->write != NULL)
+ error = b->write(id, b, obj);
}
- gitfo_free_buf(&buf);
-
- return GIT_SUCCESS;
+ return error;
}
diff --git a/src/odb.h b/src/odb.h
index a45b803b8..2726cbb46 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -1,22 +1,49 @@
#ifndef INCLUDE_odb_h__
#define INCLUDE_odb_h__
-/** First 4 bytes of a pack-*.idx file header.
- *
- * Note this header exists only in idx v2 and later. The idx v1
- * file format does not have a magic sequence at the front, and
- * must be detected by the first four bytes *not* being this value
- * and the first 8 bytes matching the following expression:
- *
- * uint32_t *fanout = ... the file data at offset 0 ...
- * ntohl(fanout[0]) < ntohl(fanout[1])
- *
- * The value chosen here for PACK_TOC is such that the above
- * cannot be true for an idx v1 file.
- */
-#define PACK_TOC 0xff744f63 /* -1tOc */
-
-/** First 4 bytes of a pack-*.pack file header. */
-#define PACK_SIG 0x5041434b /* PACK */
+#include <git/odb.h>
+#include <git/oid.h>
+
+#include "vector.h"
+
+struct git_odb {
+ void *_internal;
+ git_vector backends;
+};
+
+struct git_odb_backend {
+ git_odb *odb;
+
+ int priority;
+
+ int (* read)(
+ git_rawobj *,
+ struct git_odb_backend *,
+ const git_oid *);
+
+ int (* read_header)(
+ git_rawobj *,
+ struct git_odb_backend *,
+ const git_oid *);
+
+ int (* write)(
+ git_oid *id,
+ struct git_odb_backend *,
+ git_rawobj *obj);
+
+ int (* exists)(
+ struct git_odb_backend *,
+ const git_oid *);
+
+ void (* free)(struct git_odb_backend *);
+
+};
+
+int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj);
+int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen);
+
+
+int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir);
+int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir);
#endif
diff --git a/src/odb_loose.c b/src/odb_loose.c
new file mode 100644
index 000000000..52430467b
--- /dev/null
+++ b/src/odb_loose.c
@@ -0,0 +1,658 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "git/zlib.h"
+#include "fileops.h"
+#include "hash.h"
+#include "odb.h"
+#include "delta-apply.h"
+
+
+typedef struct { /* object header data */
+ git_otype type; /* object type */
+ size_t size; /* object size */
+} obj_hdr;
+
+typedef struct loose_backend {
+ git_odb_backend parent;
+
+ int object_zlib_level; /** loose object zlib compression level. */
+ int fsync_object_files; /** loose object file fsync flag. */
+ char *objects_dir;
+} loose_backend;
+
+
+/***********************************************************
+ *
+ * MISCELANEOUS HELPER FUNCTIONS
+ *
+ ***********************************************************/
+
+static int make_temp_file(git_file *fd, char *tmp, size_t n, char *file)
+{
+ char *template = "/tmp_obj_XXXXXX";
+ size_t tmplen = strlen(template);
+ int dirlen;
+
+ if ((dirlen = git__dirname(tmp, n, file)) < 0)
+ return GIT_ERROR;
+
+ if ((dirlen + tmplen) >= n)
+ return GIT_ERROR;
+
+ strcpy(tmp + dirlen, (dirlen) ? template : template + 1);
+
+ *fd = gitfo_mkstemp(tmp);
+ if (*fd < 0 && dirlen) {
+ /* create directory if it doesn't exist */
+ tmp[dirlen] = '\0';
+ if ((gitfo_exists(tmp) < 0) && gitfo_mkdir(tmp, 0755))
+ return GIT_ERROR;
+ /* try again */
+ strcpy(tmp + dirlen, template);
+ *fd = gitfo_mkstemp(tmp);
+ }
+ if (*fd < 0)
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+}
+
+
+
+static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id)
+{
+ size_t len = strlen(dir);
+
+ /* check length: 43 = 40 hex sha1 chars + 2 * '/' + '\0' */
+ if (len+43 > n)
+ return len+43;
+
+ /* the object dir: eg $GIT_DIR/objects */
+ strcpy(name, dir);
+ if (name[len-1] != '/')
+ name[len++] = '/';
+
+ /* loose object filename: aa/aaa... (41 bytes) */
+ git_oid_pathfmt(&name[len], id);
+ name[len+41] = '\0';
+
+ return 0;
+}
+
+
+static size_t get_binary_object_header(obj_hdr *hdr, gitfo_buf *obj)
+{
+ unsigned char c;
+ unsigned char *data = obj->data;
+ size_t shift, size, used = 0;
+
+ if (obj->len == 0)
+ return 0;
+
+ c = data[used++];
+ hdr->type = (c >> 4) & 7;
+
+ size = c & 15;
+ shift = 4;
+ while (c & 0x80) {
+ if (obj->len <= used)
+ return 0;
+ if (sizeof(size_t) * 8 <= shift)
+ return 0;
+ c = data[used++];
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+ hdr->size = size;
+
+ return used;
+}
+
+static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
+{
+ char c, typename[10];
+ size_t size, used = 0;
+
+ /*
+ * type name string followed by space.
+ */
+ while ((c = data[used]) != ' ') {
+ typename[used++] = c;
+ if (used >= sizeof(typename))
+ return 0;
+ }
+ typename[used] = 0;
+ if (used == 0)
+ return 0;
+ hdr->type = git_otype_fromstring(typename);
+ used++; /* consume the space */
+
+ /*
+ * length follows immediately in decimal (without
+ * leading zeros).
+ */
+ size = data[used++] - '0';
+ if (size > 9)
+ return 0;
+ if (size) {
+ while ((c = data[used]) != '\0') {
+ size_t d = c - '0';
+ if (d > 9)
+ break;
+ used++;
+ size = size * 10 + d;
+ }
+ }
+ hdr->size = size;
+
+ /*
+ * the length must be followed by a zero byte
+ */
+ if (data[used++] != '\0')
+ return 0;
+
+ return used;
+}
+
+
+
+/***********************************************************
+ *
+ * ZLIB RELATED FUNCTIONS
+ *
+ ***********************************************************/
+
+static void init_stream(z_stream *s, void *out, size_t len)
+{
+ memset(s, 0, sizeof(*s));
+ s->next_out = out;
+ s->avail_out = len;
+}
+
+static void set_stream_input(z_stream *s, void *in, size_t len)
+{
+ s->next_in = in;
+ s->avail_in = len;
+}
+
+static void set_stream_output(z_stream *s, void *out, size_t len)
+{
+ s->next_out = out;
+ s->avail_out = len;
+}
+
+
+static int start_inflate(z_stream *s, gitfo_buf *obj, void *out, size_t len)
+{
+ int status;
+
+ init_stream(s, out, len);
+ set_stream_input(s, obj->data, obj->len);
+
+ if ((status = inflateInit(s)) < Z_OK)
+ return status;
+
+ return inflate(s, 0);
+}
+
+static int finish_inflate(z_stream *s)
+{
+ int status = Z_OK;
+
+ while (status == Z_OK)
+ status = inflate(s, Z_FINISH);
+
+ inflateEnd(s);
+
+ if ((status != Z_STREAM_END) || (s->avail_in != 0))
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+}
+
+static int deflate_buf(z_stream *s, void *in, size_t len, int flush)
+{
+ int status = Z_OK;
+
+ set_stream_input(s, in, len);
+ while (status == Z_OK) {
+ status = deflate(s, flush);
+ if (s->avail_in == 0)
+ break;
+ }
+ return status;
+}
+
+static int deflate_obj(gitfo_buf *buf, char *hdr, int hdrlen, git_rawobj *obj, int level)
+{
+ z_stream zs;
+ int status;
+ size_t size;
+
+ assert(buf && !buf->data && hdr && obj);
+ assert(level == Z_DEFAULT_COMPRESSION || (level >= 0 && level <= 9));
+
+ buf->data = NULL;
+ buf->len = 0;
+ init_stream(&zs, NULL, 0);
+
+ if (deflateInit(&zs, level) < Z_OK)
+ return GIT_ERROR;
+
+ size = deflateBound(&zs, hdrlen + obj->len);
+
+ if ((buf->data = git__malloc(size)) == NULL) {
+ deflateEnd(&zs);
+ return GIT_ERROR;
+ }
+
+ set_stream_output(&zs, buf->data, size);
+
+ /* compress the header */
+ status = deflate_buf(&zs, hdr, hdrlen, Z_NO_FLUSH);
+
+ /* if header compressed OK, compress the object */
+ if (status == Z_OK)
+ status = deflate_buf(&zs, obj->data, obj->len, Z_FINISH);
+
+ if (status != Z_STREAM_END) {
+ deflateEnd(&zs);
+ free(buf->data);
+ buf->data = NULL;
+ return GIT_ERROR;
+ }
+
+ buf->len = zs.total_out;
+ deflateEnd(&zs);
+
+ return GIT_SUCCESS;
+}
+
+static int is_zlib_compressed_data(unsigned char *data)
+{
+ unsigned int w;
+
+ w = ((unsigned int)(data[0]) << 8) + data[1];
+ return data[0] == 0x78 && !(w % 31);
+}
+
+static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
+{
+ unsigned char *buf, *head = hb;
+ size_t tail;
+
+ /*
+ * allocate a buffer to hold the inflated data and copy the
+ * initial sequence of inflated data from the tail of the
+ * head buffer, if any.
+ */
+ if ((buf = git__malloc(hdr->size + 1)) == NULL) {
+ inflateEnd(s);
+ return NULL;
+ }
+ tail = s->total_out - used;
+ if (used > 0 && tail > 0) {
+ if (tail > hdr->size)
+ tail = hdr->size;
+ memcpy(buf, head + used, tail);
+ }
+ used = tail;
+
+ /*
+ * inflate the remainder of the object data, if any
+ */
+ if (hdr->size < used)
+ inflateEnd(s);
+ else {
+ set_stream_output(s, buf + used, hdr->size - used);
+ if (finish_inflate(s)) {
+ free(buf);
+ return NULL;
+ }
+ }
+
+ return buf;
+}
+
+/*
+ * At one point, there was a loose object format that was intended to
+ * mimic the format used in pack-files. This was to allow easy copying
+ * of loose object data into packs. This format is no longer used, but
+ * we must still read it.
+ */
+static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj)
+{
+ unsigned char *in, *buf;
+ obj_hdr hdr;
+ size_t len, used;
+
+ /*
+ * read the object header, which is an (uncompressed)
+ * binary encoding of the object type and size.
+ */
+ if ((used = get_binary_object_header(&hdr, obj)) == 0)
+ return GIT_ERROR;
+
+ if (!git_otype_is_loose(hdr.type))
+ return GIT_ERROR;
+
+ /*
+ * allocate a buffer and inflate the data into it
+ */
+ buf = git__malloc(hdr.size + 1);
+ if (!buf)
+ return GIT_ERROR;
+
+ in = ((unsigned char *)obj->data) + used;
+ len = obj->len - used;
+ if (git_odb__inflate_buffer(in, len, buf, hdr.size)) {
+ free(buf);
+ return GIT_ERROR;
+ }
+ buf[hdr.size] = '\0';
+
+ out->data = buf;
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+ return GIT_SUCCESS;
+}
+
+static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj)
+{
+ unsigned char head[64], *buf;
+ z_stream zs;
+ int z_status;
+ obj_hdr hdr;
+ size_t used;
+
+ /*
+ * check for a pack-like loose object
+ */
+ if (!is_zlib_compressed_data(obj->data))
+ return inflate_packlike_loose_disk_obj(out, obj);
+
+ /*
+ * inflate the initial part of the io buffer in order
+ * to parse the object header (type and size).
+ */
+ if ((z_status = start_inflate(&zs, obj, head, sizeof(head))) < Z_OK)
+ return GIT_ERROR;
+
+ if ((used = get_object_header(&hdr, head)) == 0)
+ return GIT_ERROR;
+
+ if (!git_otype_is_loose(hdr.type))
+ return GIT_ERROR;
+
+ /*
+ * allocate a buffer and inflate the object data into it
+ * (including the initial sequence in the head buffer).
+ */
+ if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
+ return GIT_ERROR;
+ buf[hdr.size] = '\0';
+
+ out->data = buf;
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+ return GIT_SUCCESS;
+}
+
+
+
+
+
+
+/***********************************************************
+ *
+ * ODB OBJECT READING & WRITING
+ *
+ * Backend for the public API; read headers and full objects
+ * from the ODB. Write raw data to the ODB.
+ *
+ ***********************************************************/
+
+static int read_loose(git_rawobj *out, const char *loc)
+{
+ int error;
+ gitfo_buf obj = GITFO_BUF_INIT;
+
+ assert(out && loc);
+
+ out->data = NULL;
+ out->len = 0;
+ out->type = GIT_OBJ_BAD;
+
+ if (gitfo_read_file(&obj, loc) < 0)
+ return GIT_ENOTFOUND;
+
+ error = inflate_disk_obj(out, &obj);
+ gitfo_free_buf(&obj);
+
+ return error;
+}
+
+static int read_header_loose(git_rawobj *out, const char *loc)
+{
+ int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes;
+ git_file fd;
+ z_stream zs;
+ obj_hdr header_obj;
+ unsigned char raw_buffer[16], inflated_buffer[64];
+
+ assert(out && loc);
+
+ out->data = NULL;
+
+ if ((fd = gitfo_open(loc, O_RDONLY)) < 0)
+ return GIT_ENOTFOUND;
+
+ init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
+
+ if (inflateInit(&zs) < Z_OK) {
+ error = GIT_EZLIB;
+ goto cleanup;
+ }
+
+ do {
+ if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
+ set_stream_input(&zs, raw_buffer, read_bytes);
+ z_return = inflate(&zs, 0);
+ }
+ } while (z_return == Z_OK);
+
+ if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
+ || get_object_header(&header_obj, inflated_buffer) == 0
+ || git_otype_is_loose(header_obj.type) == 0) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
+
+ out->len = header_obj.size;
+ out->type = header_obj.type;
+
+cleanup:
+ finish_inflate(&zs);
+ gitfo_close(fd);
+ return error;
+}
+
+static int write_obj(gitfo_buf *buf, git_oid *id, loose_backend *backend)
+{
+ char file[GIT_PATH_MAX];
+ char temp[GIT_PATH_MAX];
+ git_file fd;
+
+ if (object_file_name(file, sizeof(file), backend->objects_dir, id))
+ return GIT_EOSERR;
+
+ if (make_temp_file(&fd, temp, sizeof(temp), file) < 0)
+ return GIT_EOSERR;
+
+ if (gitfo_write(fd, buf->data, buf->len) < 0) {
+ gitfo_close(fd);
+ gitfo_unlink(temp);
+ return GIT_EOSERR;
+ }
+
+ if (backend->fsync_object_files)
+ gitfo_fsync(fd);
+ gitfo_close(fd);
+ gitfo_chmod(temp, 0444);
+
+ if (gitfo_move_file(temp, file) < 0) {
+ gitfo_unlink(temp);
+ return GIT_EOSERR;
+ }
+
+ return GIT_SUCCESS;
+}
+
+static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid)
+{
+ object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid);
+ return gitfo_exists(object_location);
+}
+
+
+
+
+
+
+
+
+
+/***********************************************************
+ *
+ * LOOSE BACKEND PUBLIC API
+ *
+ * Implement the git_odb_backend API calls
+ *
+ ***********************************************************/
+
+int loose_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
+{
+ char object_path[GIT_PATH_MAX];
+
+ assert(obj && backend && oid);
+
+ if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
+ return GIT_ENOTFOUND;
+
+ return read_header_loose(obj, object_path);
+}
+
+
+int loose_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
+{
+ char object_path[GIT_PATH_MAX];
+
+ assert(obj && backend && oid);
+
+ if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
+ return GIT_ENOTFOUND;
+
+ return read_loose(obj, object_path);
+}
+
+int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ char object_path[GIT_PATH_MAX];
+
+ assert(backend && oid);
+
+ return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS;
+}
+
+
+int loose_backend__write(git_oid *id, git_odb_backend *_backend, git_rawobj *obj)
+{
+ char hdr[64];
+ int hdrlen;
+ gitfo_buf buf = GITFO_BUF_INIT;
+ int error;
+ loose_backend *backend;
+
+ assert(id && _backend && obj);
+
+ backend = (loose_backend *)_backend;
+
+ if ((error = git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj)) < 0)
+ return error;
+
+ if (git_odb_exists(_backend->odb, id))
+ return GIT_SUCCESS;
+
+ if ((error = deflate_obj(&buf, hdr, hdrlen, obj, backend->object_zlib_level)) < 0)
+ return error;
+
+ error = write_obj(&buf, id, backend);
+
+ gitfo_free_buf(&buf);
+ return error;
+}
+
+void loose_backend__free(git_odb_backend *_backend)
+{
+ loose_backend *backend;
+ assert(_backend);
+ backend = (loose_backend *)_backend;
+
+ free(backend->objects_dir);
+ free(backend);
+}
+
+int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir)
+{
+ loose_backend *backend;
+
+ backend = git__calloc(1, sizeof(loose_backend));
+ if (backend == NULL)
+ return GIT_ENOMEM;
+
+ backend->objects_dir = git__strdup(objects_dir);
+ if (backend->objects_dir == NULL) {
+ free(backend);
+ return GIT_ENOMEM;
+ }
+
+ backend->object_zlib_level = Z_BEST_SPEED;
+ backend->fsync_object_files = 0;
+
+ backend->parent.read = &loose_backend__read;
+ backend->parent.read_header = &loose_backend__read_header;
+ backend->parent.write = &loose_backend__write;
+ backend->parent.exists = &loose_backend__exists;
+ backend->parent.free = &loose_backend__free;
+
+ backend->parent.priority = 2; /* higher than packfiles */
+
+ *backend_out = (git_odb_backend *)backend;
+ return GIT_SUCCESS;
+}
diff --git a/src/odb_pack.c b/src/odb_pack.c
new file mode 100644
index 000000000..f97e216c5
--- /dev/null
+++ b/src/odb_pack.c
@@ -0,0 +1,1207 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "git/zlib.h"
+#include "fileops.h"
+#include "hash.h"
+#include "odb.h"
+#include "delta-apply.h"
+
+/** First 4 bytes of a pack-*.idx file header.
+ *
+ * Note this header exists only in idx v2 and later. The idx v1
+ * file format does not have a magic sequence at the front, and
+ * must be detected by the first four bytes *not* being this value
+ * and the first 8 bytes matching the following expression:
+ *
+ * uint32_t *fanout = ... the file data at offset 0 ...
+ * ntohl(fanout[0]) < ntohl(fanout[1])
+ *
+ * The value chosen here for PACK_TOC is such that the above
+ * cannot be true for an idx v1 file.
+ */
+#define PACK_TOC 0xff744f63 /* -1tOc */
+
+/** First 4 bytes of a pack-*.pack file header. */
+#define PACK_SIG 0x5041434b /* PACK */
+
+#define GIT_PACK_NAME_MAX (5 + 40 + 1)
+
+struct pack_backend;
+
+typedef struct {
+ uint32_t n;
+ unsigned char *oid;
+ off_t offset;
+ off_t size;
+} index_entry;
+
+typedef struct { /* '.pack' file header */
+ uint32_t sig; /* PACK_SIG */
+ uint32_t ver; /* pack version */
+ uint32_t cnt; /* object count */
+} pack_hdr;
+
+typedef struct git_pack {
+ struct pack_backend *backend;
+ git_lck lock;
+
+ /** Functions to access idx_map. */
+ int (*idx_search)(
+ uint32_t *,
+ struct git_pack *,
+ const git_oid *);
+ int (*idx_search_offset)(
+ uint32_t *,
+ struct git_pack *,
+ off_t);
+ int (*idx_get)(
+ index_entry *,
+ struct git_pack *,
+ uint32_t n);
+
+ /** The .idx file, mapped into memory. */
+ git_file idx_fd;
+ git_map idx_map;
+ uint32_t *im_fanout;
+ unsigned char *im_oid;
+ uint32_t *im_crc;
+ uint32_t *im_offset32;
+ uint32_t *im_offset64;
+ uint32_t *im_off_idx;
+ uint32_t *im_off_next;
+
+ /** Number of objects in this pack. */
+ uint32_t obj_cnt;
+
+ /** File descriptor for the .pack file. */
+ git_file pack_fd;
+
+ /** Memory map of the pack's contents */
+ git_map pack_map;
+
+ /** The size of the .pack file. */
+ off_t pack_size;
+
+ /** The mtime of the .pack file. */
+ time_t pack_mtime;
+
+ /** Number of git_packlist we appear in. */
+ unsigned int refcnt;
+
+ /** Number of active users of the idx_map data. */
+ unsigned int idxcnt;
+ unsigned
+ invalid:1 /* the pack is unable to be read by libgit2 */
+ ;
+
+ /** Name of the pack file(s), without extension ("pack-abc"). */
+ char pack_name[GIT_PACK_NAME_MAX];
+} git_pack;
+
+typedef struct {
+ size_t n_packs;
+ unsigned int refcnt;
+ git_pack *packs[GIT_FLEX_ARRAY];
+} git_packlist;
+
+typedef struct pack_backend {
+ git_odb_backend parent;
+
+ git_lck lock;
+ char *objects_dir;
+ git_packlist *packlist;
+} pack_backend;
+
+
+typedef struct pack_location {
+ git_pack *ptr;
+ uint32_t n;
+} pack_location;
+
+static int pack_stat(git_pack *p);
+static int pack_openidx(git_pack *p);
+static void pack_decidx(git_pack *p);
+static int read_pack_hdr(pack_hdr *out, git_file fd);
+static int check_pack_hdr(git_pack *p);
+static int check_pack_sha1(git_pack *p);
+static int open_pack(git_pack *p);
+
+
+static int pack_openidx_map(git_pack *p);
+static int pack_openidx_v1(git_pack *p);
+static int pack_openidx_v2(git_pack *p);
+
+
+GIT_INLINE(uint32_t) decode32(void *b)
+{
+ return ntohl(*((uint32_t *)b));
+}
+
+GIT_INLINE(uint64_t) decode64(void *b)
+{
+ uint32_t *p = b;
+ return (((uint64_t)ntohl(p[0])) << 32) | ntohl(p[1]);
+}
+
+
+
+/***********************************************************
+ *
+ * PACKFILE FUNCTIONS
+ *
+ * Locate, open and access the contents of a packfile
+ *
+ ***********************************************************/
+
+static int pack_stat(git_pack *p)
+{
+ char pb[GIT_PATH_MAX];
+ struct stat sb;
+
+ if (git__fmt(pb, sizeof(pb), "%s/pack/%s.pack",
+ p->backend->objects_dir,
+ p->pack_name) < 0)
+ return GIT_ERROR;
+
+ if (gitfo_stat(pb, &sb) || !S_ISREG(sb.st_mode))
+ return GIT_ERROR;
+
+ if (sb.st_size < (3 * 4 + GIT_OID_RAWSZ))
+ return GIT_ERROR;
+
+ p->pack_size = sb.st_size;
+ p->pack_mtime = sb.st_mtime;
+
+ return GIT_SUCCESS;
+}
+
+static int pack_openidx(git_pack *p)
+{
+ gitlck_lock(&p->lock);
+
+ if (p->invalid) {
+ gitlck_unlock(&p->lock);
+ return GIT_ERROR;
+ }
+
+ if (++p->idxcnt == 1 && !p->idx_search) {
+ int status, version;
+ uint32_t *data;
+
+ if (pack_stat(p) || pack_openidx_map(p)) {
+ p->invalid = 1;
+ p->idxcnt--;
+ gitlck_unlock(&p->lock);
+ return GIT_ERROR;
+ }
+ data = p->idx_map.data;
+ status = GIT_SUCCESS;
+ version = 1;
+
+ if (decode32(&data[0]) == PACK_TOC)
+ version = decode32(&data[1]);
+
+ switch (version) {
+ case 1:
+ status = pack_openidx_v1(p);
+ break;
+ case 2:
+ status = pack_openidx_v2(p);
+ break;
+ default:
+ status = GIT_ERROR;
+ }
+
+ if (status != GIT_SUCCESS) {
+ gitfo_free_map(&p->idx_map);
+ p->invalid = 1;
+ p->idxcnt--;
+ gitlck_unlock(&p->lock);
+ return status;
+ }
+ }
+
+ gitlck_unlock(&p->lock);
+ return GIT_SUCCESS;
+}
+
+static void pack_decidx(git_pack *p)
+{
+ gitlck_lock(&p->lock);
+ p->idxcnt--;
+ gitlck_unlock(&p->lock);
+}
+
+static int read_pack_hdr(pack_hdr *out, git_file fd)
+{
+ pack_hdr hdr;
+
+ if (gitfo_read(fd, &hdr, sizeof(hdr)))
+ return GIT_ERROR;
+
+ out->sig = decode32(&hdr.sig);
+ out->ver = decode32(&hdr.ver);
+ out->cnt = decode32(&hdr.cnt);
+
+ return GIT_SUCCESS;
+}
+
+static int check_pack_hdr(git_pack *p)
+{
+ pack_hdr hdr;
+
+ if (read_pack_hdr(&hdr, p->pack_fd))
+ return GIT_ERROR;
+
+ if (hdr.sig != PACK_SIG
+ || (hdr.ver != 2 && hdr.ver != 3)
+ || hdr.cnt != p->obj_cnt)
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+}
+
+static int check_pack_sha1(git_pack *p)
+{
+ unsigned char *data = p->idx_map.data;
+ off_t pack_sha1_off = p->pack_size - GIT_OID_RAWSZ;
+ size_t idx_pack_sha1_off = p->idx_map.len - 2 * GIT_OID_RAWSZ;
+ git_oid pack_id, idx_pack_id;
+
+ if (gitfo_lseek(p->pack_fd, pack_sha1_off, SEEK_SET) == -1)
+ return GIT_ERROR;
+
+ if (gitfo_read(p->pack_fd, pack_id.id, sizeof(pack_id.id)))
+ return GIT_ERROR;
+
+ git_oid_mkraw(&idx_pack_id, data + idx_pack_sha1_off);
+
+ if (git_oid_cmp(&pack_id, &idx_pack_id))
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+}
+
+static int open_pack(git_pack *p)
+{
+ char pb[GIT_PATH_MAX];
+ struct stat sb;
+
+ if (p->pack_fd != -1)
+ return GIT_SUCCESS;
+
+ if (git__fmt(pb, sizeof(pb), "%s/pack/%s.pack",
+ p->backend->objects_dir,
+ p->pack_name) < 0)
+ return GIT_ERROR;
+
+ if (pack_openidx(p))
+ return GIT_ERROR;
+
+ if ((p->pack_fd = gitfo_open(pb, O_RDONLY)) < 0)
+ goto error_cleanup;
+
+ if (gitfo_fstat(p->pack_fd, &sb)
+ || !S_ISREG(sb.st_mode) || p->pack_size != sb.st_size
+ || check_pack_hdr(p) || check_pack_sha1(p))
+ goto error_cleanup;
+
+ if (!git__is_sizet(p->pack_size) ||
+ gitfo_map_ro(&p->pack_map, p->pack_fd, 0, (size_t)p->pack_size) < 0)
+ goto error_cleanup;
+
+ pack_decidx(p);
+ return GIT_SUCCESS;
+
+error_cleanup:
+ gitfo_close(p->pack_fd);
+ p->pack_fd = -1;
+ pack_decidx(p);
+ return GIT_ERROR;
+}
+
+static void pack_dec(git_pack *p)
+{
+ int need_free;
+
+ gitlck_lock(&p->lock);
+ need_free = !--p->refcnt;
+ gitlck_unlock(&p->lock);
+
+ if (need_free) {
+ if (p->idx_search) {
+ gitfo_free_map(&p->idx_map);
+ gitfo_close(p->idx_fd);
+ free(p->im_fanout);
+ free(p->im_off_idx);
+ free(p->im_off_next);
+ if (p->pack_fd != -1) {
+ gitfo_close(p->pack_fd);
+ gitfo_free_map(&p->pack_map);
+ }
+ }
+
+ gitlck_free(&p->lock);
+ free(p);
+ }
+}
+
+static void packlist_dec(pack_backend *backend, git_packlist *pl)
+{
+ int need_free;
+
+ assert(backend && pl);
+
+ gitlck_lock(&backend->lock);
+ need_free = !--pl->refcnt;
+ gitlck_unlock(&backend->lock);
+
+ if (need_free) {
+ size_t j;
+ for (j = 0; j < pl->n_packs; j++)
+ pack_dec(pl->packs[j]);
+ free(pl);
+ }
+}
+
+static git_pack *alloc_pack(const char *pack_name)
+{
+ git_pack *p = git__calloc(1, sizeof(*p));
+ if (!p)
+ return NULL;
+
+ gitlck_init(&p->lock);
+ strcpy(p->pack_name, pack_name);
+ p->refcnt = 1;
+ p->pack_fd = -1;
+ return p;
+}
+
+struct scanned_pack {
+ struct scanned_pack *next;
+ git_pack *pack;
+};
+
+static int scan_one_pack(void *state, char *name)
+{
+ struct scanned_pack **ret = state, *r;
+ char *s = strrchr(name, '/'), *d;
+
+ if (git__prefixcmp(s + 1, "pack-")
+ || git__suffixcmp(s, ".pack")
+ || strlen(s + 1) != GIT_PACK_NAME_MAX + 4)
+ return 0;
+
+ d = strrchr(s + 1, '.');
+ strcpy(d + 1, "idx"); /* "pack-abc.pack" -> "pack-abc.idx" */
+ if (gitfo_exists(name))
+ return 0;
+
+ if ((r = git__malloc(sizeof(*r))) == NULL)
+ return GIT_ERROR;
+
+ *d = '\0'; /* "pack-abc.pack" -_> "pack-abc" */
+ if ((r->pack = alloc_pack(s + 1)) == NULL) {
+ free(r);
+ return GIT_ERROR;
+ }
+
+ r->next = *ret;
+ *ret = r;
+ return 0;
+}
+
+static git_packlist *scan_packs(pack_backend *backend)
+{
+ char pb[GIT_PATH_MAX];
+ struct scanned_pack *state = NULL, *c;
+ size_t cnt;
+ git_packlist *new_list;
+
+ if (git__fmt(pb, sizeof(pb), "%s/pack", backend->objects_dir) < 0)
+ return NULL;
+ gitfo_dirent(pb, sizeof(pb), scan_one_pack, &state);
+
+ /* TODO - merge old entries into the new array */
+ for (cnt = 0, c = state; c; c = c->next)
+ cnt++;
+ new_list = git__malloc(sizeof(*new_list)
+ + (sizeof(new_list->packs[0]) * cnt));
+ if (!new_list)
+ goto fail;
+
+ for (cnt = 0, c = state; c; ) {
+ struct scanned_pack *n = c->next;
+ c->pack->backend = backend;
+ new_list->packs[cnt++] = c->pack;
+ free(c);
+ c = n;
+ }
+ new_list->n_packs = cnt;
+ new_list->refcnt = 2;
+ backend->packlist = new_list;
+ return new_list;
+
+fail:
+ while (state) {
+ struct scanned_pack *n = state->next;
+ pack_dec(state->pack);
+ free(state);
+ state = n;
+ }
+ return NULL;
+}
+
+static git_packlist *packlist_get(pack_backend *backend)
+{
+ git_packlist *pl;
+
+ gitlck_lock(&backend->lock);
+ if ((pl = backend->packlist) != NULL)
+ pl->refcnt++;
+ else
+ pl = scan_packs(backend);
+ gitlck_unlock(&backend->lock);
+ return pl;
+}
+
+static int locate_packfile(pack_location *location, pack_backend *backend, const git_oid *id)
+{
+ git_packlist *pl = packlist_get(backend);
+ size_t j;
+
+ if (!pl)
+ return GIT_ENOTFOUND;
+
+ for (j = 0; j < pl->n_packs; j++) {
+
+ git_pack *pack = pl->packs[j];
+ uint32_t pos;
+ int res;
+
+ if (pack_openidx(pack))
+ continue;
+
+ res = pack->idx_search(&pos, pack, id);
+ pack_decidx(pack);
+
+ if (!res) {
+ packlist_dec(backend, pl);
+
+ location->ptr = pack;
+ location->n = pos;
+
+ return GIT_SUCCESS;
+ }
+
+ }
+
+ packlist_dec(backend, pl);
+ return GIT_ENOTFOUND;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***********************************************************
+ *
+ * PACKFILE INDEX FUNCTIONS
+ *
+ * Get index formation for packfile indexes v1 and v2
+ *
+ ***********************************************************/
+
+static int pack_openidx_map(git_pack *p)
+{
+ char pb[GIT_PATH_MAX];
+ off_t len;
+
+ if (git__fmt(pb, sizeof(pb), "%s/pack/%s.idx",
+ p->backend->objects_dir,
+ p->pack_name) < 0)
+ return GIT_ERROR;
+
+ if ((p->idx_fd = gitfo_open(pb, O_RDONLY)) < 0)
+ return GIT_ERROR;
+
+ if ((len = gitfo_size(p->idx_fd)) < 0
+ || !git__is_sizet(len)
+ || gitfo_map_ro(&p->idx_map, p->idx_fd, 0, (size_t)len)) {
+ gitfo_close(p->idx_fd);
+ return GIT_ERROR;
+ }
+
+ return GIT_SUCCESS;
+}
+
+typedef struct {
+ off_t offset;
+ uint32_t n;
+} offset_idx_info;
+
+static int cmp_offset_idx_info(const void *lhs, const void *rhs)
+{
+ const offset_idx_info *a = lhs;
+ const offset_idx_info *b = rhs;
+ return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
+}
+
+static int make_offset_index(git_pack *p, offset_idx_info *data)
+{
+ off_t min_off = 3 * 4, max_off = p->pack_size - GIT_OID_RAWSZ;
+ uint32_t *idx, *next;
+ uint32_t j;
+
+ qsort(data, p->obj_cnt, sizeof(*data), cmp_offset_idx_info);
+
+ if (data[0].offset < min_off || data[p->obj_cnt].offset > max_off)
+ return GIT_ERROR;
+
+ if ((idx = git__malloc(sizeof(*idx) * (p->obj_cnt+1))) == NULL)
+ return GIT_ERROR;
+ if ((next = git__malloc(sizeof(*next) * p->obj_cnt)) == NULL) {
+ free(idx);
+ return GIT_ERROR;
+ }
+
+ for (j = 0; j < p->obj_cnt+1; j++)
+ idx[j] = data[j].n;
+
+ for (j = 0; j < p->obj_cnt; j++) {
+ assert(idx[j] < p->obj_cnt);
+ assert(idx[j+1] < p->obj_cnt+1);
+
+ next[idx[j]] = idx[j+1];
+ }
+
+ p->im_off_idx = idx;
+ p->im_off_next = next;
+ return GIT_SUCCESS;
+}
+
+static int idxv1_search(uint32_t *out, git_pack *p, const git_oid *id)
+{
+ unsigned char *data = p->im_oid;
+ uint32_t lo = id->id[0] ? p->im_fanout[id->id[0] - 1] : 0;
+ uint32_t hi = p->im_fanout[id->id[0]];
+
+ do {
+ uint32_t mid = (lo + hi) >> 1;
+ uint32_t pos = 24 * mid;
+ int cmp = memcmp(id->id, data + pos + 4, 20);
+ if (cmp < 0)
+ hi = mid;
+ else if (!cmp) {
+ *out = mid;
+ return GIT_SUCCESS;
+ } else
+ lo = mid + 1;
+ } while (lo < hi);
+ return GIT_ENOTFOUND;
+}
+
+static int idxv1_search_offset(uint32_t *out, git_pack *p, off_t offset)
+{
+ if (offset > 0 && offset < (p->pack_size - GIT_OID_RAWSZ)) {
+ uint32_t lo = 0, hi = p->obj_cnt+1;
+ unsigned char *data = p->im_oid;
+ uint32_t *idx = p->im_off_idx;
+ do {
+ uint32_t mid = (lo + hi) >> 1;
+ uint32_t n = idx[mid];
+ uint32_t pos = n * (GIT_OID_RAWSZ + 4);
+ off_t here = decode32(data + pos);
+ if (offset < here)
+ hi = mid;
+ else if (offset == here) {
+ *out = n;
+ return GIT_SUCCESS;
+ } else
+ lo = mid + 1;
+ } while (lo < hi);
+ }
+ return GIT_ENOTFOUND;
+}
+
+static int idxv1_get(index_entry *e, git_pack *p, uint32_t n)
+{
+ unsigned char *data = p->im_oid;
+ uint32_t *next = p->im_off_next;
+
+ if (n < p->obj_cnt) {
+ uint32_t pos = n * (GIT_OID_RAWSZ + 4);
+ off_t next_off = p->pack_size - GIT_OID_RAWSZ;
+ e->n = n;
+ e->oid = data + pos + 4;
+ e->offset = decode32(data + pos);
+ if (next[n] < p->obj_cnt) {
+ pos = next[n] * (GIT_OID_RAWSZ + 4);
+ next_off = decode32(data + pos);
+ }
+ e->size = next_off - e->offset;
+ return GIT_SUCCESS;
+ }
+ return GIT_ENOTFOUND;
+}
+
+static int pack_openidx_v1(git_pack *p)
+{
+ uint32_t *src_fanout = p->idx_map.data;
+ uint32_t *im_fanout;
+ offset_idx_info *info;
+ size_t expsz;
+ uint32_t j;
+
+
+ if ((im_fanout = git__malloc(sizeof(*im_fanout) * 256)) == NULL)
+ return GIT_ERROR;
+
+ im_fanout[0] = decode32(&src_fanout[0]);
+ for (j = 1; j < 256; j++) {
+ im_fanout[j] = decode32(&src_fanout[j]);
+ if (im_fanout[j] < im_fanout[j - 1]) {
+ free(im_fanout);
+ return GIT_ERROR;
+ }
+ }
+ p->obj_cnt = im_fanout[255];
+
+ expsz = 4 * 256 + 24 * p->obj_cnt + 2 * 20;
+ if (expsz != p->idx_map.len) {
+ free(im_fanout);
+ return GIT_ERROR;
+ }
+
+ p->idx_search = idxv1_search;
+ p->idx_search_offset = idxv1_search_offset;
+ p->idx_get = idxv1_get;
+ p->im_fanout = im_fanout;
+ p->im_oid = (unsigned char *)(src_fanout + 256);
+
+ if ((info = git__malloc(sizeof(*info) * (p->obj_cnt+1))) == NULL) {
+ free(im_fanout);
+ return GIT_ERROR;
+ }
+
+ for (j = 0; j < p->obj_cnt; j++) {
+ uint32_t pos = j * (GIT_OID_RAWSZ + 4);
+ info[j].offset = decode32(p->im_oid + pos);
+ info[j].n = j;
+ }
+ info[p->obj_cnt].offset = p->pack_size - GIT_OID_RAWSZ;
+ info[p->obj_cnt].n = p->obj_cnt;
+
+ if (make_offset_index(p, info)) {
+ free(im_fanout);
+ free(info);
+ return GIT_ERROR;
+ }
+ free(info);
+
+ return GIT_SUCCESS;
+}
+
+static int idxv2_search(uint32_t *out, git_pack *p, const git_oid *id)
+{
+ unsigned char *data = p->im_oid;
+ uint32_t lo = id->id[0] ? p->im_fanout[id->id[0] - 1] : 0;
+ uint32_t hi = p->im_fanout[id->id[0]];
+
+ do {
+ uint32_t mid = (lo + hi) >> 1;
+ uint32_t pos = 20 * mid;
+ int cmp = memcmp(id->id, data + pos, 20);
+ if (cmp < 0)
+ hi = mid;
+ else if (!cmp) {
+ *out = mid;
+ return GIT_SUCCESS;
+ } else
+ lo = mid + 1;
+ } while (lo < hi);
+ return GIT_ENOTFOUND;
+}
+
+static int idxv2_search_offset(uint32_t *out, git_pack *p, off_t offset)
+{
+ if (offset > 0 && offset < (p->pack_size - GIT_OID_RAWSZ)) {
+ uint32_t lo = 0, hi = p->obj_cnt+1;
+ uint32_t *idx = p->im_off_idx;
+ do {
+ uint32_t mid = (lo + hi) >> 1;
+ uint32_t n = idx[mid];
+ uint32_t o32 = decode32(p->im_offset32 + n);
+ off_t here = o32;
+
+ if (o32 & 0x80000000) {
+ uint32_t o64_idx = (o32 & ~0x80000000);
+ here = decode64(p->im_offset64 + 2*o64_idx);
+ }
+
+ if (offset < here)
+ hi = mid;
+ else if (offset == here) {
+ *out = n;
+ return GIT_SUCCESS;
+ } else
+ lo = mid + 1;
+ } while (lo < hi);
+ }
+ return GIT_ENOTFOUND;
+}
+
+static int idxv2_get(index_entry *e, git_pack *p, uint32_t n)
+{
+ unsigned char *data = p->im_oid;
+ uint32_t *next = p->im_off_next;
+
+ if (n < p->obj_cnt) {
+ uint32_t o32 = decode32(p->im_offset32 + n);
+ off_t next_off = p->pack_size - GIT_OID_RAWSZ;
+ e->n = n;
+ e->oid = data + n * GIT_OID_RAWSZ;
+ e->offset = o32;
+ if (o32 & 0x80000000) {
+ uint32_t o64_idx = (o32 & ~0x80000000);
+ e->offset = decode64(p->im_offset64 + 2*o64_idx);
+ }
+ if (next[n] < p->obj_cnt) {
+ o32 = decode32(p->im_offset32 + next[n]);
+ next_off = o32;
+ if (o32 & 0x80000000) {
+ uint32_t o64_idx = (o32 & ~0x80000000);
+ next_off = decode64(p->im_offset64 + 2*o64_idx);
+ }
+ }
+ e->size = next_off - e->offset;
+ return GIT_SUCCESS;
+ }
+ return GIT_ENOTFOUND;
+}
+
+static int pack_openidx_v2(git_pack *p)
+{
+ unsigned char *data = p->idx_map.data;
+ uint32_t *src_fanout = (uint32_t *)(data + 8);
+ uint32_t *im_fanout;
+ offset_idx_info *info;
+ size_t sz, o64_sz, o64_len;
+ uint32_t j;
+
+ if ((im_fanout = git__malloc(sizeof(*im_fanout) * 256)) == NULL)
+ return GIT_ERROR;
+
+ im_fanout[0] = decode32(&src_fanout[0]);
+ for (j = 1; j < 256; j++) {
+ im_fanout[j] = decode32(&src_fanout[j]);
+ if (im_fanout[j] < im_fanout[j - 1]) {
+ free(im_fanout);
+ return GIT_ERROR;
+ }
+ }
+ p->obj_cnt = im_fanout[255];
+
+ /* minimum size of .idx file (with empty 64-bit offsets table): */
+ sz = 4 + 4 + 256 * 4 + p->obj_cnt * (20 + 4 + 4) + 2 * 20;
+ if (p->idx_map.len < sz) {
+ free(im_fanout);
+ return GIT_ERROR;
+ }
+
+ p->idx_search = idxv2_search;
+ p->idx_search_offset = idxv2_search_offset;
+ p->idx_get = idxv2_get;
+ p->im_fanout = im_fanout;
+ p->im_oid = (unsigned char *)(src_fanout + 256);
+ p->im_crc = (uint32_t *)(p->im_oid + 20 * p->obj_cnt);
+ p->im_offset32 = p->im_crc + p->obj_cnt;
+ p->im_offset64 = p->im_offset32 + p->obj_cnt;
+
+ if ((info = git__malloc(sizeof(*info) * (p->obj_cnt+1))) == NULL) {
+ free(im_fanout);
+ return GIT_ERROR;
+ }
+
+ /* check 64-bit offset table index values are within bounds */
+ o64_sz = p->idx_map.len - sz;
+ o64_len = o64_sz / 8;
+ for (j = 0; j < p->obj_cnt; j++) {
+ uint32_t o32 = decode32(p->im_offset32 + j);
+ off_t offset = o32;
+ if (o32 & 0x80000000) {
+ uint32_t o64_idx = (o32 & ~0x80000000);
+ if (o64_idx >= o64_len) {
+ free(im_fanout);
+ free(info);
+ return GIT_ERROR;
+ }
+ offset = decode64(p->im_offset64 + 2*o64_idx);
+ }
+ info[j].offset = offset;
+ info[j].n = j;
+ }
+ info[p->obj_cnt].offset = p->pack_size - GIT_OID_RAWSZ;
+ info[p->obj_cnt].n = p->obj_cnt;
+
+ if (make_offset_index(p, info)) {
+ free(im_fanout);
+ free(info);
+ return GIT_ERROR;
+ }
+ free(info);
+
+ return GIT_SUCCESS;
+}
+
+
+
+
+
+
+
+
+/***********************************************************
+ *
+ * PACKFILE READING FUNCTIONS
+ *
+ * Read the contents of a packfile
+ *
+ ***********************************************************/
+
+
+static int unpack_object(git_rawobj *out, git_pack *p, index_entry *e);
+
+static int unpack_object_delta(git_rawobj *out, git_pack *p,
+ index_entry *base_entry,
+ uint8_t *delta_buffer,
+ size_t delta_deflated_size,
+ size_t delta_inflated_size)
+{
+ int res = 0;
+ uint8_t *delta = NULL;
+ git_rawobj base_obj;
+
+ base_obj.data = NULL;
+ base_obj.type = GIT_OBJ_BAD;
+ base_obj.len = 0;
+
+ if ((res = unpack_object(&base_obj, p, base_entry)) < 0)
+ goto cleanup;
+
+ delta = git__malloc(delta_inflated_size + 1);
+
+ if ((res = git_odb__inflate_buffer(delta_buffer, delta_deflated_size,
+ delta, delta_inflated_size)) < 0)
+ goto cleanup;
+
+ res = git__delta_apply(out, base_obj.data, base_obj.len, delta, delta_inflated_size);
+
+ out->type = base_obj.type;
+
+cleanup:
+ free(delta);
+ git_rawobj_close(&base_obj);
+ return res;
+}
+
+static int unpack_object(git_rawobj *out, git_pack *p, index_entry *e)
+{
+ git_otype object_type;
+ size_t inflated_size, deflated_size, shift;
+ uint8_t *buffer, byte;
+
+ assert(out && p && e && git__is_sizet(e->size));
+
+ if (open_pack(p))
+ return GIT_ERROR;
+
+ buffer = (uint8_t *)p->pack_map.data + e->offset;
+ deflated_size = (size_t)e->size;
+
+ if (deflated_size == 0)
+ deflated_size = (size_t)(p->pack_size - e->offset);
+
+ byte = *buffer++ & 0xFF;
+ deflated_size--;
+ object_type = (byte >> 4) & 0x7;
+ inflated_size = byte & 0xF;
+ shift = 4;
+
+ while (byte & 0x80) {
+ byte = *buffer++ & 0xFF;
+ deflated_size--;
+ inflated_size += (byte & 0x7F) << shift;
+ shift += 7;
+ }
+
+ switch (object_type) {
+ case GIT_OBJ_COMMIT:
+ case GIT_OBJ_TREE:
+ case GIT_OBJ_BLOB:
+ case GIT_OBJ_TAG: {
+
+ /* Handle a normal zlib stream */
+ out->len = inflated_size;
+ out->type = object_type;
+ out->data = git__malloc(inflated_size + 1);
+
+ if (git_odb__inflate_buffer(buffer, deflated_size, out->data, out->len) < 0) {
+ free(out->data);
+ out->data = NULL;
+ return GIT_ERROR;
+ }
+
+ return GIT_SUCCESS;
+ }
+
+ case GIT_OBJ_OFS_DELTA: {
+
+ off_t delta_offset;
+ index_entry entry;
+
+ byte = *buffer++ & 0xFF;
+ delta_offset = byte & 0x7F;
+
+ while (byte & 0x80) {
+ delta_offset += 1;
+ byte = *buffer++ & 0xFF;
+ delta_offset <<= 7;
+ delta_offset += (byte & 0x7F);
+ }
+
+ entry.n = 0;
+ entry.oid = NULL;
+ entry.offset = e->offset - delta_offset;
+ entry.size = 0;
+
+ if (unpack_object_delta(out, p, &entry,
+ buffer, deflated_size, inflated_size) < 0)
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+ }
+
+ case GIT_OBJ_REF_DELTA: {
+
+ git_oid base_id;
+ uint32_t n;
+ index_entry entry;
+ int res = GIT_ERROR;
+
+ git_oid_mkraw(&base_id, buffer);
+
+ if (!p->idx_search(&n, p, &base_id) &&
+ !p->idx_get(&entry, p, n)) {
+
+ res = unpack_object_delta(out, p, &entry,
+ buffer + GIT_OID_RAWSZ, deflated_size, inflated_size);
+ }
+
+ return res;
+ }
+
+ default:
+ return GIT_EOBJCORRUPTED;
+ }
+}
+
+static int read_packed(git_rawobj *out, const pack_location *loc)
+{
+ index_entry e;
+ int res;
+
+ assert(out && loc);
+
+ if (pack_openidx(loc->ptr) < 0)
+ return GIT_EPACKCORRUPTED;
+
+ res = loc->ptr->idx_get(&e, loc->ptr, loc->n);
+
+ if (!res)
+ res = unpack_object(out, loc->ptr, &e);
+
+ pack_decidx(loc->ptr);
+
+ return res;
+}
+
+static int read_header_packed(git_rawobj *out, const pack_location *loc)
+{
+ git_pack *pack;
+ index_entry e;
+ int error = GIT_SUCCESS, shift;
+ uint8_t *buffer, byte;
+
+ assert(out && loc);
+
+ pack = loc->ptr;
+
+ if (pack_openidx(pack))
+ return GIT_EPACKCORRUPTED;
+
+ if (pack->idx_get(&e, pack, loc->n) < 0 ||
+ open_pack(pack) < 0) {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ buffer = (uint8_t *)pack->pack_map.data + e.offset;
+
+ byte = *buffer++ & 0xFF;
+ out->type = (byte >> 4) & 0x7;
+ out->len = byte & 0xF;
+ shift = 4;
+
+ while (byte & 0x80) {
+ byte = *buffer++ & 0xFF;
+ out->len += (byte & 0x7F) << shift;
+ shift += 7;
+ }
+
+ /*
+ * FIXME: if the object is not packed as a whole,
+ * we need to do a full load and apply the deltas before
+ * being able to read the header.
+ *
+ * I don't think there are any workarounds for this.'
+ */
+
+ if (out->type == GIT_OBJ_OFS_DELTA || out->type == GIT_OBJ_REF_DELTA) {
+ error = unpack_object(out, pack, &e);
+ git_rawobj_close(out);
+ }
+
+cleanup:
+ pack_decidx(loc->ptr);
+ return error;
+}
+
+
+
+
+
+
+
+/***********************************************************
+ *
+ * PACKED BACKEND PUBLIC API
+ *
+ * Implement the git_odb_backend API calls
+ *
+ ***********************************************************/
+
+int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
+{
+ pack_location location;
+
+ assert(obj && backend && oid);
+
+ if (locate_packfile(&location, (pack_backend *)backend, oid) < 0)
+ return GIT_ENOTFOUND;
+
+ return read_header_packed(obj, &location);
+}
+
+int pack_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
+{
+ pack_location location;
+
+ assert(obj && backend && oid);
+
+ if (locate_packfile(&location, (pack_backend *)backend, oid) < 0)
+ return GIT_ENOTFOUND;
+
+ return read_packed(obj, &location);
+}
+
+int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ pack_location location;
+ assert(backend && oid);
+ return locate_packfile(&location, (pack_backend *)backend, oid) == GIT_SUCCESS;
+}
+
+void pack_backend__free(git_odb_backend *_backend)
+{
+ pack_backend *backend;
+ git_packlist *pl;
+
+ assert(_backend);
+
+ backend = (pack_backend *)_backend;
+
+ gitlck_lock(&backend->lock);
+
+ pl = backend->packlist;
+ backend->packlist = NULL;
+
+ gitlck_unlock(&backend->lock);
+ if (pl)
+ packlist_dec(backend, pl);
+
+ gitlck_free(&backend->lock);
+
+ free(backend->objects_dir);
+ free(backend);
+}
+
+int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
+{
+ pack_backend *backend;
+
+ backend = git__calloc(1, sizeof(pack_backend));
+ if (backend == NULL)
+ return GIT_ENOMEM;
+
+ backend->objects_dir = git__strdup(objects_dir);
+ if (backend->objects_dir == NULL) {
+ free(backend);
+ return GIT_ENOMEM;
+ }
+
+ gitlck_init(&backend->lock);
+
+ backend->parent.read = &pack_backend__read;
+ backend->parent.read_header = &pack_backend__read_header;
+ backend->parent.write = NULL;
+ backend->parent.exists = &pack_backend__exists;
+ backend->parent.free = &pack_backend__free;
+
+ backend->parent.priority = 1;
+
+ *backend_out = (git_odb_backend *)backend;
+ return GIT_SUCCESS;
+}
diff --git a/src/repository.c b/src/repository.c
index eade02cad..9b23ed74e 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -445,7 +445,7 @@ void git_object__source_close(git_object *object)
assert(object);
if (object->source.open) {
- git_obj_close(&object->source.raw);
+ git_rawobj_close(&object->source.raw);
object->source.open = 0;
}
}
diff --git a/src/tag.c b/src/tag.c
index 920a72b63..82d80b4b8 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -215,7 +215,7 @@ int git_tag__writeback(git_tag *tag, git_odb_source *src)
return GIT_EMISSINGOBJDATA;
git__write_oid(src, "object", git_object_id(tag->target));
- git__source_printf(src, "type %s\n", git_obj_type_to_string(tag->type));
+ git__source_printf(src, "type %s\n", git_otype_tostring(tag->type));
git__source_printf(src, "tag %s\n", tag->tag_name);
git_person__write(src, "tagger", tag->tagger);
diff --git a/tests/t0102-objtype.c b/tests/t0102-objtype.c
index b42f3f731..a234798ea 100644
--- a/tests/t0102-objtype.c
+++ b/tests/t0102-objtype.c
@@ -3,48 +3,48 @@
#include <git/odb.h>
BEGIN_TEST(type_to_string)
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_BAD), ""));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ__EXT1), ""));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_COMMIT), "commit"));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_TREE), "tree"));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_BLOB), "blob"));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_TAG), "tag"));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ__EXT2), ""));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_OFS_DELTA), "OFS_DELTA"));
- must_be_true(!strcmp(git_obj_type_to_string(GIT_OBJ_REF_DELTA), "REF_DELTA"));
-
- must_be_true(!strcmp(git_obj_type_to_string(-2), ""));
- must_be_true(!strcmp(git_obj_type_to_string(8), ""));
- must_be_true(!strcmp(git_obj_type_to_string(1234), ""));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_BAD), ""));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ__EXT1), ""));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_COMMIT), "commit"));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_TREE), "tree"));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_BLOB), "blob"));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_TAG), "tag"));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ__EXT2), ""));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_OFS_DELTA), "OFS_DELTA"));
+ must_be_true(!strcmp(git_otype_tostring(GIT_OBJ_REF_DELTA), "REF_DELTA"));
+
+ must_be_true(!strcmp(git_otype_tostring(-2), ""));
+ must_be_true(!strcmp(git_otype_tostring(8), ""));
+ must_be_true(!strcmp(git_otype_tostring(1234), ""));
END_TEST
BEGIN_TEST(string_to_type)
- must_be_true(git_obj_string_to_type(NULL) == GIT_OBJ_BAD);
- must_be_true(git_obj_string_to_type("") == GIT_OBJ_BAD);
- must_be_true(git_obj_string_to_type("commit") == GIT_OBJ_COMMIT);
- must_be_true(git_obj_string_to_type("tree") == GIT_OBJ_TREE);
- must_be_true(git_obj_string_to_type("blob") == GIT_OBJ_BLOB);
- must_be_true(git_obj_string_to_type("tag") == GIT_OBJ_TAG);
- must_be_true(git_obj_string_to_type("OFS_DELTA") == GIT_OBJ_OFS_DELTA);
- must_be_true(git_obj_string_to_type("REF_DELTA") == GIT_OBJ_REF_DELTA);
-
- must_be_true(git_obj_string_to_type("CoMmIt") == GIT_OBJ_BAD);
- must_be_true(git_obj_string_to_type("hohoho") == GIT_OBJ_BAD);
+ must_be_true(git_otype_fromstring(NULL) == GIT_OBJ_BAD);
+ must_be_true(git_otype_fromstring("") == GIT_OBJ_BAD);
+ must_be_true(git_otype_fromstring("commit") == GIT_OBJ_COMMIT);
+ must_be_true(git_otype_fromstring("tree") == GIT_OBJ_TREE);
+ must_be_true(git_otype_fromstring("blob") == GIT_OBJ_BLOB);
+ must_be_true(git_otype_fromstring("tag") == GIT_OBJ_TAG);
+ must_be_true(git_otype_fromstring("OFS_DELTA") == GIT_OBJ_OFS_DELTA);
+ must_be_true(git_otype_fromstring("REF_DELTA") == GIT_OBJ_REF_DELTA);
+
+ must_be_true(git_otype_fromstring("CoMmIt") == GIT_OBJ_BAD);
+ must_be_true(git_otype_fromstring("hohoho") == GIT_OBJ_BAD);
END_TEST
BEGIN_TEST(loose_object)
- must_be_true(git_obj__loose_object_type(GIT_OBJ_BAD) == 0);
- must_be_true(git_obj__loose_object_type(GIT_OBJ__EXT1) == 0);
- must_be_true(git_obj__loose_object_type(GIT_OBJ_COMMIT) == 1);
- must_be_true(git_obj__loose_object_type(GIT_OBJ_TREE) == 1);
- must_be_true(git_obj__loose_object_type(GIT_OBJ_BLOB) == 1);
- must_be_true(git_obj__loose_object_type(GIT_OBJ_TAG) == 1);
- must_be_true(git_obj__loose_object_type(GIT_OBJ__EXT2) == 0);
- must_be_true(git_obj__loose_object_type(GIT_OBJ_OFS_DELTA) == 0);
- must_be_true(git_obj__loose_object_type(GIT_OBJ_REF_DELTA) == 0);
-
- must_be_true(git_obj__loose_object_type(-2) == 0);
- must_be_true(git_obj__loose_object_type(8) == 0);
- must_be_true(git_obj__loose_object_type(1234) == 0);
+ must_be_true(git_otype_is_loose(GIT_OBJ_BAD) == 0);
+ must_be_true(git_otype_is_loose(GIT_OBJ__EXT1) == 0);
+ must_be_true(git_otype_is_loose(GIT_OBJ_COMMIT) == 1);
+ must_be_true(git_otype_is_loose(GIT_OBJ_TREE) == 1);
+ must_be_true(git_otype_is_loose(GIT_OBJ_BLOB) == 1);
+ must_be_true(git_otype_is_loose(GIT_OBJ_TAG) == 1);
+ must_be_true(git_otype_is_loose(GIT_OBJ__EXT2) == 0);
+ must_be_true(git_otype_is_loose(GIT_OBJ_OFS_DELTA) == 0);
+ must_be_true(git_otype_is_loose(GIT_OBJ_REF_DELTA) == 0);
+
+ must_be_true(git_otype_is_loose(-2) == 0);
+ must_be_true(git_otype_is_loose(8) == 0);
+ must_be_true(git_otype_is_loose(1234) == 0);
END_TEST
diff --git a/tests/t0103-objhash.c b/tests/t0103-objhash.c
index 8d0ba903e..94c5e85af 100644
--- a/tests/t0103-objhash.c
+++ b/tests/t0103-objhash.c
@@ -326,28 +326,28 @@ BEGIN_TEST(hash_junk)
/* invalid types: */
junk_obj.data = some_data;
- must_fail(git_obj_hash(&id, &junk_obj));
+ must_fail(git_rawobj_hash(&id, &junk_obj));
junk_obj.type = GIT_OBJ__EXT1;
- must_fail(git_obj_hash(&id, &junk_obj));
+ must_fail(git_rawobj_hash(&id, &junk_obj));
junk_obj.type = GIT_OBJ__EXT2;
- must_fail(git_obj_hash(&id, &junk_obj));
+ must_fail(git_rawobj_hash(&id, &junk_obj));
junk_obj.type = GIT_OBJ_OFS_DELTA;
- must_fail(git_obj_hash(&id, &junk_obj));
+ must_fail(git_rawobj_hash(&id, &junk_obj));
junk_obj.type = GIT_OBJ_REF_DELTA;
- must_fail(git_obj_hash(&id, &junk_obj));
+ must_fail(git_rawobj_hash(&id, &junk_obj));
/* data can be NULL only if len is zero: */
junk_obj.type = GIT_OBJ_BLOB;
junk_obj.data = NULL;
- must_pass(git_obj_hash(&id, &junk_obj));
+ must_pass(git_rawobj_hash(&id, &junk_obj));
must_be_true(git_oid_cmp(&id, &id_zero) == 0);
junk_obj.len = 1;
- must_fail(git_obj_hash(&id, &junk_obj));
+ must_fail(git_rawobj_hash(&id, &junk_obj));
END_TEST
BEGIN_TEST(hash_commit)
@@ -355,7 +355,7 @@ BEGIN_TEST(hash_commit)
must_pass(git_oid_mkstr(&id1, commit_id));
- must_pass(git_obj_hash(&id2, &commit_obj));
+ must_pass(git_rawobj_hash(&id2, &commit_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
@@ -365,7 +365,7 @@ BEGIN_TEST(hash_tree)
must_pass(git_oid_mkstr(&id1, tree_id));
- must_pass(git_obj_hash(&id2, &tree_obj));
+ must_pass(git_rawobj_hash(&id2, &tree_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
@@ -375,7 +375,7 @@ BEGIN_TEST(hash_tag)
must_pass(git_oid_mkstr(&id1, tag_id));
- must_pass(git_obj_hash(&id2, &tag_obj));
+ must_pass(git_rawobj_hash(&id2, &tag_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
@@ -385,7 +385,7 @@ BEGIN_TEST(hash_zero)
must_pass(git_oid_mkstr(&id1, zero_id));
- must_pass(git_obj_hash(&id2, &zero_obj));
+ must_pass(git_rawobj_hash(&id2, &zero_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
@@ -395,7 +395,7 @@ BEGIN_TEST(hash_one)
must_pass(git_oid_mkstr(&id1, one_id));
- must_pass(git_obj_hash(&id2, &one_obj));
+ must_pass(git_rawobj_hash(&id2, &one_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
@@ -405,7 +405,7 @@ BEGIN_TEST(hash_two)
must_pass(git_oid_mkstr(&id1, two_id));
- must_pass(git_obj_hash(&id2, &two_obj));
+ must_pass(git_rawobj_hash(&id2, &two_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
@@ -415,7 +415,7 @@ BEGIN_TEST(hash_some)
must_pass(git_oid_mkstr(&id1, some_id));
- must_pass(git_obj_hash(&id2, &some_obj));
+ must_pass(git_rawobj_hash(&id2, &some_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
diff --git a/tests/t0202-readloose.c b/tests/t0202-readloose.c
index 3cb99e4da..9956b3cd9 100644
--- a/tests/t0202-readloose.c
+++ b/tests/t0202-readloose.c
@@ -532,10 +532,10 @@ BEGIN_TEST(read_loose_commit)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, commit.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &commit));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &commit));
END_TEST
@@ -549,10 +549,10 @@ BEGIN_TEST(read_loose_tree)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, tree.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &tree));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &tree));
END_TEST
@@ -566,10 +566,10 @@ BEGIN_TEST(read_loose_tag)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, tag.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &tag));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &tag));
END_TEST
@@ -583,10 +583,10 @@ BEGIN_TEST(read_loose_zero)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, zero.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &zero));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &zero));
END_TEST
@@ -600,10 +600,10 @@ BEGIN_TEST(read_loose_one)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, one.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &one));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &one));
END_TEST
@@ -617,10 +617,10 @@ BEGIN_TEST(read_loose_two)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, two.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &two));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &two));
END_TEST
@@ -634,10 +634,10 @@ BEGIN_TEST(read_loose_some)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, some.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &some));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &some));
END_TEST
diff --git a/tests/t0203-readloose.c b/tests/t0203-readloose.c
index 5952c2ee3..3b1728b5a 100644
--- a/tests/t0203-readloose.c
+++ b/tests/t0203-readloose.c
@@ -533,10 +533,10 @@ BEGIN_TEST(read_loose_commit)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, commit.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &commit));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &commit));
END_TEST
@@ -550,10 +550,10 @@ BEGIN_TEST(read_loose_tree)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, tree.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &tree));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &tree));
END_TEST
@@ -567,10 +567,10 @@ BEGIN_TEST(read_loose_tag)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, tag.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &tag));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &tag));
END_TEST
@@ -584,10 +584,10 @@ BEGIN_TEST(read_loose_zero)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, zero.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &zero));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &zero));
END_TEST
@@ -601,10 +601,10 @@ BEGIN_TEST(read_loose_one)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, one.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &one));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &one));
END_TEST
@@ -618,10 +618,10 @@ BEGIN_TEST(read_loose_two)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, two.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &two));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &two));
END_TEST
@@ -635,10 +635,10 @@ BEGIN_TEST(read_loose_some)
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id, some.id));
- must_pass(git_odb__read_loose(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &some));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &some));
END_TEST
diff --git a/tests/t0204-readpack.c b/tests/t0204-readpack.c
index 44694543a..a7c8db7e0 100644
--- a/tests/t0204-readpack.c
+++ b/tests/t0204-readpack.c
@@ -143,9 +143,9 @@ BEGIN_TEST(readpacked_test)
must_pass(git_oid_mkstr(&id, packed_objects[i]));
must_be_true(git_odb_exists(db, &id) == 1);
- must_pass(git_odb__read_packed(&obj, db, &id));
+ must_pass(git_odb_read(&obj, db, &id));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
}
git_odb_close(db);
diff --git a/tests/t0205-readheader.c b/tests/t0205-readheader.c
index 626c5a5ac..f82e57c44 100644
--- a/tests/t0205-readheader.c
+++ b/tests/t0205-readheader.c
@@ -172,7 +172,7 @@ BEGIN_TEST(readheader_packed_test)
must_be_true(obj.len == header.len);
must_be_true(obj.type == header.type);
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
}
git_odb_close(db);
@@ -199,7 +199,7 @@ BEGIN_TEST(readheader_loose_test)
must_be_true(obj.len == header.len);
must_be_true(obj.type == header.type);
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
}
git_odb_close(db);
diff --git a/tests/t0301-write.c b/tests/t0301-write.c
index 6c995bb5a..e0bbae1b8 100644
--- a/tests/t0301-write.c
+++ b/tests/t0301-write.c
@@ -414,10 +414,10 @@ BEGIN_TEST(write_commit)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&commit));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &commit_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&commit));
END_TEST
@@ -435,10 +435,10 @@ BEGIN_TEST(write_tree)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&tree));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &tree_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&tree));
END_TEST
@@ -456,10 +456,10 @@ BEGIN_TEST(write_tag)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&tag));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &tag_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&tag));
END_TEST
@@ -477,10 +477,10 @@ BEGIN_TEST(write_zero)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&zero));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &zero_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&zero));
END_TEST
@@ -498,10 +498,10 @@ BEGIN_TEST(write_one)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&one));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &one_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&one));
END_TEST
@@ -519,10 +519,10 @@ BEGIN_TEST(write_two)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&two));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &two_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&two));
END_TEST
@@ -540,10 +540,10 @@ BEGIN_TEST(write_some)
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&some));
- must_pass(git_odb__read_loose(&obj, db, &id1));
+ must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &some_obj));
- git_obj_close(&obj);
+ git_rawobj_close(&obj);
git_odb_close(db);
must_pass(remove_object_files(&some));
END_TEST
diff --git a/tests/test_helpers.c b/tests/test_helpers.c
index 86192233a..cd14c0e29 100644
--- a/tests/test_helpers.c
+++ b/tests/test_helpers.c
@@ -125,7 +125,7 @@ int remove_loose_object(const char *repository_folder, git_object *object)
int cmp_objects(git_rawobj *o, object_data *d)
{
- if (o->type != git_obj_string_to_type(d->type))
+ if (o->type != git_otype_fromstring(d->type))
return -1;
if (o->len != d->dlen)
return -1;