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 "attrcache.h"
9 -
10 - #include "repository.h"
11 - #include "attr_file.h"
12 - #include "config.h"
13 - #include "sysdir.h"
14 - #include "ignore.h"
15 -
16 516182 2 GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
17 - {
18 - GIT_UNUSED(cache); /* avoid warning if threading is off */
19 -
20 518415 2,3 if (git_mutex_lock(&cache->lock) < 0) {
21 ##### 4 git_error_set(GIT_ERROR_OS, "unable to get attr cache lock");
22 ##### 5 return -1;
23 - }
24 518415 6 return 0;
25 - }
26 -
27 517707 2 GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache)
28 - {
29 - GIT_UNUSED(cache); /* avoid warning if threading is off */
30 517707 2 git_mutex_unlock(&cache->lock);
31 518507 3 }
32 -
33 515232 2 GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
34 - git_attr_cache *cache, const char *path)
35 - {
36 515232 2 return git_strmap_get(cache->files, path);
37 - }
38 -
39 6424 2 int git_attr_cache__alloc_file_entry(
40 - git_attr_file_entry **out,
41 - const char *base,
42 - const char *path,
43 - git_pool *pool)
44 - {
45 6424 2 size_t baselen = 0, pathlen = strlen(path);
46 6424 2 size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
47 - git_attr_file_entry *ce;
48 -
49 6424 2-4 if (base != NULL && git_path_root(path) < 0) {
50 6032 5 baselen = strlen(base);
51 6032 5 cachesize += baselen;
52 -
53 6032 5,6 if (baselen && base[baselen - 1] != '/')
54 ##### 7 cachesize++;
55 - }
56 -
57 6424 8 ce = git_pool_mallocz(pool, cachesize);
58 6425 9,10 GIT_ERROR_CHECK_ALLOC(ce);
59 -
60 6425 11 if (baselen) {
61 6033 12 memcpy(ce->fullpath, base, baselen);
62 -
63 6033 12 if (base[baselen - 1] != '/')
64 ##### 13 ce->fullpath[baselen++] = '/';
65 - }
66 6425 14 memcpy(&ce->fullpath[baselen], path, pathlen);
67 -
68 6425 14 ce->path = &ce->fullpath[baselen];
69 6425 14 *out = ce;
70 -
71 6425 14 return 0;
72 - }
73 -
74 - /* call with attrcache locked */
75 6415 2 static int attr_cache_make_entry(
76 - git_attr_file_entry **out, git_repository *repo, const char *path)
77 - {
78 6415 2 git_attr_cache *cache = git_repository_attr_cache(repo);
79 6415 3 git_attr_file_entry *entry = NULL;
80 - int error;
81 -
82 6416 3-5 if ((error = git_attr_cache__alloc_file_entry(&entry, git_repository_workdir(repo),
83 - path, &cache->pool)) < 0)
84 ##### 6 return error;
85 -
86 6416 7,8 if ((error = git_strmap_set(cache->files, entry->path, entry)) < 0)
87 ##### 9 return error;
88 -
89 6416 10 *out = entry;
90 6416 10 return error;
91 - }
92 -
93 - /* insert entry or replace existing if we raced with another thread */
94 174951 2 static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
95 - {
96 - git_attr_file_entry *entry;
97 - git_attr_file *old;
98 -
99 175482 2,3 if (attr_cache_lock(cache) < 0)
100 ##### 4 return -1;
101 -
102 175482 5 entry = attr_cache_lookup_entry(cache, file->entry->path);
103 -
104 175552 6 GIT_REFCOUNT_OWN(file, entry);
105 175552 6 GIT_REFCOUNT_INC(file);
106 -
107 - /*
108 - * Replace the existing value if another thread has
109 - * created it in the meantime.
110 - */
111 175605 7 old = git__swap(entry->file[file->source], file);
112 -
113 175626 8 if (old) {
114 169251 9 GIT_REFCOUNT_OWN(old, NULL);
115 169251 9 git_attr_file__free(old);
116 - }
117 -
118 175607 10 attr_cache_unlock(cache);
119 175675 11 return 0;
120 - }
121 -
122 10 2 static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
123 - {
124 10 2 int error = 0;
125 - git_attr_file_entry *entry;
126 10 2 git_attr_file *old = NULL;
127 -
128 10 2 if (!file)
129 ##### 3 return 0;
130 -
131 10 4,5 if ((error = attr_cache_lock(cache)) < 0)
132 ##### 6 return error;
133 -
134 10 7,8 if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
135 10 9 old = git__compare_and_swap(&entry->file[file->source], file, NULL);
136 -
137 10 10 attr_cache_unlock(cache);
138 -
139 10 11 if (old) {
140 10 12 GIT_REFCOUNT_OWN(old, NULL);
141 10 12 git_attr_file__free(old);
142 - }
143 -
144 10 13 return error;
145 - }
146 -
147 - /* Look up cache entry and file.
148 - * - If entry is not present, create it while the cache is locked.
149 - * - If file is present, increment refcount before returning it, so the
150 - * cache can be unlocked and it won't go away.
151 - */
152 - 2 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files)static int attr_cache_lookup(
153 - git_attr_file **out_file,
154 - git_attr_file_entry **out_entry,
155 - git_repository *repo,
156 - git_attr_session *attr_session,
157 - git_attr_file_source source,
158 - const char *base,
159 - const char *filename)
160 - {
161 - 2 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) int error = 0;
162 - 2 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) git_buf path = GIT_BUF_INIT;
163 - 2 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) const char *wd = git_repository_workdir(repo), *relfile;
164 - 3 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) git_attr_cache *cache = git_repository_attr_cache(repo);
165 - 4 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) git_attr_file_entry *entry = NULL;
166 - 4 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) git_attr_file *file = NULL;
167 -
168 - /* join base and path as needed */
169 - 4-6 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) if (base != NULL && git_path_root(filename) < 0) {
170 - 7-9 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) git_buf *p = attr_session ? &attr_session->tmp : &path;
171 -
172 - 10,11 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) if (git_buf_joinpath(p, base, filename) < 0)
173 - 12 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) return -1;
174 -
175 - 13 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) filename = p->ptr;
176 - }
177 -
178 - 14 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) relfile = filename;
179 - 14-16 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) if (wd && !git__prefixcmp(relfile, wd))
180 - 17 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) relfile += strlen(wd);
181 -
182 - /* check cache for existing entry */
183 - 18,19 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) if ((error = attr_cache_lock(cache)) < 0)
184 - 20 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) goto cleanup;
185 -
186 - 21 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) entry = attr_cache_lookup_entry(cache, relfile);
187 - 22 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) if (!entry)
188 - 23 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) error = attr_cache_make_entry(&entry, repo, relfile);
189 - 24 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) else if (entry->file[source] != NULL) {
190 - 25 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) file = entry->file[source];
191 - 25 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) GIT_REFCOUNT_INC(file);
192 - }
193 -
194 - 26 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) attr_cache_unlock(cache);
195 -
196 - cleanup:
197 - 27 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) *out_file = file;
198 - 27 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) *out_entry = entry;
199 -
200 - 27 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) git_buf_dispose(&path);
201 - 28 suppressed: function cannot be solved attr_cache_lookup (automatic due to inconsistent arc counts in .gcda files) return error;
202 - }
203 -
204 - 2 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files)int git_attr_cache__get(
205 - git_attr_file **out,
206 - git_repository *repo,
207 - git_attr_session *attr_session,
208 - git_attr_file_source source,
209 - const char *base,
210 - const char *filename,
211 - git_attr_file_parser parser,
212 - bool allow_macros)
213 - {
214 - 2 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) int error = 0;
215 - 2 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_attr_cache *cache = git_repository_attr_cache(repo);
216 - 3 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_attr_file_entry *entry = NULL;
217 - 3 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_attr_file *file = NULL, *updated = NULL;
218 -
219 - 3,4 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if ((error = attr_cache_lookup(
220 - &file, &entry, repo, attr_session, source, base, filename)) < 0)
221 - 5 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) return error;
222 -
223 - /* load file if we don't have one or if existing one is out of date */
224 - 6-8 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
225 - 9 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser, allow_macros);
226 -
227 - /* if we loaded the file, insert into and/or update cache */
228 - 10 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if (updated) {
229 - 11,12 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if ((error = attr_cache_upsert(cache, updated)) < 0)
230 - 13 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_attr_file__free(updated);
231 - else {
232 - 14 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_attr_file__free(file); /* offset incref from lookup */
233 - 15 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) file = updated;
234 - }
235 - }
236 -
237 - /* if file could not be loaded */
238 - 16 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if (error < 0) {
239 - /* remove existing entry */
240 - 17 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if (file) {
241 - 18 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) attr_cache_remove(cache, file);
242 - 19 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_attr_file__free(file); /* offset incref from lookup */
243 - 20 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) file = NULL;
244 - }
245 - /* no error if file simply doesn't exist */
246 - 21 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) if (error == GIT_ENOTFOUND) {
247 - 22 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) git_error_clear();
248 - 23 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) error = 0;
249 - }
250 - }
251 -
252 - 24 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) *out = file;
253 - 24 suppressed: function cannot be solved git_attr_cache__get (automatic due to inconsistent arc counts in .gcda files) return error;
254 - }
255 -
256 8 2 bool git_attr_cache__is_cached(
257 - git_repository *repo,
258 - git_attr_file_source source,
259 - const char *filename)
260 - {
261 8 2 git_attr_cache *cache = git_repository_attr_cache(repo);
262 - git_attr_file_entry *entry;
263 - git_strmap *files;
264 -
265 8 3,4 if (!cache || !(files = cache->files))
266 ##### 5 return false;
267 -
268 8 6,7 if ((entry = git_strmap_get(files, filename)) == NULL)
269 ##### 8 return false;
270 -
271 8 9 return entry && (entry->file[source] != NULL);
272 - }
273 -
274 -
275 2558 2 static int attr_cache__lookup_path(
276 - char **out, git_config *cfg, const char *key, const char *fallback)
277 - {
278 2558 2 git_buf buf = GIT_BUF_INIT;
279 - int error;
280 2558 2 git_config_entry *entry = NULL;
281 -
282 2558 2 *out = NULL;
283 -
284 2558 2,3 if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
285 ##### 4 return error;
286 -
287 2558 5 if (entry) {
288 3 6 const char *cfgval = entry->value;
289 -
290 - /* expand leading ~/ as needed */
291 3 6-8 if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
292 3 9,10,13 if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2])))
293 3 11,12 *out = git_buf_detach(&buf);
294 ##### 14 } else if (cfgval) {
295 3 15-17 *out = git__strdup(cfgval);
296 - }
297 - }
298 2555 18,19 else if (!git_sysdir_find_xdg_file(&buf, fallback)) {
299 ##### 20,21 *out = git_buf_detach(&buf);
300 - }
301 -
302 2558 22 git_config_entry_free(entry);
303 2558 23 git_buf_dispose(&buf);
304 -
305 2558 24 return error;
306 - }
307 -
308 1279 2 static void attr_cache__free(git_attr_cache *cache)
309 - {
310 - bool unlock;
311 -
312 1279 2 if (!cache)
313 1279 3,31 return;
314 -
315 1279 4 unlock = (attr_cache_lock(cache) == 0);
316 -
317 1279 5 if (cache->files != NULL) {
318 - git_attr_file_entry *entry;
319 - git_attr_file *file;
320 - int i;
321 -
322 32074 6-14 git_strmap_foreach_value(cache->files, entry, {
323 - for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
324 - if ((file = git__swap(entry->file[i], NULL)) != NULL) {
325 - GIT_REFCOUNT_OWN(file, NULL);
326 - git_attr_file__free(file);
327 - }
328 - }
329 - });
330 1279 15,16 git_strmap_free(cache->files);
331 - }
332 -
333 1279 17 if (cache->macros != NULL) {
334 - git_attr_rule *rule;
335 -
336 2764 18-21 git_strmap_foreach_value(cache->macros, rule, {
337 - git_attr_rule__free(rule);
338 - });
339 1279 22,23 git_strmap_free(cache->macros);
340 - }
341 -
342 1279 24 git_pool_clear(&cache->pool);
343 -
344 1279 25 git__free(cache->cfg_attr_file);
345 1279 26 cache->cfg_attr_file = NULL;
346 -
347 1279 26 git__free(cache->cfg_excl_file);
348 1279 27 cache->cfg_excl_file = NULL;
349 -
350 1279 27 if (unlock)
351 1279 28 attr_cache_unlock(cache);
352 1279 29 git_mutex_free(&cache->lock);
353 -
354 1279 30 git__free(cache);
355 - }
356 -
357 55532 2 int git_attr_cache__init(git_repository *repo)
358 - {
359 55532 2 int ret = 0;
360 55532 2 git_attr_cache *cache = git_repository_attr_cache(repo);
361 55529 3 git_config *cfg = NULL;
362 -
363 55529 3 if (cache)
364 54250 4 return 0;
365 -
366 1279 5 cache = git__calloc(1, sizeof(git_attr_cache));
367 1279 6,7 GIT_ERROR_CHECK_ALLOC(cache);
368 -
369 - /* set up lock */
370 1279 8,9 if (git_mutex_init(&cache->lock) < 0) {
371 ##### 10 git_error_set(GIT_ERROR_OS, "unable to initialize lock for attr cache");
372 ##### 11 git__free(cache);
373 ##### 12 return -1;
374 - }
375 -
376 1279 13,14 if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
377 ##### 15 goto cancel;
378 -
379 - /* cache config settings for attributes and ignores */
380 1279 16 ret = attr_cache__lookup_path(
381 - &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
382 1279 17 if (ret < 0)
383 ##### 18 goto cancel;
384 -
385 1279 19 ret = attr_cache__lookup_path(
386 - &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
387 1279 20 if (ret < 0)
388 ##### 21 goto cancel;
389 -
390 - /* allocate hashtable for attribute and ignore file contents,
391 - * hashtable for attribute macros, and string pool
392 - */
393 1279 22-25 if ((ret = git_strmap_new(&cache->files)) < 0 ||
394 1279 24,26,27 (ret = git_strmap_new(&cache->macros)) < 0 ||
395 1279 26 (ret = git_pool_init(&cache->pool, 1)) < 0)
396 - goto cancel;
397 -
398 1279 28 cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
399 1279 29 if (cache)
400 ##### 30 goto cancel; /* raced with another thread, free this but no error */
401 -
402 1279 31 git_config_free(cfg);
403 -
404 - /* insert default macros */
405 1279 32 return git_attr_add_macro(repo, "binary", "-diff -merge -text -crlf");
406 -
407 - cancel:
408 ##### 33 attr_cache__free(cache);
409 ##### 34 git_config_free(cfg);
410 ##### 35 return ret;
411 - }
412 -
413 4094 2 int git_attr_cache_flush(git_repository *repo)
414 - {
415 - git_attr_cache *cache;
416 -
417 - /* this could be done less expensively, but for now, we'll just free
418 - * the entire attrcache and let the next use reinitialize it...
419 - */
420 4094 2-4 if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL)
421 1279 5 attr_cache__free(cache);
422 -
423 4094 6 return 0;
424 - }
425 -
426 1487 2 int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
427 - {
428 1487 2 git_attr_cache *cache = git_repository_attr_cache(repo);
429 - git_attr_rule *preexisting;
430 1487 3 bool locked = false;
431 1487 3 int error = 0;
432 -
433 - /*
434 - * Callers assume that if we return success, that the
435 - * macro will have been adopted by the attributes cache.
436 - * Thus, we have to free the macro here if it's not being
437 - * added to the cache.
438 - *
439 - * TODO: generate warning log if (macro->assigns.length == 0)
440 - */
441 1487 3 if (macro->assigns.length == 0) {
442 ##### 4 git_attr_rule__free(macro);
443 ##### 5 goto out;
444 - }
445 -
446 1487 6,7 if ((error = attr_cache_lock(cache)) < 0)
447 ##### 8 goto out;
448 1487 9 locked = true;
449 -
450 1487 9,10 if ((preexisting = git_strmap_get(cache->macros, macro->match.pattern)) != NULL)
451 2 11 git_attr_rule__free(preexisting);
452 -
453 1487 12,13 if ((error = git_strmap_set(cache->macros, macro->match.pattern, macro)) < 0)
454 ##### 14 goto out;
455 -
456 - out:
457 1487 15 if (locked)
458 1487 16 attr_cache_unlock(cache);
459 1487 17 return error;
460 - }
461 -
462 1038 2 git_attr_rule *git_attr_cache__lookup_macro(
463 - git_repository *repo, const char *name)
464 - {
465 1038 2 git_strmap *macros = git_repository_attr_cache(repo)->macros;
466 -
467 1038 3 return git_strmap_get(macros, name);
468 - }