source src/tree-cache.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
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 | 523 | 2 | static git_tree_cache *find_child( | |
14 | - | const git_tree_cache *tree, const char *path, const char *end) | ||
15 | - | { | ||
16 | 523 | 2-4 | size_t i, dirlen = end ? (size_t)(end - path) : strlen(path); | |
17 | - | |||
18 | 1327 | 5,9,10 | for (i = 0; i < tree->children_count; ++i) { | |
19 | 1174 | 6 | git_tree_cache *child = tree->children[i]; | |
20 | - | |||
21 | 1174 | 6,7 | if (child->namelen == dirlen && !memcmp(path, child->name, dirlen)) | |
22 | 370 | 8 | return child; | |
23 | - | } | ||
24 | - | |||
25 | 153 | 11 | return NULL; | |
26 | - | } | ||
27 | - | |||
28 | 52410 | 2 | void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path) | |
29 | - | { | ||
30 | 52410 | 2 | const char *ptr = path, *end; | |
31 | - | |||
32 | 52410 | 2 | if (tree == NULL) | |
33 | 49233 | 3 | return; | |
34 | - | |||
35 | 3177 | 4 | tree->entry_count = -1; | |
36 | - | |||
37 | 3529 | 4,11 | while (ptr != NULL) { | |
38 | 3529 | 5 | end = strchr(ptr, '/'); | |
39 | - | |||
40 | 3529 | 5 | if (end == NULL) /* End of path */ | |
41 | 3150 | 6 | break; | |
42 | - | |||
43 | 379 | 7 | tree = find_child(tree, ptr, end); | |
44 | 379 | 8 | if (tree == NULL) /* We don't have that tree */ | |
45 | 27 | 9 | return; | |
46 | - | |||
47 | 352 | 10 | tree->entry_count = -1; | |
48 | 352 | 10 | ptr = end + 1; | |
49 | - | } | ||
50 | - | } | ||
51 | - | |||
52 | 471 | 2 | const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path) | |
53 | - | { | ||
54 | 471 | 2 | const char *ptr = path, *end; | |
55 | - | |||
56 | 471 | 2 | if (tree == NULL) { | |
57 | 330 | 3 | return NULL; | |
58 | - | } | ||
59 | - | |||
60 | - | while (1) { | ||
61 | 144 | 4 | end = strchr(ptr, '/'); | |
62 | - | |||
63 | 144 | 4 | tree = find_child(tree, ptr, end); | |
64 | 144 | 5 | if (tree == NULL) /* Can't find it */ | |
65 | 126 | 6 | return NULL; | |
66 | - | |||
67 | 18 | 7,8 | if (end == NULL || *end + 1 == '\0') | |
68 | 15 | 9 | return tree; | |
69 | - | |||
70 | 3 | 10 | ptr = end + 1; | |
71 | 3 | 10 | } | |
72 | - | } | ||
73 | - | |||
74 | 434 | 2 | static int read_tree_internal(git_tree_cache **out, | |
75 | - | const char **buffer_in, const char *buffer_end, | ||
76 | - | git_pool *pool) | ||
77 | - | { | ||
78 | 434 | 2 | git_tree_cache *tree = NULL; | |
79 | - | const char *name_start, *buffer; | ||
80 | - | int count; | ||
81 | - | |||
82 | 434 | 2 | buffer = name_start = *buffer_in; | |
83 | - | |||
84 | 434 | 2 | if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) | |
85 | ##### | 3 | goto corrupted; | |
86 | - | |||
87 | 434 | 4 | if (++buffer >= buffer_end) | |
88 | ##### | 5 | goto corrupted; | |
89 | - | |||
90 | 434 | 6,7 | if (git_tree_cache_new(&tree, name_start, pool) < 0) | |
91 | ##### | 8 | return -1; | |
92 | - | |||
93 | - | /* Blank-terminated ASCII decimal number of entries in this tree */ | ||
94 | 434 | 9,10 | if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0) | |
95 | ##### | 11 | goto corrupted; | |
96 | - | |||
97 | 434 | 12 | tree->entry_count = count; | |
98 | - | |||
99 | 434 | 12,13 | if (*buffer != ' ' || ++buffer >= buffer_end) | |
100 | - | goto corrupted; | ||
101 | - | |||
102 | - | /* Number of children of the tree, newline-terminated */ | ||
103 | 434 | 14-16 | if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0 || count < 0) | |
104 | - | goto corrupted; | ||
105 | - | |||
106 | 434 | 17 | tree->children_count = count; | |
107 | - | |||
108 | 434 | 17,18 | if (*buffer != '\n' || ++buffer > buffer_end) | |
109 | - | goto corrupted; | ||
110 | - | |||
111 | - | /* The SHA1 is only there if it's not invalidated */ | ||
112 | 433 | 19 | if (tree->entry_count >= 0) { | |
113 | - | /* 160-bit SHA-1 for this tree and it's children */ | ||
114 | 425 | 20 | if (buffer + GIT_OID_RAWSZ > buffer_end) | |
115 | ##### | 21 | goto corrupted; | |
116 | - | |||
117 | 425 | 22 | git_oid_fromraw(&tree->oid, (const unsigned char *)buffer); | |
118 | 425 | 23 | buffer += GIT_OID_RAWSZ; | |
119 | - | } | ||
120 | - | |||
121 | - | /* Parse children: */ | ||
122 | 433 | 24 | if (tree->children_count > 0) { | |
123 | - | size_t i, bufsize; | ||
124 | - | |||
125 | 101 | 25-31,42 | GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bufsize, tree->children_count, sizeof(git_tree_cache*)); | |
126 | - | |||
127 | 101 | 32 | tree->children = git_pool_malloc(pool, bufsize); | |
128 | 101 | 33,34 | GIT_ERROR_CHECK_ALLOC(tree->children); | |
129 | - | |||
130 | 101 | 35 | memset(tree->children, 0x0, bufsize); | |
131 | - | |||
132 | 304 | 35,39-41 | for (i = 0; i < tree->children_count; ++i) { | |
133 | 203 | 36,37 | if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0) | |
134 | ##### | 38 | goto corrupted; | |
135 | - | } | ||
136 | - | } | ||
137 | - | |||
138 | 433 | 43 | *buffer_in = buffer; | |
139 | 433 | 43 | *out = tree; | |
140 | 433 | 43 | return 0; | |
141 | - | |||
142 | - | corrupted: | ||
143 | 1 | 44 | git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index"); | |
144 | 1 | 45 | return -1; | |
145 | - | } | ||
146 | - | |||
147 | 231 | 2 | int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool) | |
148 | - | { | ||
149 | 231 | 2 | const char *buffer_end = buffer + buffer_size; | |
150 | - | |||
151 | 231 | 2,3 | if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0) | |
152 | 1 | 4 | return -1; | |
153 | - | |||
154 | 230 | 5 | if (buffer < buffer_end) { | |
155 | ##### | 6 | git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index (unexpected trailing data)"); | |
156 | ##### | 7 | return -1; | |
157 | - | } | ||
158 | - | |||
159 | 230 | 8 | return 0; | |
160 | - | } | ||
161 | - | |||
162 | 762 | 2 | static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool) | |
163 | - | { | ||
164 | - | git_repository *repo; | ||
165 | - | size_t i, j, nentries, ntrees, alloc_size; | ||
166 | - | int error; | ||
167 | - | |||
168 | 762 | 2 | repo = git_tree_owner(tree); | |
169 | - | |||
170 | 762 | 3,4 | git_oid_cpy(&cache->oid, git_tree_id(tree)); | |
171 | 762 | 5 | 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 | 762 | 6 | ntrees = 0; | |
179 | 3864 | 6,11,12 | for (i = 0; i < nentries; i++) { | |
180 | - | const git_tree_entry *entry; | ||
181 | - | |||
182 | 3102 | 7 | entry = git_tree_entry_byindex(tree, i); | |
183 | 3102 | 8,9 | if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE) | |
184 | 189 | 10 | ntrees++; | |
185 | - | } | ||
186 | - | |||
187 | 762 | 13-19 | GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_size, ntrees, sizeof(git_tree_cache *)); | |
188 | - | |||
189 | 762 | 20 | cache->children_count = ntrees; | |
190 | 762 | 20 | cache->children = git_pool_mallocz(pool, alloc_size); | |
191 | 762 | 21,22 | GIT_ERROR_CHECK_ALLOC(cache->children); | |
192 | - | |||
193 | 762 | 23 | j = 0; | |
194 | 3864 | 23,42,43 | for (i = 0; i < nentries; i++) { | |
195 | - | const git_tree_entry *entry; | ||
196 | - | git_tree *subtree; | ||
197 | - | |||
198 | 3102 | 24 | entry = git_tree_entry_byindex(tree, i); | |
199 | 3102 | 25,26 | if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) { | |
200 | 2913 | 27 | cache->entry_count++; | |
201 | 2913 | 27 | continue; | |
202 | - | } | ||
203 | - | |||
204 | 189 | 28-30 | if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0) | |
205 | ##### | 31,41 | return error; | |
206 | - | |||
207 | 189 | 32-34 | if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0) | |
208 | ##### | 35 | return error; | |
209 | - | |||
210 | 189 | 36 | error = read_tree_recursive(cache->children[j], subtree, pool); | |
211 | 189 | 37 | git_tree_free(subtree); | |
212 | 189 | 38 | cache->entry_count += cache->children[j]->entry_count; | |
213 | 189 | 38 | j++; | |
214 | - | |||
215 | 189 | 38 | if (error < 0) | |
216 | 189 | 39,40 | return error; | |
217 | - | } | ||
218 | - | |||
219 | 762 | 44 | return 0; | |
220 | - | } | ||
221 | - | |||
222 | 573 | 2 | 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 | - | |||
227 | 573 | 2,3 | if ((error = git_tree_cache_new(&cache, "", pool)) < 0) | |
228 | ##### | 4 | return error; | |
229 | - | |||
230 | 573 | 5,6 | if ((error = read_tree_recursive(cache, tree, pool)) < 0) | |
231 | ##### | 7 | return error; | |
232 | - | |||
233 | 573 | 8 | *out = cache; | |
234 | 573 | 8 | return 0; | |
235 | - | } | ||
236 | - | |||
237 | 1196 | 2 | int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool) | |
238 | - | { | ||
239 | - | size_t name_len, alloc_size; | ||
240 | - | git_tree_cache *tree; | ||
241 | - | |||
242 | 1196 | 2 | name_len = strlen(name); | |
243 | - | |||
244 | 1196 | 2-8 | GIT_ERROR_CHECK_ALLOC_ADD3(&alloc_size, sizeof(git_tree_cache), name_len, 1); | |
245 | - | |||
246 | 1196 | 9 | tree = git_pool_malloc(pool, alloc_size); | |
247 | 1196 | 10,11 | GIT_ERROR_CHECK_ALLOC(tree); | |
248 | - | |||
249 | 1196 | 12 | memset(tree, 0x0, sizeof(git_tree_cache)); | |
250 | - | /* NUL-terminated tree name */ | ||
251 | 1196 | 12 | tree->namelen = name_len; | |
252 | 1196 | 12 | memcpy(tree->name, name, name_len); | |
253 | 1196 | 12 | tree->name[name_len] = '\0'; | |
254 | - | |||
255 | 1196 | 12 | *out = tree; | |
256 | 1196 | 12 | return 0; | |
257 | - | } | ||
258 | - | |||
259 | 910 | 2 | static void write_tree(git_buf *out, git_tree_cache *tree) | |
260 | - | { | ||
261 | - | size_t i; | ||
262 | - | |||
263 | 910 | 2 | git_buf_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count); | |
264 | - | |||
265 | 910 | 3 | if (tree->entry_count != -1) | |
266 | 413 | 4 | git_buf_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ); | |
267 | - | |||
268 | 1028 | 5,7,8 | for (i = 0; i < tree->children_count; i++) | |
269 | 118 | 6 | write_tree(out, tree->children[i]); | |
270 | 910 | 9 | } | |
271 | - | |||
272 | 792 | 2 | int git_tree_cache_write(git_buf *out, git_tree_cache *tree) | |
273 | - | { | ||
274 | 792 | 2 | write_tree(out, tree); | |
275 | - | |||
276 | 792 | 3 | return git_buf_oom(out) ? -1 : 0; | |
277 | - | } |