]>
Commit | Line | Data |
---|---|---|
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 |
13 | static 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 | ||
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 | ||
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 |
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 | ||
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 | 74 | static 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 | 147 | int 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 |
162 | static 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 | ||
222 | int 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 | 237 | int 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 |
259 | static 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 | ||
272 | int 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 | } |