mirror of
https://git.proxmox.com/git/libgit2
synced 2025-12-26 04:49:11 +00:00
While C Git has been writing entry count -1 (ie. never other negative numbers) as invalid since day 1, it accepts all negative entry counts as invalid. JGit follows the same rule. libgit2 should also follow, or the index that works with C Git or JGit may someday be rejected by libgit2. Other reimplementations like dulwich and grit have not bothered with parsing or writing tree cache.
185 lines
3.9 KiB
C
185 lines
3.9 KiB
C
/*
|
|
* Copyright (C) 2009-2012 the libgit2 contributors
|
|
*
|
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
* a Linking Exception. For full terms see the included COPYING file.
|
|
*/
|
|
|
|
#include "tree-cache.h"
|
|
|
|
static git_tree_cache *find_child(const git_tree_cache *tree, const char *path)
|
|
{
|
|
size_t i, dirlen;
|
|
const char *end;
|
|
|
|
end = strchr(path, '/');
|
|
if (end == NULL) {
|
|
end = strrchr(path, '\0');
|
|
}
|
|
|
|
dirlen = end - path;
|
|
|
|
for (i = 0; i < tree->children_count; ++i) {
|
|
const char *childname = tree->children[i]->name;
|
|
|
|
if (strlen(childname) == dirlen && !memcmp(path, childname, dirlen))
|
|
return tree->children[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
|
|
{
|
|
const char *ptr = path, *end;
|
|
|
|
if (tree == NULL)
|
|
return;
|
|
|
|
tree->entries = -1;
|
|
|
|
while (ptr != NULL) {
|
|
end = strchr(ptr, '/');
|
|
|
|
if (end == NULL) /* End of path */
|
|
break;
|
|
|
|
tree = find_child(tree, ptr);
|
|
if (tree == NULL) /* We don't have that tree */
|
|
return;
|
|
|
|
tree->entries = -1;
|
|
ptr = end + 1;
|
|
}
|
|
}
|
|
|
|
const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)
|
|
{
|
|
const char *ptr = path, *end;
|
|
|
|
if (tree == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
while (1) {
|
|
end = strchr(ptr, '/');
|
|
|
|
tree = find_child(tree, ptr);
|
|
if (tree == NULL) { /* Can't find it */
|
|
return NULL;
|
|
}
|
|
|
|
if (end == NULL || *end + 1 == '\0')
|
|
return tree;
|
|
|
|
ptr = end + 1;
|
|
}
|
|
}
|
|
|
|
static int read_tree_internal(git_tree_cache **out,
|
|
const char **buffer_in, const char *buffer_end, git_tree_cache *parent)
|
|
{
|
|
git_tree_cache *tree = NULL;
|
|
const char *name_start, *buffer;
|
|
int count;
|
|
size_t name_len;
|
|
|
|
buffer = name_start = *buffer_in;
|
|
|
|
if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
|
|
goto corrupted;
|
|
|
|
if (++buffer >= buffer_end)
|
|
goto corrupted;
|
|
|
|
name_len = strlen(name_start);
|
|
tree = git__malloc(sizeof(git_tree_cache) + name_len + 1);
|
|
GITERR_CHECK_ALLOC(tree);
|
|
|
|
memset(tree, 0x0, sizeof(git_tree_cache));
|
|
tree->parent = parent;
|
|
|
|
/* NUL-terminated tree name */
|
|
memcpy(tree->name, name_start, name_len);
|
|
tree->name[name_len] = '\0';
|
|
|
|
/* Blank-terminated ASCII decimal number of entries in this tree */
|
|
if (git__strtol32(&count, buffer, &buffer, 10) < 0)
|
|
goto corrupted;
|
|
|
|
tree->entries = count;
|
|
|
|
if (*buffer != ' ' || ++buffer >= buffer_end)
|
|
goto corrupted;
|
|
|
|
/* Number of children of the tree, newline-terminated */
|
|
if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)
|
|
goto corrupted;
|
|
|
|
tree->children_count = count;
|
|
|
|
if (*buffer != '\n' || ++buffer > buffer_end)
|
|
goto corrupted;
|
|
|
|
/* The SHA1 is only there if it's not invalidated */
|
|
if (tree->entries >= 0) {
|
|
/* 160-bit SHA-1 for this tree and it's children */
|
|
if (buffer + GIT_OID_RAWSZ > buffer_end)
|
|
goto corrupted;
|
|
|
|
git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);
|
|
buffer += GIT_OID_RAWSZ;
|
|
}
|
|
|
|
/* Parse children: */
|
|
if (tree->children_count > 0) {
|
|
unsigned int i;
|
|
|
|
tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *));
|
|
GITERR_CHECK_ALLOC(tree->children);
|
|
|
|
for (i = 0; i < tree->children_count; ++i) {
|
|
if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
*buffer_in = buffer;
|
|
*out = tree;
|
|
return 0;
|
|
|
|
corrupted:
|
|
git_tree_cache_free(tree);
|
|
giterr_set(GITERR_INDEX, "Corruped TREE extension in index");
|
|
return -1;
|
|
}
|
|
|
|
int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size)
|
|
{
|
|
const char *buffer_end = buffer + buffer_size;
|
|
|
|
if (read_tree_internal(tree, &buffer, buffer_end, NULL) < 0)
|
|
return -1;
|
|
|
|
if (buffer < buffer_end) {
|
|
giterr_set(GITERR_INDEX, "Corruped TREE extension in index (unexpected trailing data)");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void git_tree_cache_free(git_tree_cache *tree)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (tree == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < tree->children_count; ++i)
|
|
git_tree_cache_free(tree->children[i]);
|
|
|
|
git__free(tree->children);
|
|
git__free(tree);
|
|
}
|