]> git.proxmox.com Git - libgit2.git/blame - src/tree-cache.c
Update upstream source from tag 'upstream/1.3.0+dfsg.1'
[libgit2.git] / src / tree-cache.c
CommitLineData
b4171320 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
b4171320
CMN
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include "tree-cache.h"
eae0bfdc 9
19c88310 10#include "pool.h"
6843cebe 11#include "tree.h"
b4171320 12
1fa17b5c
RB
13static git_tree_cache *find_child(
14 const git_tree_cache *tree, const char *path, const char *end)
69bffab9 15{
1fa17b5c 16 size_t i, dirlen = end ? (size_t)(end - path) : strlen(path);
69bffab9
CMN
17
18 for (i = 0; i < tree->children_count; ++i) {
1fa17b5c 19 git_tree_cache *child = tree->children[i];
69bffab9 20
1fa17b5c
RB
21 if (child->namelen == dirlen && !memcmp(path, child->name, dirlen))
22 return child;
69bffab9
CMN
23 }
24
25 return NULL;
26}
27
28void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
29{
30 const char *ptr = path, *end;
31
32 if (tree == NULL)
33 return;
34
c2f8b215 35 tree->entry_count = -1;
69bffab9
CMN
36
37 while (ptr != NULL) {
38 end = strchr(ptr, '/');
39
40 if (end == NULL) /* End of path */
41 break;
42
1fa17b5c 43 tree = find_child(tree, ptr, end);
69bffab9
CMN
44 if (tree == NULL) /* We don't have that tree */
45 return;
46
c2f8b215 47 tree->entry_count = -1;
69bffab9
CMN
48 ptr = end + 1;
49 }
50}
51
3ba69ba8
CMN
52const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)
53{
54 const char *ptr = path, *end;
55
56 if (tree == NULL) {
57 return NULL;
58 }
59
60 while (1) {
61 end = strchr(ptr, '/');
62
1fa17b5c
RB
63 tree = find_child(tree, ptr, end);
64 if (tree == NULL) /* Can't find it */
3ba69ba8 65 return NULL;
3ba69ba8 66
3f035860 67 if (end == NULL || *end + 1 == '\0')
3ba69ba8
CMN
68 return tree;
69
70 ptr = end + 1;
71 }
72}
73
b4171320 74static int read_tree_internal(git_tree_cache **out,
19c88310 75 const char **buffer_in, const char *buffer_end,
46bb0067 76 git_pool *pool)
b4171320 77{
69bffab9 78 git_tree_cache *tree = NULL;
b4171320
CMN
79 const char *name_start, *buffer;
80 int count;
b4171320
CMN
81
82 buffer = name_start = *buffer_in;
83
3fbcac89
VM
84 if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
85 goto corrupted;
b4171320 86
3fbcac89
VM
87 if (++buffer >= buffer_end)
88 goto corrupted;
b4171320 89
46bb0067 90 if (git_tree_cache_new(&tree, name_start, pool) < 0)
d091a9db 91 return -1;
b183ffe7 92
b4171320 93 /* Blank-terminated ASCII decimal number of entries in this tree */
6c7cee42 94 if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0)
3fbcac89 95 goto corrupted;
b4171320 96
c2f8b215 97 tree->entry_count = count;
b4171320 98
3fbcac89
VM
99 if (*buffer != ' ' || ++buffer >= buffer_end)
100 goto corrupted;
b4171320
CMN
101
102 /* Number of children of the tree, newline-terminated */
6c7cee42 103 if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0 || count < 0)
3fbcac89 104 goto corrupted;
b4171320
CMN
105
106 tree->children_count = count;
107
3fbcac89
VM
108 if (*buffer != '\n' || ++buffer > buffer_end)
109 goto corrupted;
b4171320 110
acd31b4a 111 /* The SHA1 is only there if it's not invalidated */
c2f8b215 112 if (tree->entry_count >= 0) {
acd31b4a 113 /* 160-bit SHA-1 for this tree and it's children */
3fbcac89
VM
114 if (buffer + GIT_OID_RAWSZ > buffer_end)
115 goto corrupted;
b4171320 116
acd31b4a
CMN
117 git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);
118 buffer += GIT_OID_RAWSZ;
119 }
b4171320
CMN
120
121 /* Parse children: */
122 if (tree->children_count > 0) {
0c9c969a 123 size_t i, bufsize;
b4171320 124
0c9c969a
UG
125 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bufsize, tree->children_count, sizeof(git_tree_cache*));
126
127 tree->children = git_pool_malloc(pool, bufsize);
ac3d33df 128 GIT_ERROR_CHECK_ALLOC(tree->children);
b4171320 129
0c9c969a 130 memset(tree->children, 0x0, bufsize);
82e6a42c 131
b4171320 132 for (i = 0; i < tree->children_count; ++i) {
46bb0067 133 if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0)
7b69289f 134 goto corrupted;
b4171320
CMN
135 }
136 }
137
138 *buffer_in = buffer;
139 *out = tree;
3fbcac89 140 return 0;
b4171320 141
3fbcac89 142 corrupted:
ac3d33df 143 git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index");
3fbcac89 144 return -1;
b4171320
CMN
145}
146
19c88310 147int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool)
b4171320
CMN
148{
149 const char *buffer_end = buffer + buffer_size;
b4171320 150
46bb0067 151 if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0)
3fbcac89 152 return -1;
b4171320 153
3fbcac89 154 if (buffer < buffer_end) {
ac3d33df 155 git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index (unexpected trailing data)");
3fbcac89
VM
156 return -1;
157 }
b4171320 158
3fbcac89 159 return 0;
b4171320
CMN
160}
161
6843cebe
CMN
162static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool)
163{
164 git_repository *repo;
0c9c969a 165 size_t i, j, nentries, ntrees, alloc_size;
6843cebe
CMN
166 int error;
167
168 repo = git_tree_owner(tree);
169
170 git_oid_cpy(&cache->oid, git_tree_id(tree));
171 nentries = git_tree_entrycount(tree);
172
173 /*
174 * We make sure we know how many trees we need to allocate for
175 * so we don't have to realloc and change the pointers for the
176 * parents.
177 */
178 ntrees = 0;
179 for (i = 0; i < nentries; i++) {
180 const git_tree_entry *entry;
181
182 entry = git_tree_entry_byindex(tree, i);
183 if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE)
184 ntrees++;
185 }
186
0c9c969a
UG
187 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_size, ntrees, sizeof(git_tree_cache *));
188
6843cebe 189 cache->children_count = ntrees;
0c9c969a 190 cache->children = git_pool_mallocz(pool, alloc_size);
ac3d33df 191 GIT_ERROR_CHECK_ALLOC(cache->children);
6843cebe
CMN
192
193 j = 0;
194 for (i = 0; i < nentries; i++) {
195 const git_tree_entry *entry;
196 git_tree *subtree;
197
198 entry = git_tree_entry_byindex(tree, i);
bb0757d5
CMN
199 if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) {
200 cache->entry_count++;
6843cebe 201 continue;
bb0757d5 202 }
6843cebe 203
46bb0067 204 if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0)
6843cebe
CMN
205 return error;
206
207 if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0)
208 return error;
209
210 error = read_tree_recursive(cache->children[j], subtree, pool);
211 git_tree_free(subtree);
bb0757d5 212 cache->entry_count += cache->children[j]->entry_count;
6843cebe
CMN
213 j++;
214
215 if (error < 0)
216 return error;
217 }
218
219 return 0;
220}
221
222int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool)
223{
224 int error;
225 git_tree_cache *cache;
226
46bb0067 227 if ((error = git_tree_cache_new(&cache, "", pool)) < 0)
6843cebe
CMN
228 return error;
229
230 if ((error = read_tree_recursive(cache, tree, pool)) < 0)
231 return error;
232
233 *out = cache;
234 return 0;
235}
236
46bb0067 237int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool)
d091a9db 238{
0c9c969a 239 size_t name_len, alloc_size;
d091a9db
CMN
240 git_tree_cache *tree;
241
242 name_len = strlen(name);
0c9c969a
UG
243
244 GIT_ERROR_CHECK_ALLOC_ADD3(&alloc_size, sizeof(git_tree_cache), name_len, 1);
245
246 tree = git_pool_malloc(pool, alloc_size);
ac3d33df 247 GIT_ERROR_CHECK_ALLOC(tree);
d091a9db
CMN
248
249 memset(tree, 0x0, sizeof(git_tree_cache));
d091a9db
CMN
250 /* NUL-terminated tree name */
251 tree->namelen = name_len;
252 memcpy(tree->name, name, name_len);
253 tree->name[name_len] = '\0';
254
255 *out = tree;
256 return 0;
257}
c2f8b215 258
c2f8b215
CMN
259static void write_tree(git_buf *out, git_tree_cache *tree)
260{
261 size_t i;
262
cf1013a8 263 git_buf_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count);
c2f8b215 264
795d8e93
CMN
265 if (tree->entry_count != -1)
266 git_buf_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ);
c2f8b215
CMN
267
268 for (i = 0; i < tree->children_count; i++)
269 write_tree(out, tree->children[i]);
270}
271
272int git_tree_cache_write(git_buf *out, git_tree_cache *tree)
273{
c2f8b215
CMN
274 write_tree(out, tree);
275
276 return git_buf_oom(out) ? -1 : 0;
277}