static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf) { git_buf str = GIT_BUF_INIT; char oid[GIT_OID_HEXSZ +1] = {0}; unsigned int len; if (caps->ofs_delta) git_buf_puts(&str, GIT_CAP_OFS_DELTA " "); if (caps->multi_ack) git_buf_puts(&str, GIT_CAP_MULTI_ACK " "); if (git_buf_oom(&str)) return -1; len = (unsigned int) (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + git_buf_len(&str) + 1 /* LF */); git_buf_grow(buf, git_buf_len(buf) + len); git_oid_fmt(oid, &head->oid); git_buf_printf(buf, "%04xwant %s %s\n", len, oid, git_buf_cstr(&str)); git_buf_free(&str); return git_buf_oom(buf); }
static int ident_insert_id( git_buf *to, const git_buf *from, const git_filter_source *src) { char oid[GIT_OID_HEXSZ+1]; const char *id_start, *id_end, *from_end = from->ptr + from->size; size_t need_size; /* replace $Id$ with blob id */ if (!git_filter_source_id(src)) return GIT_PASSTHROUGH; git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src)); if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0) return GIT_PASSTHROUGH; need_size = (size_t)(id_start - from->ptr) + 5 /* "$Id: " */ + GIT_OID_HEXSZ + 1 /* "$" */ + (size_t)(from_end - id_end); if (git_buf_grow(to, need_size) < 0) return -1; git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr)); git_buf_put(to, "$Id: ", 5); git_buf_put(to, oid, GIT_OID_HEXSZ); git_buf_putc(to, '$'); git_buf_put(to, id_end, (size_t)(from_end - id_end)); return git_buf_oom(to) ? -1 : 0; }
static int bitflip_filter_apply( git_filter *self, void **payload, git_buf *to, const git_buf *from, const git_filter_source *source) { const unsigned char *src = (const unsigned char *)from->ptr; unsigned char *dst; size_t i; GIT_UNUSED(self); GIT_UNUSED(payload); /* verify that attribute path match worked as expected */ cl_assert_equal_i( 0, git__strncmp("hero", git_filter_source_path(source), 4)); if (!from->size) return 0; cl_git_pass(git_buf_grow(to, from->size)); dst = (unsigned char *)to->ptr; for (i = 0; i < from->size; i++) dst[i] = VERY_SECURE_ENCRYPTION(src[i]); to->size = from->size; return 0; }
int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len) { git_zstream zs = GIT_ZSTREAM_INIT; int error = 0; if ((error = git_zstream_init(&zs)) < 0) return error; if ((error = git_zstream_set_input(&zs, in, in_len)) < 0) goto done; while (!git_zstream_done(&zs)) { size_t step = git_zstream_suggest_output_len(&zs), written; if ((error = git_buf_grow(out, out->size + step)) < 0) goto done; written = out->asize - out->size; if ((error = git_zstream_get_output( out->ptr + out->size, &written, &zs)) < 0) goto done; out->size += written; } /* NULL terminate for consistency if possible */ if (out->size < out->asize) out->ptr[out->size] = '\0'; done: git_zstream_free(&zs); return error; }
static int reverse_filter_apply( git_filter *self, void **payload, git_buf *to, const git_buf *from, const git_filter_source *source) { const unsigned char *src = (const unsigned char *)from->ptr; const unsigned char *end = src + from->size; unsigned char *dst; GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source); /* verify that attribute path match worked as expected */ cl_assert_equal_i( 0, git__strncmp("hero", git_filter_source_path(source), 4)); if (!from->size) return 0; cl_git_pass(git_buf_grow(to, from->size)); dst = (unsigned char *)to->ptr + from->size - 1; while (src < end) *dst-- = *src++; to->size = from->size; return 0; }
/* * Create a git protocol request. * * For example: git-upload-pack '/libgit2/libgit2' */ static int gen_proto(git_buf *request, const char *cmd, const char *url) { char *repo; int len; if (!git__prefixcmp(url, prefix_ssh)) { url = url + strlen(prefix_ssh); repo = strchr(url, '/'); } else { repo = strchr(url, ':'); if (repo) repo++; } if (!repo) { giterr_set(GITERR_NET, "Malformed git protocol URL"); return -1; } len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1; git_buf_grow(request, len); git_buf_printf(request, "%s '%s'", cmd, repo); git_buf_putc(request, '\0'); if (git_buf_oom(request)) return -1; return 0; }
int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) { ssize_t read_size = 0; size_t alloc_len; git_buf_clear(buf); if (!git__is_ssizet(len)) { giterr_set(GITERR_INVALID, "read too large"); return -1; } GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1); if (git_buf_grow(buf, alloc_len) < 0) return -1; /* p_read loops internally to read len bytes */ read_size = p_read(fd, buf->ptr, len); if (read_size != (ssize_t)len) { giterr_set(GITERR_OS, "failed to read descriptor"); git_buf_free(buf); return -1; } buf->ptr[read_size] = '\0'; buf->size = read_size; return 0; }
/* * Create a git protocol request. * * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 */ static int gen_proto(git_buf *request, const char *cmd, const char *url) { char *delim, *repo; char host[] = "host="; size_t len; delim = strchr(url, '/'); if (delim == NULL) { giterr_set(GITERR_NET, "Malformed URL"); return -1; } repo = delim; delim = strchr(url, ':'); if (delim == NULL) delim = strchr(url, '/'); len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1; git_buf_grow(request, len); git_buf_printf(request, "%04x%s %s%c%s", (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host); git_buf_put(request, url, delim - url); git_buf_putc(request, '\0'); if (git_buf_oom(request)) return -1; return 0; }
int git_buf_put_w(git_buf *buf, const wchar_t *string_w, size_t len_w) { int utf8_len, utf8_write_len; size_t new_size; if (!len_w) return 0; assert(string_w); /* Measure the string necessary for conversion */ if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string_w, len_w, NULL, 0, NULL, NULL)) == 0) return 0; assert(utf8_len > 0); GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, (size_t)utf8_len); GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1); if (git_buf_grow(buf, new_size) < 0) return -1; if ((utf8_write_len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, string_w, len_w, &buf->ptr[buf->size], utf8_len, NULL, NULL)) == 0) return handle_wc_error(); assert(utf8_write_len == utf8_len); buf->size += utf8_write_len; buf->ptr[buf->size] = '\0'; return 0; }
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) { unsigned int i; int error; git_buf tree = GIT_BUF_INIT; assert(bld); sort_entries(bld); /* Grow the buffer beforehand to an estimated size */ git_buf_grow(&tree, bld->entries.length * 72); for (i = 0; i < bld->entries.length; ++i) { git_tree_entry *entry = bld->entries.contents[i]; if (entry->removed) continue; git_buf_printf(&tree, "%o ", entry->attr); git_buf_put(&tree, entry->filename, entry->filename_len + 1); git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ); } if (git_buf_oom(&tree)) { git_buf_free(&tree); return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data"); } error = git_odb_write(oid, git_repository_database(repo), tree.ptr, tree.size, GIT_OBJ_TREE); git_buf_free(&tree); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree"); }
static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_buf *buf) { git_buf str = GIT_BUF_INIT; char oid[GIT_OID_HEXSZ +1] = {0}; unsigned int len; /* Prefer side-band-64k if the server supports both */ if (caps->side_band) { if (caps->side_band_64k) git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K); else git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND); } if (caps->ofs_delta) git_buf_puts(&str, GIT_CAP_OFS_DELTA " "); if (caps->multi_ack) git_buf_puts(&str, GIT_CAP_MULTI_ACK " "); if (caps->include_tag) git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " "); if (git_buf_oom(&str)) return -1; len = (unsigned int) (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + git_buf_len(&str) + 1 /* LF */); git_buf_grow(buf, git_buf_len(buf) + len); git_oid_fmt(oid, &head->oid); git_buf_printf(buf, "%04xwant %s %s\n", len, oid, git_buf_cstr(&str)); git_buf_free(&str); return git_buf_oom(buf); }
/** * If we make a ridiculously large request the first time we * actually allocate some space in the git_buf, the realloc() * will fail. And because the git_buf_grow() wrapper always * sets mark_oom, the code in git_buf_try_grow() will free * the internal buffer and set it to git_buf__oom. * * We initialized the internal buffer to (the static variable) * git_buf__initbuf. The purpose of this test is to make sure * that we don't try to free the static buffer. */ void test_buf_oom__grow(void) { git_buf buf = GIT_BUF_INIT; git_buf_clear(&buf); cl_assert(git_buf_grow(&buf, TOOBIG) == -1); cl_assert(git_buf_oom(&buf)); git_buf_free(&buf); }
void test_core_buffer__dont_grow_borrowed(void) { const char *somestring = "blah blah"; git_buf buf = GIT_BUF_INIT; git_buf_attach_notowned(&buf, somestring, strlen(somestring) + 1); cl_assert_equal_p(somestring, buf.ptr); cl_assert_equal_i(0, buf.asize); cl_assert_equal_i(strlen(somestring) + 1, buf.size); cl_git_fail_with(GIT_EINVALID, git_buf_grow(&buf, 1024)); }
int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) { char *nfd = *in, *nfc; size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv; int retry = 1; if (!ic || ic->map == (iconv_t)-1 || !git_path_has_non_ascii(*in, *inlen)) return 0; git_buf_clear(&ic->buf); while (1) { GITERR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1); if (git_buf_grow(&ic->buf, alloclen) < 0) return -1; nfc = ic->buf.ptr + ic->buf.size; nfclen = ic->buf.asize - ic->buf.size; rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen); ic->buf.size = (nfc - ic->buf.ptr); if (rv != (size_t)-1) break; /* if we cannot convert the data (probably because iconv thinks * it is not valid UTF-8 source data), then use original data */ if (errno != E2BIG) return 0; /* make space for 2x the remaining data to be converted * (with per retry overhead to avoid infinite loops) */ wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4); if (retry++ > 4) goto fail; } ic->buf.ptr[ic->buf.size] = '\0'; *in = ic->buf.ptr; *inlen = ic->buf.size; return 0; fail: giterr_set(GITERR_OS, "Unable to convert unicode path data"); return -1; }
int git_buf_text_puts_escaped( git_buf *buf, const char *string, const char *esc_chars, const char *esc_with) { const char *scan; size_t total = 0, esc_len = strlen(esc_with), count; if (!string) return 0; for (scan = string; *scan; ) { /* count run of non-escaped characters */ count = strcspn(scan, esc_chars); total += count; scan += count; /* count run of escaped characters */ count = strspn(scan, esc_chars); total += count * (esc_len + 1); scan += count; } if (git_buf_grow(buf, buf->size + total + 1) < 0) return -1; for (scan = string; *scan; ) { count = strcspn(scan, esc_chars); memmove(buf->ptr + buf->size, scan, count); scan += count; buf->size += count; for (count = strspn(scan, esc_chars); count > 0; --count) { /* copy escape sequence */ memmove(buf->ptr + buf->size, esc_with, esc_len); buf->size += esc_len; /* copy character to be escaped */ buf->ptr[buf->size] = *scan; buf->size++; scan++; } } buf->ptr[buf->size] = '\0'; return 0; }
int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src) { const char *scan = src->ptr; const char *scan_end = src->ptr + src->size; const char *next = memchr(scan, '\r', src->size); size_t new_size; char *out; assert(tgt != src); if (!next) return git_buf_set(tgt, src->ptr, src->size); /* reduce reallocs while in the loop */ GITERR_CHECK_ALLOC_ADD(&new_size, src->size, 1); if (git_buf_grow(tgt, new_size) < 0) return -1; out = tgt->ptr; tgt->size = 0; /* Find the next \r and copy whole chunk up to there to tgt */ for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) { if (next > scan) { size_t copylen = (size_t)(next - scan); memcpy(out, scan, copylen); out += copylen; } /* Do not drop \r unless it is followed by \n */ if (next + 1 == scan_end || next[1] != '\n') *out++ = '\r'; } /* Copy remaining input into dest */ if (scan < scan_end) { size_t remaining = (size_t)(scan_end - scan); memcpy(out, scan, remaining); out += remaining; } tgt->size = (size_t)(out - tgt->ptr); tgt->ptr[tgt->size] = '\0'; return 0; }
int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src) { const char *start = src->ptr; const char *end = start + src->size; const char *scan = start; const char *next = memchr(scan, '\n', src->size); size_t alloclen; assert(tgt != src); if (!next) return git_buf_set(tgt, src->ptr, src->size); /* attempt to reduce reallocs while in the loop */ GITERR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4); GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); if (git_buf_grow(tgt, alloclen) < 0) return -1; tgt->size = 0; for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) { size_t copylen = next - scan; /* if we find mixed line endings, bail */ if (next > start && next[-1] == '\r') { git_buf_free(tgt); return GIT_PASSTHROUGH; } GITERR_CHECK_ALLOC_ADD(&alloclen, copylen, 3); if (git_buf_grow_by(tgt, alloclen) < 0) return -1; if (next > scan) { memcpy(tgt->ptr + tgt->size, scan, copylen); tgt->size += copylen; } tgt->ptr[tgt->size++] = '\r'; tgt->ptr[tgt->size++] = '\n'; } tgt->ptr[tgt->size] = '\0'; return git_buf_put(tgt, scan, end - scan); }
int git_object_short_id(git_buf *out, const git_object *obj) { git_repository *repo; int len = GIT_ABBREV_DEFAULT, error; git_oid id = {{0}}; git_odb *odb; assert(out && obj); git_buf_sanitize(out); repo = git_object_owner(obj); if ((error = git_repository__cvar(&len, repo, GIT_CVAR_ABBREV)) < 0) return error; if ((error = git_repository_odb(&odb, repo)) < 0) return error; while (len < GIT_OID_HEXSZ) { /* set up short oid */ memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2); if (len & 1) id.id[len / 2] &= 0xf0; error = git_odb_exists_prefix(NULL, odb, &id, len); if (error != GIT_EAMBIGUOUS) break; giterr_clear(); len++; } if (!error && !(error = git_buf_grow(out, len + 1))) { git_oid_tostr(out->ptr, len + 1, &id); out->size = len; } git_odb_free(odb); return error; }
static int index_path(git_buf *path, git_indexer *idx, const char *suffix) { const char prefix[] = "pack-"; size_t slash = (size_t)path->size; /* search backwards for '/' */ while (slash > 0 && path->ptr[slash - 1] != '/') slash--; if (git_buf_grow(path, slash + 1 + strlen(prefix) + GIT_OID_HEXSZ + strlen(suffix) + 1) < 0) return -1; git_buf_truncate(path, slash); git_buf_puts(path, prefix); git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash); path->size += GIT_OID_HEXSZ; git_buf_puts(path, suffix); return git_buf_oom(path) ? -1 : 0; }
static int ident_remove_id( git_buf *to, const git_buf *from) { const char *id_start, *id_end, *from_end = from->ptr + from->size; size_t need_size; if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0) return GIT_PASSTHROUGH; need_size = (size_t)(id_start - from->ptr) + 4 /* "$Id$" */ + (size_t)(from_end - id_end); if (git_buf_grow(to, need_size) < 0) return -1; git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr)); git_buf_put(to, "$Id$", 4); git_buf_put(to, id_end, (size_t)(from_end - id_end)); return git_buf_oom(to) ? -1 : 0; }
static int object_file_name( git_buf *name, const loose_backend *be, const git_oid *id) { size_t alloclen; /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */ GITERR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3); if (git_buf_grow(name, alloclen) < 0) return -1; git_buf_set(name, be->objects_dir, be->objects_dirlen); git_path_to_dir(name); /* loose object filename: aa/aaa... (41 bytes) */ git_oid_pathfmt(name->ptr + name->size, id); name->size += GIT_OID_HEXSZ + 1; name->ptr[name->size] = '\0'; return 0; }
/* * Create a git protocol request. * * For example: git-upload-pack '/libgit2/libgit2' */ static int gen_proto(git_buf *request, const char *cmd, const char *url) { const char *repo; int len; size_t i; for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) { const char *p = ssh_prefixes[i]; if (!git__prefixcmp(url, p)) { url = url + strlen(p); repo = strchr(url, '/'); if (repo && repo[1] == '~') ++repo; goto done; } } repo = strchr(url, ':'); if (repo) repo++; done: if (!repo) { giterr_set(GITERR_NET, "malformed git protocol URL"); return -1; } len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1; git_buf_grow(request, len); git_buf_puts(request, cmd); git_buf_puts(request, " '"); git_buf_decode_percent(request, repo, strlen(repo)); git_buf_puts(request, "'"); if (git_buf_oom(request)) return -1; return 0; }
int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) { ssize_t read_size = 0; git_buf_clear(buf); if (git_buf_grow(buf, len + 1) < 0) return -1; /* p_read loops internally to read len bytes */ read_size = p_read(fd, buf->ptr, len); if (read_size != (ssize_t)len) { giterr_set(GITERR_OS, "Failed to read descriptor"); return -1; } buf->ptr[read_size] = '\0'; buf->size = read_size; return 0; }
int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated) { git_file fd; size_t len; struct stat st; assert(buf && path && *path); if (updated != NULL) *updated = 0; if ((fd = git_futils_open_ro(path)) < 0) return fd; if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) { p_close(fd); giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path); return -1; } /* * If we were given a time, we only want to read the file if it * has been modified. */ if (mtime != NULL && *mtime >= st.st_mtime) { p_close(fd); return 0; } if (mtime != NULL) *mtime = st.st_mtime; len = (size_t) st.st_size; git_buf_clear(buf); if (git_buf_grow(buf, len + 1) < 0) { p_close(fd); return -1; } buf->ptr[len] = '\0'; while (len > 0) { ssize_t read_size = p_read(fd, buf->ptr, len); if (read_size < 0) { p_close(fd); giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path); return -1; } len -= read_size; buf->size += read_size; } p_close(fd); if (updated != NULL) *updated = 1; return 0; }
static int parse_section_header_ext(git_config_parser *reader, const char *line, const char *base_name, char **section_name) { int c, rpos; char *first_quote, *last_quote; git_buf buf = GIT_BUF_INIT; size_t quoted_len, alloc_len, base_name_len = strlen(base_name); /* * base_name is what came before the space. We should be at the * first quotation mark, except for now, line isn't being kept in * sync so we only really use it to calculate the length. */ first_quote = strchr(line, '"'); if (first_quote == NULL) { set_parse_error(reader, 0, "Missing quotation marks in section header"); goto end_error; } last_quote = strrchr(line, '"'); quoted_len = last_quote - first_quote; if (quoted_len == 0) { set_parse_error(reader, 0, "Missing closing quotation mark in section header"); goto end_error; } GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); if (git_buf_grow(&buf, alloc_len) < 0 || git_buf_printf(&buf, "%s.", base_name) < 0) goto end_error; rpos = 0; line = first_quote; c = line[++rpos]; /* * At the end of each iteration, whatever is stored in c will be * added to the string. In case of error, jump to out */ do { switch (c) { case 0: set_parse_error(reader, 0, "Unexpected end-of-line in section header"); goto end_error; case '"': goto end_parse; case '\\': c = line[++rpos]; if (c == 0) { set_parse_error(reader, rpos, "Unexpected end-of-line in section header"); goto end_error; } default: break; } git_buf_putc(&buf, (char)c); c = line[++rpos]; } while (line + rpos < last_quote); end_parse: if (git_buf_oom(&buf)) goto end_error; if (line[rpos] != '"' || line[rpos + 1] != ']') { set_parse_error(reader, rpos, "Unexpected text after closing quotes"); git_buf_free(&buf); return -1; } *section_name = git_buf_detach(&buf); return 0; end_error: git_buf_free(&buf); return -1; }
/* Locate an object matching a given short oid */ static int locate_object_short_oid( git_buf *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, size_t len) { char *objects_dir = backend->objects_dir; size_t dir_len = strlen(objects_dir), alloc_len; loose_locate_object_state state; int error; /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_buf_set(object_location, objects_dir, dir_len); git_path_to_dir(object_location); /* save adjusted position at end of dir so it can be restored later */ dir_len = git_buf_len(object_location); /* 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 */ if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0) return -1; object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ if (git_path_isdir(object_location->ptr) == false) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); state.dir_len = git_buf_len(object_location); state.short_oid_len = len; state.found = 0; /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); if (state.found > 1) return git_odb__error_ambiguous("multiple matches in loose objects"); /* Convert obtained hex formatted oid to raw */ error = git_oid_fromstr(res_oid, (char *)state.res_oid); if (error) return error; /* Update the location according to the oid obtained */ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); git_buf_truncate(object_location, dir_len); if (git_buf_grow(object_location, alloc_len) < 0) return -1; 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 0; }