summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRamsay Jones <ramsay@ramsay1.demon.co.uk>2009-06-04 16:47:59 +0100
committerAndreas Ericsson <ae@op5.se>2009-06-05 10:22:20 +0200
commite17a3f5673e0dc4d9ade34a0a276e6c202506fdf (patch)
treed9a9eb65ed8db7f1200aa017e5ea2204266e7ae4 /src
parentca481fc4f12cec56b71bd50ae72e3f088b69021f (diff)
downloadlibgit2-e17a3f5673e0dc4d9ade34a0a276e6c202506fdf.tar.gz
Implement git_odb_write()
Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk> Signed-off-by: Andreas Ericsson <ae@op5.se>
Diffstat (limited to 'src')
-rw-r--r--src/git/odb.h12
-rw-r--r--src/odb.c173
2 files changed, 181 insertions, 4 deletions
diff --git a/src/git/odb.h b/src/git/odb.h
index 8a8cd3e5f..58b932645 100644
--- a/src/git/odb.h
+++ b/src/git/odb.h
@@ -96,6 +96,18 @@ GIT_EXTERN(int) git_odb__read_packed(git_obj *out, git_odb *db, const git_oid *i
GIT_EXTERN(int) git_odb__read_loose(git_obj *out, git_odb *db, const git_oid *id);
/**
+ * Write an object to the database.
+ *
+ * @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 written;
+ * - GIT_ERROR otherwise.
+ */
+GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_obj *obj);
+
+/**
* Release all memory used by the obj structure.
*
* As a result of this call, obj->data will be set to NULL.
diff --git a/src/odb.c b/src/odb.c
index 6b7d1f799..5f10ae02d 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -85,6 +85,11 @@ struct git_odb {
/** 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 */
@@ -158,13 +163,12 @@ static int format_object_header(char *hdr, size_t n, git_obj *obj)
return len+1;
}
-int git_obj_hash(git_oid *id, git_obj *obj)
+static int hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_obj *obj)
{
git_buf_vec vec[2];
- char hdr[64];
int hdrlen;
- assert(id && obj);
+ assert(id && hdr && len && obj);
if (!git_obj__loose_object_type(obj->type))
return GIT_ERROR;
@@ -172,9 +176,11 @@ int git_obj_hash(git_oid *id, git_obj *obj)
if (!obj->data && obj->len != 0)
return GIT_ERROR;
- if ((hdrlen = format_object_header(hdr, sizeof(hdr), obj)) < 0)
+ if ((hdrlen = format_object_header(hdr, n, obj)) < 0)
return GIT_ERROR;
+ *len = hdrlen;
+
vec[0].data = hdr;
vec[0].len = hdrlen;
vec[1].data = obj->data;
@@ -185,6 +191,16 @@ int git_obj_hash(git_oid *id, git_obj *obj)
return GIT_SUCCESS;
}
+int git_obj_hash(git_oid *id, git_obj *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);
@@ -483,6 +499,125 @@ static int inflate_disk_obj(git_obj *out, gitfo_buf *obj)
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);
+ size_t 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_obj *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 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_ERROR;
+
+ if (make_temp_file(&fd, temp, sizeof(temp), file) < 0)
+ return GIT_ERROR;
+
+ if (gitfo_write(fd, buf->data, buf->len) < 0) {
+ gitfo_close(fd);
+ gitfo_unlink(temp);
+ return GIT_ERROR;
+ }
+
+ 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_ERROR;
+ }
+
+ return GIT_SUCCESS;
+}
+
static int open_alternates(git_odb *db)
{
unsigned n = 0;
@@ -891,6 +1026,9 @@ int git_odb_open(git_odb **out, const char *objects_dir)
gitlck_init(&db->lock);
+ db->object_zlib_level = Z_BEST_SPEED;
+ db->fsync_object_files = 0;
+
*out = db;
return GIT_SUCCESS;
}
@@ -1004,3 +1142,30 @@ int git_odb__read_packed(git_obj *out, git_odb *db, const git_oid *id)
return GIT_ENOTFOUND;
}
+int git_odb_write(git_oid *id, git_odb *db, git_obj *obj)
+{
+ char hdr[64];
+ int hdrlen;
+ gitfo_buf buf = GITFO_BUF_INIT;
+
+ assert(id && db && obj);
+
+ if (hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj) < 0)
+ return GIT_ERROR;
+
+ if (git_odb_exists(db, id))
+ return GIT_SUCCESS;
+
+ if (deflate_obj(&buf, hdr, hdrlen, obj, db->object_zlib_level) < 0)
+ return GIT_ERROR;
+
+ if (write_obj(&buf, id, db) < 0) {
+ gitfo_free_buf(&buf);
+ return GIT_ERROR;
+ }
+
+ gitfo_free_buf(&buf);
+
+ return GIT_SUCCESS;
+}
+