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 | - | } |