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