diff options
author | Ramsay Jones <ramsay@ramsay1.demon.co.uk> | 2009-06-04 16:47:59 +0100 |
---|---|---|
committer | Andreas Ericsson <ae@op5.se> | 2009-06-05 10:22:20 +0200 |
commit | e17a3f5673e0dc4d9ade34a0a276e6c202506fdf (patch) | |
tree | d9a9eb65ed8db7f1200aa017e5ea2204266e7ae4 /src | |
parent | ca481fc4f12cec56b71bd50ae72e3f088b69021f (diff) | |
download | libgit2-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.h | 12 | ||||
-rw-r--r-- | src/odb.c | 173 |
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. @@ -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; +} + |