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