diff options
Diffstat (limited to 'src/odb_loose.c')
-rw-r--r-- | src/odb_loose.c | 283 |
1 files changed, 170 insertions, 113 deletions
diff --git a/src/odb_loose.c b/src/odb_loose.c index f1789e071..f177af86c 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -26,7 +26,6 @@ typedef struct { /* object header data */ typedef struct { git_odb_stream stream; git_filebuf fbuf; - int finished; } loose_writestream; typedef struct loose_backend { @@ -51,31 +50,28 @@ typedef struct { } loose_locate_object_state; - /*********************************************************** * * MISCELANEOUS HELPER FUNCTIONS * ***********************************************************/ -static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id) +static int object_file_name(git_buf *name, const char *dir, const git_oid *id) { - size_t len = strlen(dir); + git_buf_sets(name, dir); - /* check length: 43 = 40 hex sha1 chars + 2 * '/' + '\0' */ - if (len+43 > n) - return len+43; + /* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */ + if (git_buf_grow(name, name->size + GIT_OID_HEXSZ + 3) < GIT_SUCCESS) + return GIT_ENOMEM; - /* the object dir: eg $GIT_DIR/objects */ - strcpy(name, dir); - if (name[len-1] != '/') - name[len++] = '/'; + git_path_to_dir(name); /* loose object filename: aa/aaa... (41 bytes) */ - git_oid_pathfmt(&name[len], id); - name[len+41] = '\0'; + git_oid_pathfmt(name->ptr + name->size, id); + name->size += GIT_OID_HEXSZ + 1; + name->ptr[name->size] = '\0'; - return 0; + return GIT_SUCCESS; } @@ -384,18 +380,21 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj) * ***********************************************************/ -static int read_loose(git_rawobj *out, const char *loc) +static int read_loose(git_rawobj *out, git_buf *loc) { int error; git_fbuffer obj = GIT_FBUFFER_INIT; assert(out && loc); + if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS) + return error; + out->data = NULL; out->len = 0; out->type = GIT_OBJ_BAD; - if (git_futils_readbuffer(&obj, loc) < 0) + if (git_futils_readbuffer(&obj, loc->ptr) < 0) return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found"); error = inflate_disk_obj(out, &obj); @@ -404,7 +403,7 @@ static int read_loose(git_rawobj *out, const char *loc) return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object"); } -static int read_header_loose(git_rawobj *out, const char *loc) +static int read_header_loose(git_rawobj *out, git_buf *loc) { int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes; git_file fd; @@ -414,9 +413,12 @@ static int read_header_loose(git_rawobj *out, const char *loc) assert(out && loc); + if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS) + return error; + out->data = NULL; - if ((fd = p_open(loc, O_RDONLY)) < 0) + if ((fd = p_open(loc->ptr, O_RDONLY)) < 0) return git__throw(GIT_ENOTFOUND, "Failed to read loose object header. File not found"); init_stream(&zs, inflated_buffer, sizeof(inflated_buffer)); @@ -456,33 +458,39 @@ cleanup: return GIT_SUCCESS; } -static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid) +static int locate_object( + git_buf *object_location, + loose_backend *backend, + const git_oid *oid) { - object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid); - return git_futils_exists(object_location); + int error = object_file_name(object_location, backend->objects_dir, oid); + + if (error == GIT_SUCCESS) + error = git_futils_exists(git_buf_cstr(object_location)); + + return error; } /* Explore an entry of a directory and see if it matches a short oid */ -static int fn_locate_object_short_oid(void *state, char *pathbuf) { +static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) { loose_locate_object_state *sstate = (loose_locate_object_state *)state; - size_t pathbuf_len = strlen(pathbuf); - if (pathbuf_len - sstate->dir_len != GIT_OID_HEXSZ - 2) { + if (pathbuf->size - sstate->dir_len != GIT_OID_HEXSZ - 2) { /* Entry cannot be an object. Continue to next entry */ return GIT_SUCCESS; } - if (!git_futils_exists(pathbuf) && git_futils_isdir(pathbuf)) { + if (!git_futils_exists(pathbuf->ptr) && git_futils_isdir(pathbuf->ptr)) { /* We are already in the directory matching the 2 first hex characters, * compare the first ncmp characters of the oids */ if (!memcmp(sstate->short_oid + 2, - (unsigned char *)pathbuf + sstate->dir_len, + (unsigned char *)pathbuf->ptr + sstate->dir_len, sstate->short_oid_len - 2)) { if (!sstate->found) { sstate->res_oid[0] = sstate->short_oid[0]; sstate->res_oid[1] = sstate->short_oid[1]; - memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2); + memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2); } sstate->found++; } @@ -494,39 +502,50 @@ static int fn_locate_object_short_oid(void *state, char *pathbuf) { } /* Locate an object matching a given short oid */ -static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len) +static int locate_object_short_oid( + git_buf *object_location, + git_oid *res_oid, + loose_backend *backend, + const git_oid *short_oid, + unsigned int len) { char *objects_dir = backend->objects_dir; size_t dir_len = strlen(objects_dir); loose_locate_object_state state; int error; - if (dir_len+43 > GIT_PATH_MAX) - return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long"); + /* prealloc memory for OBJ_DIR/xx/ */ + if ((error = git_buf_grow(object_location, dir_len + 5)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to locate object from short oid"); - strcpy(object_location, objects_dir); + git_buf_sets(object_location, objects_dir); + git_path_to_dir(object_location); - /* Add a separator if not already there */ - if (object_location[dir_len-1] != '/') - object_location[dir_len++] = '/'; + /* save adjusted position at end of dir so it can be restored later */ + dir_len = object_location->size; /* Convert raw oid to hex formatted oid */ git_oid_fmt((char *)state.short_oid, short_oid); + /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ - sprintf(object_location+dir_len, "%.2s/", state.short_oid); + error = git_buf_printf(object_location, "%.2s/", state.short_oid); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to locate object from short oid"); /* Check that directory exists */ - if (git_futils_exists(object_location) || git_futils_isdir(object_location)) + if (git_futils_exists(object_location->ptr) || + git_futils_isdir(object_location->ptr)) return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found"); - state.dir_len = dir_len+3; + state.dir_len = object_location->size; state.short_oid_len = len; state.found = 0; + /* Explore directory to find a unique object matching short_oid */ - error = git_futils_direach(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state); - if (error) { + error = git_futils_direach(object_location, fn_locate_object_short_oid, &state); + if (error) return git__rethrow(error, "Failed to locate object from short oid"); - } + if (!state.found) { return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found"); } @@ -538,7 +557,16 @@ static int locate_object_short_oid(char *object_location, git_oid *res_oid, loos } /* Update the location according to the oid obtained */ - git_oid_pathfmt(object_location+dir_len, res_oid); + + git_buf_truncate(object_location, dir_len); + error = git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2); + if (error) + return git__rethrow(error, "Failed to locate object from short oid"); + + git_oid_pathfmt(object_location->ptr + dir_len, res_oid); + + object_location->size += GIT_OID_HEXSZ + 1; + object_location->ptr[object_location->size] = '\0'; return GIT_SUCCESS; } @@ -561,45 +589,49 @@ static int locate_object_short_oid(char *object_location, git_oid *res_oid, loos static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { - char object_path[GIT_PATH_MAX]; + git_buf object_path = GIT_BUF_INIT; git_rawobj raw; - int error; + int error = GIT_SUCCESS; assert(backend && oid); raw.len = 0; raw.type = GIT_OBJ_BAD; - if (locate_object(object_path, (loose_backend *)backend, oid) < 0) - return git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found"); + if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) + error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found"); + else if ((error = read_header_loose(&raw, &object_path)) == GIT_SUCCESS) { + *len_p = raw.len; + *type_p = raw.type; + } - if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS) - return error; + git_buf_free(&object_path); - *len_p = raw.len; - *type_p = raw.type; - return GIT_SUCCESS; + return error; } static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { - char object_path[GIT_PATH_MAX]; + git_buf object_path = GIT_BUF_INIT; git_rawobj raw; - int error; + int error = GIT_SUCCESS; assert(backend && oid); - if (locate_object(object_path, (loose_backend *)backend, oid) < 0) - return git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found"); - - if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to read loose backend"); + if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) + error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found"); + else if ((error = read_loose(&raw, &object_path)) == GIT_SUCCESS) { + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + } + else { + git__rethrow(error, "Failed to read loose backend"); + } - *buffer_p = raw.data; - *len_p = raw.len; - *type_p = raw.type; + git_buf_free(&object_path); - return GIT_SUCCESS; + return error; } static int loose_backend__read_prefix( @@ -611,45 +643,52 @@ static int loose_backend__read_prefix( const git_oid *short_oid, unsigned int len) { + int error = GIT_SUCCESS; + if (len < GIT_OID_MINPREFIXLEN) - return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose " + "backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); if (len >= GIT_OID_HEXSZ) { /* We can fall back to regular read method */ - int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); + error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (error == GIT_SUCCESS) git_oid_cpy(out_oid, short_oid); - - return error; } else { - char object_path[GIT_PATH_MAX]; + git_buf object_path = GIT_BUF_INIT; git_rawobj raw; - int error; assert(backend && short_oid); - if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) { - return git__rethrow(error, "Failed to read loose backend"); + if ((error = locate_object_short_oid(&object_path, out_oid, + (loose_backend *)backend, short_oid, len)) < 0) + git__rethrow(error, "Failed to read loose backend"); + else if ((error = read_loose(&raw, &object_path)) < GIT_SUCCESS) + git__rethrow(error, "Failed to read loose backend"); + else { + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; } - if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to read loose backend"); - - *buffer_p = raw.data; - *len_p = raw.len; - *type_p = raw.type; + git_buf_free(&object_path); } - return GIT_SUCCESS; + return error; } static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) { - char object_path[GIT_PATH_MAX]; + git_buf object_path = GIT_BUF_INIT; + int error; assert(backend && oid); - return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS; + error = locate_object(&object_path, (loose_backend *)backend, oid); + + git_buf_free(&object_path); + + return (error == GIT_SUCCESS); } static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) @@ -658,30 +697,39 @@ static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) loose_backend *backend = (loose_backend *)_stream->backend; int error; - char final_path[GIT_PATH_MAX]; + git_buf final_path = GIT_BUF_INIT; if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write loose backend"); + goto cleanup; - if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) - return GIT_ENOMEM; + if ((error = object_file_name(&final_path, backend->objects_dir, oid)) < GIT_SUCCESS) + goto cleanup; - if ((error = git_futils_mkpath2file(final_path, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write loose backend"); + if ((error = git_buf_lasterror(&final_path)) < GIT_SUCCESS) + goto cleanup; - stream->finished = 1; + if ((error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS) + goto cleanup; /* * Don't try to add an existing object to the repository. This * is what git does and allows us to sidestep the fact that * we're not allowed to overwrite a read-only file on Windows. */ - if (git_futils_exists(final_path) == GIT_SUCCESS) { + if (git_futils_exists(final_path.ptr) == GIT_SUCCESS) { git_filebuf_cleanup(&stream->fbuf); - return GIT_SUCCESS; + goto cleanup; } - return git_filebuf_commit_at(&stream->fbuf, final_path, GIT_OBJECT_FILE_MODE); + error = git_filebuf_commit_at(&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE); + +cleanup: + git_buf_free(&final_path); + + if (error < GIT_SUCCESS) + git__rethrow(error, "Failed to write loose backend"); + + return error; } static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len) @@ -694,9 +742,7 @@ static void loose_backend__stream_free(git_odb_stream *_stream) { loose_writestream *stream = (loose_writestream *)_stream; - if (!stream->finished) - git_filebuf_cleanup(&stream->fbuf); - + git_filebuf_cleanup(&stream->fbuf); git__free(stream); } @@ -718,7 +764,8 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_ loose_backend *backend; loose_writestream *stream; - char hdr[64], tmp_path[GIT_PATH_MAX]; + char hdr[64]; + git_buf tmp_path = GIT_BUF_INIT; int hdrlen; int error; @@ -742,33 +789,38 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_ stream->stream.free = &loose_backend__stream_free; stream->stream.mode = GIT_STREAM_WRONLY; - git_path_join(tmp_path, backend->objects_dir, "tmp_object"); + error = git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object"); + if (error < GIT_SUCCESS) + goto cleanup; - error = git_filebuf_open(&stream->fbuf, tmp_path, + error = git_filebuf_open(&stream->fbuf, tmp_path.ptr, GIT_FILEBUF_HASH_CONTENTS | GIT_FILEBUF_TEMPORARY | (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)); - - if (error < GIT_SUCCESS) { - git__free(stream); - return git__rethrow(error, "Failed to create loose backend stream"); - } + if (error < GIT_SUCCESS) + goto cleanup; error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen); - if (error < GIT_SUCCESS) { - git_filebuf_cleanup(&stream->fbuf); - git__free(stream); - return git__rethrow(error, "Failed to create loose backend stream"); - } + if (error < GIT_SUCCESS) + goto cleanup; + + git_buf_free(&tmp_path); *stream_out = (git_odb_stream *)stream; return GIT_SUCCESS; + +cleanup: + git_buf_free(&tmp_path); + git_filebuf_cleanup(&stream->fbuf); + git__free(stream); + return git__rethrow(error, "Failed to create loose backend stream"); } static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type) { int error, header_len; - char final_path[GIT_PATH_MAX], header[64]; + git_buf final_path = GIT_BUF_INIT; + char header[64]; git_filebuf fbuf = GIT_FILEBUF_INIT; loose_backend *backend; @@ -781,30 +833,35 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v return GIT_EOBJCORRUPTED; } - git_path_join(final_path, backend->objects_dir, "tmp_object"); + error = git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object"); + if (error < GIT_SUCCESS) + goto cleanup; - error = git_filebuf_open(&fbuf, final_path, + error = git_filebuf_open(&fbuf, final_path.ptr, GIT_FILEBUF_HASH_CONTENTS | GIT_FILEBUF_TEMPORARY | (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)); - if (error < GIT_SUCCESS) - return error; + goto cleanup; git_filebuf_write(&fbuf, header, header_len); git_filebuf_write(&fbuf, data, len); git_filebuf_hash(oid, &fbuf); - if ((error = object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) < GIT_SUCCESS) + error = object_file_name(&final_path, backend->objects_dir, oid); + if (error < GIT_SUCCESS) goto cleanup; - if ((error = git_futils_mkpath2file(final_path, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS) + error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE); + if (error < GIT_SUCCESS) goto cleanup; - return git_filebuf_commit_at(&fbuf, final_path, GIT_OBJECT_FILE_MODE); + error = git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE); cleanup: - git_filebuf_cleanup(&fbuf); + if (error < GIT_SUCCESS) + git_filebuf_cleanup(&fbuf); + git_buf_free(&final_path); return error; } |