summaryrefslogtreecommitdiff
path: root/src/index.c
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2014-03-14 13:20:51 -0700
committerRussell Belfer <rb@github.com>2014-04-17 14:43:45 -0700
commit8a2834d34173220c56bd1898397c0e6d200f327d (patch)
tree62d91bb19bc017a7bb5eb829bf2e52f04581d98c /src/index.c
parent40ed499039f887ebcb0b5badf0157519148398b8 (diff)
downloadlibgit2-8a2834d34173220c56bd1898397c0e6d200f327d.tar.gz
Index locking and entry allocation changes
This makes the lock management on the index a little bit broader, having a number of routines hold the lock across looking up the item to be modified and actually making the modification. Still not true thread safety, but more pure index modifications are now safe which allows the simple cases (such as starting up a diff while index modifications are underway) safe enough to get the snapshot without hitting allocation problems. As part of this, I simplified the allocation of index entries to use a flex array and just put the path at the end of the index entry. This makes every entry self-contained and makes it a little easier to feel sure that pointers to strings aren't being accidentally copied and freed while other references are still being held.
Diffstat (limited to 'src/index.c')
-rw-r--r--src/index.c369
1 files changed, 205 insertions, 164 deletions
diff --git a/src/index.c b/src/index.c
index d733e4e48..094328b95 100644
--- a/src/index.c
+++ b/src/index.c
@@ -90,13 +90,24 @@ struct entry_long {
struct entry_srch_key {
const char *path;
- size_t path_len;
+ size_t pathlen;
int stage;
};
+struct entry_internal {
+ git_index_entry entry;
+ size_t pathlen;
+ char path[GIT_FLEX_ARRAY];
+};
+
+struct reuc_entry_internal {
+ git_index_reuc_entry entry;
+ size_t pathlen;
+ char path[GIT_FLEX_ARRAY];
+};
+
/* local declarations */
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
-static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
static int read_header(struct index_header *dest, const void *buffer);
static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
@@ -109,12 +120,12 @@ static void index_entry_reuc_free(git_index_reuc_entry *reuc);
int git_index_entry_srch(const void *key, const void *array_member)
{
const struct entry_srch_key *srch_key = key;
- const git_index_entry *entry = array_member;
+ const struct entry_internal *entry = array_member;
int cmp;
size_t len1, len2, len;
- len1 = srch_key->path_len;
- len2 = strlen(entry->path);
+ len1 = srch_key->pathlen;
+ len2 = entry->pathlen;
len = len1 < len2 ? len1 : len2;
cmp = memcmp(srch_key->path, entry->path, len);
@@ -126,7 +137,7 @@ int git_index_entry_srch(const void *key, const void *array_member)
return 1;
if (srch_key->stage != GIT_INDEX_STAGE_ANY)
- return srch_key->stage - GIT_IDXENTRY_STAGE(entry);
+ return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry);
return 0;
}
@@ -134,12 +145,12 @@ int git_index_entry_srch(const void *key, const void *array_member)
int git_index_entry_isrch(const void *key, const void *array_member)
{
const struct entry_srch_key *srch_key = key;
- const git_index_entry *entry = array_member;
+ const struct entry_internal *entry = array_member;
int cmp;
size_t len1, len2, len;
- len1 = srch_key->path_len;
- len2 = strlen(entry->path);
+ len1 = srch_key->pathlen;
+ len2 = entry->pathlen;
len = len1 < len2 ? len1 : len2;
cmp = strncasecmp(srch_key->path, entry->path, len);
@@ -152,7 +163,7 @@ int git_index_entry_isrch(const void *key, const void *array_member)
return 1;
if (srch_key->stage != GIT_INDEX_STAGE_ANY)
- return srch_key->stage - GIT_IDXENTRY_STAGE(entry);
+ return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry);
return 0;
}
@@ -276,17 +287,11 @@ static int reuc_icmp(const void *a, const void *b)
static void index_entry_reuc_free(git_index_reuc_entry *reuc)
{
- if (!reuc)
- return;
- git__free(reuc->path);
git__free(reuc);
}
static void index_entry_free(git_index_entry *entry)
{
- if (!entry)
- return;
- git__free(entry->path);
git__free(entry);
}
@@ -447,18 +452,22 @@ void git_index_free(git_index *index)
/* call with locked index */
static void index_free_deleted(git_index *index)
{
+ int readers = (int)git_atomic_get(&index->readers);
size_t i;
- if (git_atomic_get(&index->readers) > 0)
+ if (readers > 0)
return;
- for (i = 0; i < index->deleted.length; ++i)
- index_entry_free(git__swap(index->deleted.contents[i], NULL));
+ for (i = 0; i < index->deleted.length; ++i) {
+ git_index_entry *ie = git__swap(index->deleted.contents[i], NULL);
+ index_entry_free(ie);
+ }
git_vector_clear(&index->deleted);
}
-static int index_remove_entry(git_index *index, size_t pos, bool need_lock)
+/* call with locked index */
+static int index_remove_entry(git_index *index, size_t pos)
{
int error = 0;
git_index_entry *entry = git_vector_get(&index->entries, pos);
@@ -466,23 +475,17 @@ static int index_remove_entry(git_index *index, size_t pos, bool need_lock)
if (entry != NULL)
git_tree_cache_invalidate_path(index->tree, entry->path);
- if (need_lock && git_mutex_lock(&index->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to lock index");
- return -1;
- }
-
error = git_vector_remove(&index->entries, pos);
if (!error) {
- if (!git_atomic_get(&index->readers))
- index_entry_free(entry);
- else
+ int readers = (int)git_atomic_get(&index->readers);
+
+ if (readers > 0)
error = git_vector_insert(&index->deleted, entry);
+ else
+ index_entry_free(entry);
}
- if (need_lock)
- git_mutex_unlock(&index->lock);
-
return error;
}
@@ -501,7 +504,7 @@ int git_index_clear(git_index *index)
}
while (!error && index->entries.length > 0)
- error = index_remove_entry(index, index->entries.length - 1, false);
+ error = index_remove_entry(index, index->entries.length - 1);
index_free_deleted(index);
git_mutex_unlock(&index->lock);
@@ -684,6 +687,16 @@ size_t git_index_entrycount(const git_index *index)
return index->entries.length;
}
+GIT_INLINE(int) git_index__find_internal(
+ size_t *out, git_index *index, const char *path, size_t path_len, int stage,
+ bool need_lock)
+{
+ if (index_sort_if_needed(index, need_lock) < 0)
+ return -1;
+ return git_index__find_in_entries(
+ out, &index->entries, index->entries_search, path, path_len, stage);
+}
+
const git_index_entry *git_index_get_byindex(
git_index *index, size_t n)
{
@@ -700,7 +713,7 @@ const git_index_entry *git_index_get_bypath(
assert(index);
- if (git_index__find(&pos, index, path, 0, stage) < 0) {
+ if (git_index__find_internal(&pos, index, path, 0, stage, true) < 0) {
giterr_set(GITERR_INDEX, "Index does not contain %s", path);
return NULL;
}
@@ -724,6 +737,21 @@ void git_index_entry__init_from_stat(
entry->file_size = st->st_size;
}
+static git_index_entry *index_entry_alloc(const char *path)
+{
+ size_t pathlen = strlen(path);
+ struct entry_internal *entry =
+ git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
+ if (!entry)
+ return NULL;
+
+ entry->pathlen = pathlen;
+ memcpy(entry->path, path, pathlen);
+ entry->entry.path = entry->path;
+
+ return (git_index_entry *)entry;
+}
+
static int index_entry_init(
git_index_entry **entry_out, git_index *index, const char *rel_path)
{
@@ -743,22 +771,31 @@ static int index_entry_init(
if (error < 0)
return error;
- entry = git__calloc(1, sizeof(git_index_entry));
+ entry = index_entry_alloc(rel_path);
GITERR_CHECK_ALLOC(entry);
- git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
-
entry->id = oid;
- entry->path = git__strdup(rel_path);
- if (!entry->path) {
- git__free(entry);
- return -1;
- }
+ git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
- *entry_out = entry;
+ *entry_out = (git_index_entry *)entry;
return 0;
}
+static git_index_reuc_entry *reuc_entry_alloc(const char *path)
+{
+ size_t pathlen = strlen(path);
+ struct reuc_entry_internal *entry =
+ git__calloc(sizeof(struct reuc_entry_internal) + pathlen + 1, 1);
+ if (!entry)
+ return NULL;
+
+ entry->pathlen = pathlen;
+ memcpy(entry->path, path, pathlen);
+ entry->entry.path = entry->path;
+
+ return (git_index_reuc_entry *)entry;
+}
+
static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
const char *path,
int ancestor_mode, const git_oid *ancestor_oid,
@@ -769,17 +806,9 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
assert(reuc_out && path);
- *reuc_out = NULL;
-
- reuc = git__calloc(1, sizeof(git_index_reuc_entry));
+ *reuc_out = reuc = reuc_entry_alloc(path);
GITERR_CHECK_ALLOC(reuc);
- reuc->path = git__strdup(path);
- if (reuc->path == NULL) {
- git__free(reuc);
- return -1;
- }
-
if ((reuc->mode[0] = ancestor_mode) > 0)
git_oid_cpy(&reuc->oid[0], ancestor_oid);
@@ -789,10 +818,16 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
if ((reuc->mode[2] = their_mode) > 0)
git_oid_cpy(&reuc->oid[2], their_oid);
- *reuc_out = reuc;
return 0;
}
+static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
+{
+ char *tgt_path = tgt->path;
+ memcpy(tgt, src, sizeof(*tgt));
+ tgt->path = tgt_path; /* reset to existing path data */
+}
+
static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
{
git_index_entry *entry;
@@ -802,19 +837,10 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
return 0;
}
- entry = git__malloc(sizeof(git_index_entry));
+ *out = entry = index_entry_alloc(src->path);
GITERR_CHECK_ALLOC(entry);
- memcpy(entry, src, sizeof(git_index_entry));
-
- /* duplicate the path string so we own it */
- entry->path = git__strdup(entry->path);
- if (!entry->path) {
- git__free(entry);
- return -1;
- }
-
- *out = entry;
+ index_entry_cpy(entry, src);
return 0;
}
@@ -827,13 +853,13 @@ static int has_file_name(git_index *index,
const char *name = entry->path;
while (pos < index->entries.length) {
- git_index_entry *p = index->entries.contents[pos++];
+ struct entry_internal *p = index->entries.contents[pos++];
- if (len >= strlen(p->path))
+ if (len >= p->pathlen)
break;
if (memcmp(name, p->path, len))
break;
- if (GIT_IDXENTRY_STAGE(p) != stage)
+ if (GIT_IDXENTRY_STAGE(&p->entry) != stage)
continue;
if (p->path[len] != '/')
continue;
@@ -841,7 +867,7 @@ static int has_file_name(git_index *index,
if (!ok_to_replace)
break;
- if (index_remove_entry(index, --pos, true) < 0)
+ if (index_remove_entry(index, --pos) < 0)
break;
}
return retval;
@@ -860,7 +886,7 @@ static int has_dir_name(git_index *index,
const char *slash = name + strlen(name);
for (;;) {
- size_t len, position;
+ size_t len, pos;
for (;;) {
if (*--slash == '/')
@@ -870,12 +896,12 @@ static int has_dir_name(git_index *index,
}
len = slash - name;
- if (git_index__find(&position, index, name, len, stage) == 0) {
+ if (!git_index__find_internal(&pos, index, name, len, stage, false)) {
retval = -1;
if (!ok_to_replace)
break;
- if (index_remove_entry(index, position, true) < 0)
+ if (index_remove_entry(index, pos) < 0)
break;
continue;
}
@@ -885,20 +911,19 @@ static int has_dir_name(git_index *index,
* already matches the sub-directory, then we know
* we're ok, and we can exit.
*/
- while (position < index->entries.length) {
- git_index_entry *p = index->entries.contents[position];
+ for (; pos < index->entries.length; ++pos) {
+ struct entry_internal *p = index->entries.contents[pos];
- if ((strlen(p->path) <= len) ||
- (p->path[len] != '/') ||
+ if (p->pathlen <= len ||
+ p->path[len] != '/' ||
memcmp(p->path, name, len))
break; /* not our subdirectory */
- if (GIT_IDXENTRY_STAGE(p) == stage)
+ if (GIT_IDXENTRY_STAGE(&p->entry) == stage)
return retval;
-
- position++;
}
}
+
return retval;
}
@@ -909,7 +934,8 @@ static int check_file_directory_collision(git_index *index,
retval = retval + has_dir_name(index, entry, ok_to_replace);
if (retval) {
- giterr_set(GITERR_INDEX, "'%s' appears as both a file an a directory", entry->path);
+ giterr_set(GITERR_INDEX,
+ "'%s' appears as both a file and a directory", entry->path);
return -1;
}
@@ -931,10 +957,9 @@ static int index_insert(
assert(index && entry_ptr);
entry = *entry_ptr;
- assert(entry && entry->path);
/* make sure that the path length flag is correct */
- path_length = strlen(entry->path);
+ path_length = ((struct entry_internal *)entry)->pathlen;
entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
@@ -943,43 +968,43 @@ static int index_insert(
else
entry->flags |= GIT_IDXENTRY_NAMEMASK;
+ if ((error = git_mutex_lock(&index->lock)) < 0) {
+ giterr_set(GITERR_OS, "Unable to acquire index lock");
+ return error;
+ }
+
+ git_vector_sort(&index->entries);
+
/* look if an entry with this path already exists */
- if (!git_index__find(
- &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry))) {
+ if (!git_index__find_internal(
+ &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) {
existing = index->entries.contents[position];
/* update filemode to existing values if stat is not trusted */
entry->mode = index_merge_mode(index, existing, entry->mode);
}
+ /* look for tree / blob name collisions, removing conflicts if requested */
error = check_file_directory_collision(index, entry, position, replace);
if (error < 0)
- goto done;
+ /* skip changes */;
/* if we are replacing an existing item, overwrite the existing entry
* and return it in place of the passed in one.
*/
- if (existing && replace) {
- git__free(entry->path);
- entry->path = existing->path;
-
- memcpy(existing, entry, sizeof(*entry));
+ else if (existing && replace) {
+ index_entry_cpy(existing, entry);
+ index_entry_free(entry);
*entry_ptr = existing;
-
- git__free(entry);
- return 0;
}
-
- /* if replacing is not requested or no existing entry exists, just
- * insert entry at the end; the index is no longer sorted
- */
- if ((error = git_mutex_lock(&index->lock)) < 0) {
- giterr_set(GITERR_OS, "Unable to acquire index lock");
- } else {
+ else {
+ /* if replacing is not requested or no existing entry exists, just
+ * insert entry at the end; the index is no longer sorted
+ */
error = git_vector_insert(&index->entries, entry);
- git_mutex_unlock(&index->lock);
}
-done:
+ git_mutex_unlock(&index->lock);
+
if (error < 0) {
index_entry_free(*entry_ptr);
*entry_ptr = NULL;
@@ -1053,7 +1078,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
git_index_entry *entry = NULL;
int ret;
- assert(index && source_entry);
+ assert(index && source_entry && source_entry->path);
if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
(ret = index_insert(index, &entry, 1)) < 0)
@@ -1065,15 +1090,24 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
int git_index_remove(git_index *index, const char *path, int stage)
{
+ int error;
size_t position;
- if (git_index__find(&position, index, path, 0, stage) < 0) {
- giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d",
- path, stage);
- return GIT_ENOTFOUND;
+ if (git_mutex_lock(&index->lock) < 0) {
+ giterr_set(GITERR_OS, "Failed to lock index");
+ return -1;
+ }
+
+ if (git_index__find_internal(&position, index, path, 0, stage, false) < 0) {
+ giterr_set(
+ GITERR_INDEX, "Index does not contain %s at stage %d", path, stage);
+ error = GIT_ENOTFOUND;
+ } else {
+ error = index_remove_entry(index, position);
}
- return index_remove_entry(index, position, true);
+ git_mutex_unlock(&index->lock);
+ return error;
}
int git_index_remove_directory(git_index *index, const char *dir, int stage)
@@ -1083,12 +1117,17 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
size_t pos;
git_index_entry *entry;
- if (git_buf_sets(&pfx, dir) < 0 || git_path_to_dir(&pfx) < 0)
+ if (git_mutex_lock(&index->lock) < 0) {
+ giterr_set(GITERR_OS, "Failed to lock index");
return -1;
+ }
- git_index__find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY);
+ if (!(error = git_buf_sets(&pfx, dir)) &&
+ !(error = git_path_to_dir(&pfx)))
+ git_index__find_internal(
+ &pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY, false);
- while (1) {
+ while (!error) {
entry = git_vector_get(&index->entries, pos);
if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0)
break;
@@ -1098,12 +1137,12 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
continue;
}
- if ((error = index_remove_entry(index, pos, true)) < 0)
- break;
+ error = index_remove_entry(index, pos);
- /* removed entry at 'pos' so we don't need to increment it */
+ /* removed entry at 'pos' so we don't need to increment */
}
+ git_mutex_unlock(&index->lock);
git_buf_free(&pfx);
return error;
@@ -1115,7 +1154,7 @@ int git_index__find_in_entries(
{
struct entry_srch_key srch_key;
srch_key.path = path;
- srch_key.path_len = !path_len ? strlen(path) : path_len;
+ srch_key.pathlen = !path_len ? strlen(path) : path_len;
srch_key.stage = stage;
return git_vector_bsearch2(out, entries, entry_srch, &srch_key);
}
@@ -1124,12 +1163,7 @@ int git_index__find(
size_t *out, git_index *index, const char *path, size_t path_len, int stage)
{
assert(index && path);
-
- if (index_sort_if_needed(index, true) < 0)
- return -1;
-
- return git_index__find_in_entries(
- out, &index->entries, index->entries_search, path, path_len, stage);
+ return git_index__find_internal(out, index, path, path_len, stage, true);
}
int git_index_find(size_t *at_pos, git_index *index, const char *path)
@@ -1138,7 +1172,13 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path)
assert(index && path);
+ if (git_mutex_lock(&index->lock) < 0) {
+ giterr_set(GITERR_OS, "Failed to lock index");
+ return -1;
+ }
+
if (git_vector_bsearch2(&pos, &index->entries, index->entries_search_path, path) < 0) {
+ git_mutex_unlock(&index->lock);
giterr_set(GITERR_INDEX, "Index does not contain %s", path);
return GIT_ENOTFOUND;
}
@@ -1158,6 +1198,7 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path)
if (at_pos)
*at_pos = pos;
+ git_mutex_unlock(&index->lock);
return 0;
}
@@ -1288,6 +1329,11 @@ int git_index_conflict_remove(git_index *index, const char *path)
if (git_index_find(&pos, index, path) < 0)
return GIT_ENOTFOUND;
+ if (git_mutex_lock(&index->lock) < 0) {
+ giterr_set(GITERR_OS, "Unable to lock index");
+ return -1;
+ }
+
while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) {
if (index->entries_cmp_path(conflict_entry->path, path) != 0)
@@ -1298,10 +1344,12 @@ int git_index_conflict_remove(git_index *index, const char *path)
continue;
}
- if ((error = index_remove_entry(index, pos, true)) < 0)
+ if ((error = index_remove_entry(index, pos)) < 0)
break;
}
+ git_mutex_unlock(&index->lock);
+
return error;
}
@@ -1311,7 +1359,7 @@ static int index_conflicts_match(const git_vector *v, size_t idx, void *p)
git_index_entry *entry = git_vector_get(v, idx);
if (GIT_IDXENTRY_STAGE(entry) > 0 &&
- !index_remove_entry(index, idx, false))
+ !index_remove_entry(index, idx))
return 1;
return 0;
@@ -1488,7 +1536,6 @@ static int index_reuc_insert(
return git_vector_insert(&index->reuc, reuc);
/* exists, replace it */
- git__free((*existing)->path);
git__free(*existing);
*existing = reuc;
@@ -1596,13 +1643,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
if (size <= len)
return index_error_invalid("reading reuc entries");
- lost = git__calloc(1, sizeof(git_index_reuc_entry));
+ lost = reuc_entry_alloc(buffer);
GITERR_CHECK_ALLOC(lost);
- /* read NUL-terminated pathname for entry */
- lost->path = git__strdup(buffer);
- GITERR_CHECK_ALLOC(lost->path);
-
size -= len;
buffer += len;
@@ -1700,41 +1743,41 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
return 0;
}
-static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
+static size_t read_entry(
+ git_index_entry **out, const void *buffer, size_t buffer_size)
{
size_t path_length, entry_size;
uint16_t flags_raw;
const char *path_ptr;
const struct entry_short *source = buffer;
+ git_index_entry entry = {{0}};
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
return 0;
- memset(dest, 0x0, sizeof(git_index_entry));
-
- dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
- dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
- dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
- dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
- dest->dev = ntohl(source->dev);
- dest->ino = ntohl(source->ino);
- dest->mode = ntohl(source->mode);
- dest->uid = ntohl(source->uid);
- dest->gid = ntohl(source->gid);
- dest->file_size = ntohl(source->file_size);
- git_oid_cpy(&dest->id, &source->oid);
- dest->flags = ntohs(source->flags);
-
- if (dest->flags & GIT_IDXENTRY_EXTENDED) {
+ entry.ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
+ entry.ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
+ entry.mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
+ entry.mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
+ entry.dev = ntohl(source->dev);
+ entry.ino = ntohl(source->ino);
+ entry.mode = ntohl(source->mode);
+ entry.uid = ntohl(source->uid);
+ entry.gid = ntohl(source->gid);
+ entry.file_size = ntohl(source->file_size);
+ git_oid_cpy(&entry.id, &source->oid);
+ entry.flags = ntohs(source->flags);
+
+ if (entry.flags & GIT_IDXENTRY_EXTENDED) {
const struct entry_long *source_l = (const struct entry_long *)source;
path_ptr = source_l->path;
flags_raw = ntohs(source_l->flags_extended);
- memcpy(&dest->flags_extended, &flags_raw, 2);
+ memcpy(&entry.flags_extended, &flags_raw, 2);
} else
path_ptr = source->path;
- path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;
+ path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
/* if this is a very long string, we must find its
* real length without overflowing */
@@ -1748,7 +1791,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
path_length = path_end - path_ptr;
}
- if (dest->flags & GIT_IDXENTRY_EXTENDED)
+ if (entry.flags & GIT_IDXENTRY_EXTENDED)
entry_size = long_entry_size(path_length);
else
entry_size = short_entry_size(path_length);
@@ -1756,8 +1799,10 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
return 0;
- dest->path = git__strdup(path_ptr);
- assert(dest->path);
+ entry.path = (char *)path_ptr;
+
+ if (index_entry_dup(out, &entry) < 0)
+ return 0;
return entry_size;
}
@@ -1853,20 +1898,12 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
return -1;
}
- git_vector_clear(&index->entries);
+ assert(!index->entries.length);
/* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
- size_t entry_size;
git_index_entry *entry;
-
- entry = git__malloc(sizeof(git_index_entry));
- if (!entry) {
- error = -1;
- goto done;
- }
-
- entry_size = read_entry(entry, buffer, buffer_size);
+ size_t entry_size = read_entry(&entry, buffer, buffer_size);
/* 0 bytes read means an object corruption */
if (entry_size == 0) {
@@ -1874,8 +1911,10 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
goto done;
}
- if ((error = git_vector_insert(&index->entries, entry)) < 0)
+ if ((error = git_vector_insert(&index->entries, entry)) < 0) {
+ index_entry_free(entry);
goto done;
+ }
seek_forward(entry_size);
}
@@ -1953,7 +1992,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
size_t path_len, disk_size;
char *path;
- path_len = strlen(entry->path);
+ path_len = ((struct entry_internal *)entry)->pathlen;
if (entry->flags & GIT_IDXENTRY_EXTENDED)
disk_size = long_entry_size(path_len);
@@ -2222,7 +2261,7 @@ static int read_tree_cb(
if (git_buf_joinpath(&path, root, tentry->filename) < 0)
return -1;
- entry = git__calloc(1, sizeof(git_index_entry));
+ entry = index_entry_alloc(path.ptr);
GITERR_CHECK_ALLOC(entry);
entry->mode = tentry->attr;
@@ -2236,7 +2275,9 @@ static int read_tree_cb(
entry->mode == old_entry->mode &&
git_oid_equal(&entry->id, &old_entry->id))
{
+ char *oldpath = entry->path;
memcpy(entry, old_entry, sizeof(*entry));
+ entry->path = oldpath;
entry->flags_extended = 0;
}
@@ -2245,7 +2286,6 @@ static int read_tree_cb(
else
entry->flags = GIT_IDXENTRY_NAMEMASK;
- entry->path = git_buf_detach(&path);
git_buf_free(&path);
if (git_vector_insert(data->new_entries, entry) < 0) {
@@ -2543,10 +2583,11 @@ int git_index__snapshot(git_vector *entries, git_index *index)
void git_index__release_snapshot(git_index *index)
{
git_atomic_dec(&index->readers);
- git_index_free(index);
if (!git_mutex_lock(&index->lock)) {
index_free_deleted(index); /* try to free pending deleted items */
git_mutex_unlock(&index->lock);
}
+
+ git_index_free(index);
}