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 "filter.h"
9 -
10 - #include "common.h"
11 - #include "futils.h"
12 - #include "hash.h"
13 - #include "repository.h"
14 - #include "global.h"
15 - #include "git2/sys/filter.h"
16 - #include "git2/config.h"
17 - #include "blob.h"
18 - #include "attr_file.h"
19 - #include "array.h"
20 -
21 - struct git_filter_source {
22 - git_repository *repo;
23 - const char *path;
24 - git_oid oid; /* zero if unknown (which is likely) */
25 - uint16_t filemode; /* zero if unknown */
26 - git_filter_mode_t mode;
27 - uint32_t flags;
28 - };
29 -
30 - typedef struct {
31 - const char *filter_name;
32 - git_filter *filter;
33 - void *payload;
34 - } git_filter_entry;
35 -
36 - struct git_filter_list {
37 - git_array_t(git_filter_entry) filters;
38 - git_filter_source source;
39 - git_buf *temp_buf;
40 - char path[GIT_FLEX_ARRAY];
41 - };
42 -
43 - typedef struct {
44 - char *filter_name;
45 - git_filter *filter;
46 - int priority;
47 - int initialized;
48 - size_t nattrs, nmatches;
49 - char *attrdata;
50 - const char *attrs[GIT_FLEX_ARRAY];
51 - } git_filter_def;
52 -
53 78 2 static int filter_def_priority_cmp(const void *a, const void *b)
54 - {
55 78 2 int pa = ((const git_filter_def *)a)->priority;
56 78 2 int pb = ((const git_filter_def *)b)->priority;
57 78 2 return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
58 - }
59 -
60 - struct git_filter_registry {
61 - git_rwlock lock;
62 - git_vector filters;
63 - };
64 -
65 - static struct git_filter_registry filter_registry;
66 -
67 - static void git_filter_global_shutdown(void);
68 -
69 -
70 29 2 static int filter_def_scan_attrs(
71 - git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
72 - {
73 29 2 const char *start, *scan = attr_str;
74 - int has_eq;
75 -
76 29 2 *nattr = *nmatch = 0;
77 -
78 29 2 if (!scan)
79 1 3 return 0;
80 -
81 74 4,26 while (*scan) {
82 64 5-8 while (git__isspace(*scan)) scan++;
83 -
84 287 9,12-15 for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) {
85 241 10 if (*scan == '=')
86 3 11 has_eq = 1;
87 - }
88 -
89 46 16 if (scan > start) {
90 46 17 (*nattr)++;
91 46 17-20 if (has_eq || *start == '-' || *start == '+' || *start == '!')
92 19 21 (*nmatch)++;
93 -
94 46 22 if (has_eq)
95 3 23 git_buf_putc(attrs, '=');
96 46 24 git_buf_put(attrs, start, scan - start);
97 46 25 git_buf_putc(attrs, '\0');
98 - }
99 - }
100 -
101 28 27 return 0;
102 - }
103 -
104 29 2 static void filter_def_set_attrs(git_filter_def *fdef)
105 - {
106 29 2 char *scan = fdef->attrdata;
107 - size_t i;
108 -
109 75 2,12,13 for (i = 0; i < fdef->nattrs; ++i) {
110 - const char *name, *value;
111 -
112 46 3 switch (*scan) {
113 - case '=':
114 3 4 name = scan + 1;
115 21 4-6 for (scan++; *scan != '='; scan++) /* find '=' */;
116 3 7 *scan++ = '\0';
117 3 7 value = scan;
118 3 7 break;
119 - case '-':
120 ##### 8 name = scan + 1; value = git_attr__false; break;
121 - case '+':
122 16 9 name = scan + 1; value = git_attr__true; break;
123 - case '!':
124 ##### 10 name = scan + 1; value = git_attr__unset; break;
125 - default:
126 27 11 name = scan; value = NULL; break;
127 - }
128 -
129 46 12 fdef->attrs[i] = name;
130 46 12 fdef->attrs[i + fdef->nattrs] = value;
131 -
132 46 12 scan += strlen(scan) + 1;
133 - }
134 29 14 }
135 -
136 115 2 static int filter_def_name_key_check(const void *key, const void *fdef)
137 - {
138 115 5 const char *name =
139 115 2-4 fdef ? ((const git_filter_def *)fdef)->filter_name : NULL;
140 115 5 return name ? git__strcmp(key, name) : -1;
141 - }
142 -
143 20 2 static int filter_def_filter_key_check(const void *key, const void *fdef)
144 - {
145 20 2-4 const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL;
146 20 5 return (key == filter) ? 0 : -1;
147 - }
148 -
149 - /* Note: callers must lock the registry before calling this function */
150 29 2 static int filter_registry_insert(
151 - const char *name, git_filter *filter, int priority)
152 - {
153 - git_filter_def *fdef;
154 29 2 size_t nattr = 0, nmatch = 0, alloc_len;
155 29 2 git_buf attrs = GIT_BUF_INIT;
156 -
157 29 2,3 if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
158 ##### 4 return -1;
159 -
160 29 5-11 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2);
161 29 12-18 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *));
162 29 19-25 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def));
163 -
164 29 26 fdef = git__calloc(1, alloc_len);
165 29 27,28 GIT_ERROR_CHECK_ALLOC(fdef);
166 -
167 29 29 fdef->filter_name = git__strdup(name);
168 29 30,31 GIT_ERROR_CHECK_ALLOC(fdef->filter_name);
169 -
170 29 32 fdef->filter = filter;
171 29 32 fdef->priority = priority;
172 29 32 fdef->nattrs = nattr;
173 29 32 fdef->nmatches = nmatch;
174 29 32 fdef->attrdata = git_buf_detach(&attrs);
175 -
176 29 33 filter_def_set_attrs(fdef);
177 -
178 29 34,35 if (git_vector_insert(&filter_registry.filters, fdef) < 0) {
179 ##### 36 git__free(fdef->filter_name);
180 ##### 37 git__free(fdef->attrdata);
181 ##### 38 git__free(fdef);
182 ##### 39 return -1;
183 - }
184 -
185 29 40 git_vector_sort(&filter_registry.filters);
186 29 41 return 0;
187 - }
188 -
189 9 2 int git_filter_global_init(void)
190 - {
191 9 2 git_filter *crlf = NULL, *ident = NULL;
192 9 2 int error = 0;
193 -
194 9 2,3 if (git_rwlock_init(&filter_registry.lock) < 0)
195 ##### 4 return -1;
196 -
197 9 5,6 if ((error = git_vector_init(&filter_registry.filters, 2,
198 - filter_def_priority_cmp)) < 0)
199 ##### 7 goto done;
200 -
201 9 8,9,11 if ((crlf = git_crlf_filter_new()) == NULL ||
202 9 10 filter_registry_insert(
203 9 12,13 GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 ||
204 9 15 (ident = git_ident_filter_new()) == NULL ||
205 9 14 filter_registry_insert(
206 - GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
207 ##### 16 error = -1;
208 -
209 9 17 git__on_shutdown(git_filter_global_shutdown);
210 -
211 - done:
212 9 18 if (error) {
213 ##### 19 git_filter_free(crlf);
214 ##### 20 git_filter_free(ident);
215 - }
216 -
217 9 21 return error;
218 - }
219 -
220 9 2 static void git_filter_global_shutdown(void)
221 - {
222 - size_t pos;
223 - git_filter_def *fdef;
224 -
225 9 2,3 if (git_rwlock_wrlock(&filter_registry.lock) < 0)
226 9 4,19 return;
227 -
228 31 5,13-15 git_vector_foreach(&filter_registry.filters, pos, fdef) {
229 22 6,7 if (fdef->filter && fdef->filter->shutdown) {
230 22 8 fdef->filter->shutdown(fdef->filter);
231 22 9 fdef->initialized = false;
232 - }
233 -
234 22 10 git__free(fdef->filter_name);
235 22 11 git__free(fdef->attrdata);
236 22 12 git__free(fdef);
237 - }
238 -
239 9 16 git_vector_free(&filter_registry.filters);
240 -
241 9 17 git_rwlock_wrunlock(&filter_registry.lock);
242 9 18 git_rwlock_free(&filter_registry.lock);
243 - }
244 -
245 - /* Note: callers must lock the registry before calling this function */
246 30 2 static int filter_registry_find(size_t *pos, const char *name)
247 - {
248 30 2 return git_vector_search2(
249 - pos, &filter_registry.filters, filter_def_name_key_check, name);
250 - }
251 -
252 - /* Note: callers must lock the registry before calling this function */
253 18 2 static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
254 - {
255 18 2 git_filter_def *fdef = NULL;
256 -
257 18 2,3 if (!filter_registry_find(pos, name))
258 17 4 fdef = git_vector_get(&filter_registry.filters, *pos);
259 -
260 18 5 return fdef;
261 - }
262 -
263 -
264 12 2 int git_filter_register(
265 - const char *name, git_filter *filter, int priority)
266 - {
267 - int error;
268 -
269 12 2-4 assert(name && filter);
270 -
271 12 5,6 if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
272 ##### 7 git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
273 ##### 8 return -1;
274 - }
275 -
276 12 9,10 if (!filter_registry_find(NULL, name)) {
277 1 11 git_error_set(
278 - GIT_ERROR_FILTER, "attempt to reregister existing filter '%s'", name);
279 1 12 error = GIT_EEXISTS;
280 1 12 goto done;
281 - }
282 -
283 11 13 error = filter_registry_insert(name, filter, priority);
284 -
285 - done:
286 12 14 git_rwlock_wrunlock(&filter_registry.lock);
287 12 15 return error;
288 - }
289 -
290 10 2 int git_filter_unregister(const char *name)
291 - {
292 - size_t pos;
293 - git_filter_def *fdef;
294 10 2 int error = 0;
295 -
296 10 2,3 assert(name);
297 -
298 - /* cannot unregister default filters */
299 10 4,5 if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
300 2 6 git_error_set(GIT_ERROR_FILTER, "cannot unregister filter '%s'", name);
301 2 7 return -1;
302 - }
303 -
304 8 8,9 if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
305 ##### 10 git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
306 ##### 11 return -1;
307 - }
308 -
309 8 12,13 if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
310 1 14 git_error_set(GIT_ERROR_FILTER, "cannot find filter '%s' to unregister", name);
311 1 15 error = GIT_ENOTFOUND;
312 1 15 goto done;
313 - }
314 -
315 7 16 git_vector_remove(&filter_registry.filters, pos);
316 -
317 7 17-19 if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
318 3 20 fdef->filter->shutdown(fdef->filter);
319 3 21 fdef->initialized = false;
320 - }
321 -
322 7 22 git__free(fdef->filter_name);
323 7 23 git__free(fdef->attrdata);
324 7 24 git__free(fdef);
325 -
326 - done:
327 8 25 git_rwlock_wrunlock(&filter_registry.lock);
328 8 26 return error;
329 - }
330 -
331 17 2 static int filter_initialize(git_filter_def *fdef)
332 - {
333 17 2 int error = 0;
334 -
335 17 2-4 if (!fdef->initialized && fdef->filter && fdef->filter->initialize) {
336 ##### 5,6 if ((error = fdef->filter->initialize(fdef->filter)) < 0)
337 ##### 7 return error;
338 - }
339 -
340 17 8 fdef->initialized = true;
341 17 8 return 0;
342 - }
343 -
344 10 2 git_filter *git_filter_lookup(const char *name)
345 - {
346 - size_t pos;
347 - git_filter_def *fdef;
348 10 2 git_filter *filter = NULL;
349 -
350 10 2,3 if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
351 ##### 4 git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
352 ##### 5 return NULL;
353 - }
354 -
355 10 6-8 if ((fdef = filter_registry_lookup(&pos, name)) == NULL ||
356 10 8-10 (!fdef->initialized && filter_initialize(fdef) < 0))
357 - goto done;
358 -
359 10 11 filter = fdef->filter;
360 -
361 - done:
362 10 12 git_rwlock_rdunlock(&filter_registry.lock);
363 10 13 return filter;
364 - }
365 -
366 18 2 void git_filter_free(git_filter *filter)
367 - {
368 18 2 git__free(filter);
369 18 3 }
370 -
371 36298 2 git_repository *git_filter_source_repo(const git_filter_source *src)
372 - {
373 36298 2 return src->repo;
374 - }
375 -
376 1984 2 const char *git_filter_source_path(const git_filter_source *src)
377 - {
378 1984 2 return src->path;
379 - }
380 -
381 ##### 2 uint16_t git_filter_source_filemode(const git_filter_source *src)
382 - {
383 ##### 2 return src->filemode;
384 - }
385 -
386 40 2 const git_oid *git_filter_source_id(const git_filter_source *src)
387 - {
388 40 2 return git_oid_is_zero(&src->oid) ? NULL : &src->oid;
389 - }
390 -
391 2725 2 git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
392 - {
393 2725 2 return src->mode;
394 - }
395 -
396 11937 2 uint32_t git_filter_source_flags(const git_filter_source *src)
397 - {
398 11937 2 return src->flags;
399 - }
400 -
401 2680 2 static int filter_list_new(
402 - git_filter_list **out, const git_filter_source *src)
403 - {
404 2680 2 git_filter_list *fl = NULL;
405 2680 2-4 size_t pathlen = src->path ? strlen(src->path) : 0, alloclen;
406 -
407 2680 5-11 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen);
408 2680 12-18 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
409 -
410 2680 19 fl = git__calloc(1, alloclen);
411 2680 20,21 GIT_ERROR_CHECK_ALLOC(fl);
412 -
413 2680 22 if (src->path)
414 2670 23 memcpy(fl->path, src->path, pathlen);
415 2680 24 fl->source.repo = src->repo;
416 2680 24 fl->source.path = fl->path;
417 2680 24 fl->source.mode = src->mode;
418 2680 24 fl->source.flags = src->flags;
419 -
420 2680 24 *out = fl;
421 2680 24 return 0;
422 - }
423 -
424 56540 2 static int filter_list_check_attributes(
425 - const char ***out,
426 - git_repository *repo,
427 - git_attr_session *attr_session,
428 - git_filter_def *fdef,
429 - const git_filter_source *src)
430 - {
431 56540 2 const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
432 56546 3 uint32_t flags = 0;
433 - size_t i;
434 - int error;
435 -
436 56546 3,4 GIT_ERROR_CHECK_ALLOC(strs);
437 -
438 56546 5 if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
439 44 6 flags |= GIT_ATTR_CHECK_NO_SYSTEM;
440 -
441 56546 7 if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
442 26 8 flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
443 -
444 56546 9 error = git_attr_get_many_with_session(
445 56546 9 strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs);
446 -
447 - /* if no values were found but no matches are needed, it's okay! */
448 56566 10,11 if (error == GIT_ENOTFOUND && !fdef->nmatches) {
449 ##### 12 git_error_clear();
450 ##### 13 git__free((void *)strs);
451 ##### 14 return 0;
452 - }
453 -
454 136929 15,26-28 for (i = 0; !error && i < fdef->nattrs; ++i) {
455 80393 16 const char *want = fdef->attrs[fdef->nattrs + i];
456 - git_attr_value_t want_type, found_type;
457 -
458 80393 16 if (!want)
459 35757 17 continue;
460 -
461 44636 18 want_type = git_attr_value(want);
462 44607 19 found_type = git_attr_value(strs[i]);
463 -
464 44606 20 if (want_type != found_type)
465 44557 21 error = GIT_ENOTFOUND;
466 49 22,23 else if (want_type == GIT_ATTR_VALUE_STRING &&
467 3 23,24 strcmp(want, strs[i]) &&
468 3 24 strcmp(want, "*"))
469 ##### 25 error = GIT_ENOTFOUND;
470 - }
471 -
472 56536 29 if (error)
473 44558 30 git__free((void *)strs);
474 - else
475 11978 31 *out = strs;
476 -
477 56546 32 return error;
478 - }
479 -
480 10 2 int git_filter_list_new(
481 - git_filter_list **out,
482 - git_repository *repo,
483 - git_filter_mode_t mode,
484 - uint32_t flags)
485 - {
486 10 2 git_filter_source src = { 0 };
487 10 2 src.repo = repo;
488 10 2 src.path = NULL;
489 10 2 src.mode = mode;
490 10 2 src.flags = flags;
491 10 2 return filter_list_new(out, &src);
492 - }
493 -
494 11929 2 int git_filter_list__load_ext(
495 - git_filter_list **filters,
496 - git_repository *repo,
497 - git_blob *blob, /* can be NULL */
498 - const char *path,
499 - git_filter_mode_t mode,
500 - git_filter_options *filter_opts)
501 - {
502 11929 2 int error = 0;
503 11929 2 git_filter_list *fl = NULL;
504 11929 2 git_filter_source src = { 0 };
505 - git_filter_entry *fe;
506 - size_t idx;
507 - git_filter_def *fdef;
508 -
509 11929 2,3 if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
510 ##### 4 git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
511 ##### 5 return -1;
512 - }
513 -
514 11929 6 src.repo = repo;
515 11929 6 src.path = path;
516 11929 6 src.mode = mode;
517 11929 6 src.flags = filter_opts->flags;
518 -
519 11929 6 if (blob)
520 2747 7,8 git_oid_cpy(&src.oid, git_blob_id(blob));
521 -
522 68471 9,47-49 git_vector_foreach(&filter_registry.filters, idx, fdef) {
523 56547 10 const char **values = NULL;
524 56547 10 void *payload = NULL;
525 -
526 56547 10,11 if (!fdef || !fdef->filter)
527 44564 12,45 continue;
528 -
529 56545 13 if (fdef->nattrs > 0) {
530 56543 14 error = filter_list_check_attributes(
531 - &values, repo, filter_opts->attr_session, fdef, &src);
532 -
533 56540 15 if (error == GIT_ENOTFOUND) {
534 44562 16 error = 0;
535 44562 16 continue;
536 11978 17 } else if (error < 0)
537 ##### 18,46 break;
538 - }
539 -
540 11980 19-21 if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
541 ##### 22 break;
542 -
543 11980 23 if (fdef->filter->check)
544 11932 24 error = fdef->filter->check(
545 - fdef->filter, &payload, &src, values);
546 -
547 11979 25 git__free((void *)values);
548 -
549 11978 26 if (error == GIT_PASSTHROUGH)
550 9269 27 error = 0;
551 2709 28 else if (error < 0)
552 ##### 29 break;
553 - else {
554 2709 30 if (!fl) {
555 2670 31,32 if ((error = filter_list_new(&fl, &src)) < 0)
556 ##### 33 break;
557 -
558 2670 34 fl->temp_buf = filter_opts->temp_buf;
559 - }
560 -
561 2709 35-40 fe = git_array_alloc(fl->filters);
562 2709 41,42 GIT_ERROR_CHECK_ALLOC(fe);
563 -
564 2709 43 fe->filter = fdef->filter;
565 2709 43 fe->filter_name = fdef->filter_name;
566 11978 43,44 fe->payload = payload;
567 - }
568 - }
569 -
570 11924 50 git_rwlock_rdunlock(&filter_registry.lock);
571 -
572 11929 51,52 if (error && fl != NULL) {
573 ##### 53 git_array_clear(fl->filters);
574 ##### 54 git__free(fl);
575 ##### 55 fl = NULL;
576 - }
577 -
578 11929 56 *filters = fl;
579 11929 56 return error;
580 - }
581 -
582 9102 2 int git_filter_list_load(
583 - git_filter_list **filters,
584 - git_repository *repo,
585 - git_blob *blob, /* can be NULL */
586 - const char *path,
587 - git_filter_mode_t mode,
588 - uint32_t flags)
589 - {
590 9102 2 git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
591 -
592 9102 2 filter_opts.flags = flags;
593 -
594 9102 2 return git_filter_list__load_ext(
595 - filters, repo, blob, path, mode, &filter_opts);
596 - }
597 -
598 9903 2 void git_filter_list_free(git_filter_list *fl)
599 - {
600 - uint32_t i;
601 -
602 9903 2 if (!fl)
603 9903 3,14 return;
604 -
605 5399 4,10,11 for (i = 0; i < git_array_size(fl->filters); ++i) {
606 2719 5-7 git_filter_entry *fe = git_array_get(fl->filters, i);
607 2719 8 if (fe->filter->cleanup)
608 2668 9 fe->filter->cleanup(fe->filter, fe->payload);
609 - }
610 -
611 2680 12 git_array_clear(fl->filters);
612 2680 13 git__free(fl);
613 - }
614 -
615 22 2 int git_filter_list_contains(
616 - git_filter_list *fl,
617 - const char *name)
618 - {
619 - size_t i;
620 -
621 22 2,3 assert(name);
622 -
623 22 4 if (!fl)
624 4 5 return 0;
625 -
626 27 6,9,10 for (i = 0; i < fl->filters.size; i++) {
627 24 7 if (strcmp(fl->filters.ptr[i].filter_name, name) == 0)
628 15 8 return 1;
629 - }
630 -
631 3 11 return 0;
632 - }
633 -
634 10 2 int git_filter_list_push(
635 - git_filter_list *fl, git_filter *filter, void *payload)
636 - {
637 10 2 int error = 0;
638 - size_t pos;
639 10 2 git_filter_def *fdef = NULL;
640 - git_filter_entry *fe;
641 -
642 10 2-4 assert(fl && filter);
643 -
644 10 5,6 if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
645 ##### 7 git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
646 ##### 8 return -1;
647 - }
648 -
649 10 9,10 if (git_vector_search2(
650 - &pos, &filter_registry.filters,
651 - filter_def_filter_key_check, filter) == 0)
652 10 11 fdef = git_vector_get(&filter_registry.filters, pos);
653 -
654 10 12 git_rwlock_rdunlock(&filter_registry.lock);
655 -
656 10 13 if (fdef == NULL) {
657 ##### 14 git_error_set(GIT_ERROR_FILTER, "cannot use an unregistered filter");
658 ##### 15 return -1;
659 - }
660 -
661 10 16-18 if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
662 ##### 19 return error;
663 -
664 10 20-25 fe = git_array_alloc(fl->filters);
665 10 26,27 GIT_ERROR_CHECK_ALLOC(fe);
666 10 28 fe->filter = filter;
667 10 28 fe->payload = payload;
668 -
669 10 28 return 0;
670 - }
671 -
672 5 2 size_t git_filter_list_length(const git_filter_list *fl)
673 - {
674 5 2 return fl ? git_array_size(fl->filters) : 0;
675 - }
676 -
677 - struct buf_stream {
678 - git_writestream parent;
679 - git_buf *target;
680 - bool complete;
681 - };
682 -
683 1697 2 static int buf_stream_write(
684 - git_writestream *s, const char *buffer, size_t len)
685 - {
686 1697 2 struct buf_stream *buf_stream = (struct buf_stream *)s;
687 1697 2,3 assert(buf_stream);
688 -
689 1697 4,5 assert(buf_stream->complete == 0);
690 -
691 1697 6 return git_buf_put(buf_stream->target, buffer, len);
692 - }
693 -
694 1968 2 static int buf_stream_close(git_writestream *s)
695 - {
696 1968 2 struct buf_stream *buf_stream = (struct buf_stream *)s;
697 1968 2,3 assert(buf_stream);
698 -
699 1968 4,5 assert(buf_stream->complete == 0);
700 1968 6 buf_stream->complete = 1;
701 -
702 1968 6 return 0;
703 - }
704 -
705 ##### 2 static void buf_stream_free(git_writestream *s)
706 - {
707 - GIT_UNUSED(s);
708 ##### 2 }
709 -
710 1968 2 static void buf_stream_init(struct buf_stream *writer, git_buf *target)
711 - {
712 1968 2 memset(writer, 0, sizeof(struct buf_stream));
713 -
714 1968 2 writer->parent.write = buf_stream_write;
715 1968 2 writer->parent.close = buf_stream_close;
716 1968 2 writer->parent.free = buf_stream_free;
717 1968 2 writer->target = target;
718 -
719 1968 2 git_buf_clear(target);
720 1968 3 }
721 -
722 399 2 int git_filter_list_apply_to_data(
723 - git_buf *tgt, git_filter_list *filters, git_buf *src)
724 - {
725 - struct buf_stream writer;
726 - int error;
727 -
728 399 2 git_buf_sanitize(tgt);
729 399 3 git_buf_sanitize(src);
730 -
731 399 4 if (!filters) {
732 84 5 git_buf_attach_notowned(tgt, src->ptr, src->size);
733 84 6 return 0;
734 - }
735 -
736 315 7 buf_stream_init(&writer, tgt);
737 -
738 315 8,9 if ((error = git_filter_list_stream_data(filters, src,
739 - &writer.parent)) < 0)
740 3 10 return error;
741 -
742 312 11,12 assert(writer.complete);
743 312 13 return error;
744 - }
745 -
746 1572 2 int git_filter_list_apply_to_file(
747 - git_buf *out,
748 - git_filter_list *filters,
749 - git_repository *repo,
750 - const char *path)
751 - {
752 - struct buf_stream writer;
753 - int error;
754 -
755 1572 2 buf_stream_init(&writer, out);
756 -
757 1572 3,4 if ((error = git_filter_list_stream_file(
758 - filters, repo, path, &writer.parent)) < 0)
759 272 5 return error;
760 -
761 1300 6,7 assert(writer.complete);
762 1300 8 return error;
763 - }
764 -
765 2796 2 static int buf_from_blob(git_buf *out, git_blob *blob)
766 - {
767 2796 2 git_object_size_t rawsize = git_blob_rawsize(blob);
768 -
769 2796 3,4 if (!git__is_sizet(rawsize)) {
770 ##### 5 git_error_set(GIT_ERROR_OS, "blob is too large to filter");
771 ##### 6 return -1;
772 - }
773 -
774 2796 7,8 git_buf_attach_notowned(out, git_blob_rawcontent(blob), (size_t)rawsize);
775 2796 9 return 0;
776 - }
777 -
778 81 2 int git_filter_list_apply_to_blob(
779 - git_buf *out,
780 - git_filter_list *filters,
781 - git_blob *blob)
782 - {
783 - struct buf_stream writer;
784 - int error;
785 -
786 81 2 buf_stream_init(&writer, out);
787 -
788 81 3,4 if ((error = git_filter_list_stream_blob(
789 - filters, blob, &writer.parent)) < 0)
790 ##### 5 return error;
791 -
792 81 6,7 assert(writer.complete);
793 81 8 return error;
794 - }
795 -
796 - struct proxy_stream {
797 - git_writestream parent;
798 - git_filter *filter;
799 - const git_filter_source *source;
800 - void **payload;
801 - git_buf input;
802 - git_buf temp_buf;
803 - git_buf *output;
804 - git_writestream *target;
805 - };
806 -
807 2655 2 static int proxy_stream_write(
808 - git_writestream *s, const char *buffer, size_t len)
809 - {
810 2655 2 struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
811 2655 2,3 assert(proxy_stream);
812 -
813 2655 4 return git_buf_put(&proxy_stream->input, buffer, len);
814 - }
815 -
816 2733 2 static int proxy_stream_close(git_writestream *s)
817 - {
818 2733 2 struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
819 - git_buf *writebuf;
820 2733 2 git_error_state error_state = {0};
821 - int error;
822 -
823 2733 2,3 assert(proxy_stream);
824 -
825 2733 4,4 error = proxy_stream->filter->apply(
826 - proxy_stream->filter,
827 - proxy_stream->payload,
828 - proxy_stream->output,
829 2733 4 &proxy_stream->input,
830 - proxy_stream->source);
831 -
832 2733 5 if (error == GIT_PASSTHROUGH) {
833 1670 6 writebuf = &proxy_stream->input;
834 1063 7 } else if (error == 0) {
835 789 8 git_buf_sanitize(proxy_stream->output);
836 789 9 writebuf = proxy_stream->output;
837 - } else {
838 - /* close stream before erroring out taking care
839 - * to preserve the original error */
840 274 10 git_error_state_capture(&error_state, error);
841 274 11 proxy_stream->target->close(proxy_stream->target);
842 274 12 git_error_state_restore(&error_state);
843 274 13 return error;
844 - }
845 -
846 2459 14,14,15 if ((error = proxy_stream->target->write(
847 2459 14 proxy_stream->target, writebuf->ptr, writebuf->size)) == 0)
848 2459 16 error = proxy_stream->target->close(proxy_stream->target);
849 -
850 2459 17 return error;
851 - }
852 -
853 2733 2 static void proxy_stream_free(git_writestream *s)
854 - {
855 2733 2 struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
856 2733 2,3 assert(proxy_stream);
857 -
858 2733 4 git_buf_dispose(&proxy_stream->input);
859 2733 5 git_buf_dispose(&proxy_stream->temp_buf);
860 2733 6 git__free(proxy_stream);
861 2733 7 }
862 -
863 2733 2 static int proxy_stream_init(
864 - git_writestream **out,
865 - git_filter *filter,
866 - git_buf *temp_buf,
867 - void **payload,
868 - const git_filter_source *source,
869 - git_writestream *target)
870 - {
871 2733 2 struct proxy_stream *proxy_stream = git__calloc(1, sizeof(struct proxy_stream));
872 2733 3,4 GIT_ERROR_CHECK_ALLOC(proxy_stream);
873 -
874 2733 5 proxy_stream->parent.write = proxy_stream_write;
875 2733 5 proxy_stream->parent.close = proxy_stream_close;
876 2733 5 proxy_stream->parent.free = proxy_stream_free;
877 2733 5 proxy_stream->filter = filter;
878 2733 5 proxy_stream->payload = payload;
879 2733 5 proxy_stream->source = source;
880 2733 5 proxy_stream->target = target;
881 2733 5-7 proxy_stream->output = temp_buf ? temp_buf : &proxy_stream->temp_buf;
882 -
883 2733 8 if (temp_buf)
884 845 9 git_buf_clear(temp_buf);
885 -
886 2733 10 *out = (git_writestream *)proxy_stream;
887 2733 10 return 0;
888 - }
889 -
890 4684 2 static int stream_list_init(
891 - git_writestream **out,
892 - git_vector *streams,
893 - git_filter_list *filters,
894 - git_writestream *target)
895 - {
896 4684 2 git_writestream *last_stream = target;
897 - size_t i;
898 4684 2 int error = 0;
899 -
900 4684 2 *out = NULL;
901 -
902 4684 2 if (!filters) {
903 1972 3 *out = target;
904 1972 3 return 0;
905 - }
906 -
907 - /* Create filters last to first to get the chaining direction */
908 5447 4,20,21 for (i = 0; i < git_array_size(filters->filters); ++i) {
909 2736 5,8 size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ?
910 2736 5-7 git_array_size(filters->filters) - 1 - i : i;
911 2736 8-10 git_filter_entry *fe = git_array_get(filters->filters, filter_idx);
912 - git_writestream *filter_stream;
913 -
914 2736 11-13 assert(fe->filter->stream || fe->filter->apply);
915 -
916 - /* If necessary, create a stream that proxies the traditional
917 - * application.
918 - */
919 2736 14 if (fe->filter->stream)
920 3 15,15 error = fe->filter->stream(&filter_stream, fe->filter,
921 3 15 &fe->payload, &filters->source, last_stream);
922 - else
923 - /* Create a stream that proxies the one-shot apply */
924 2733 16 error = proxy_stream_init(&filter_stream, fe->filter,
925 2733 16 filters->temp_buf, &fe->payload, &filters->source,
926 - last_stream);
927 -
928 2736 17 if (error < 0)
929 1 18 goto out;
930 -
931 2735 19 git_vector_insert(streams, filter_stream);
932 2735 20 last_stream = filter_stream;
933 - }
934 -
935 - out:
936 2712 22 if (error)
937 1 23 last_stream->close(last_stream);
938 - else
939 2711 24 *out = last_stream;
940 -
941 2712 25 return error;
942 - }
943 -
944 4684 2 static void filter_streams_free(git_vector *streams)
945 - {
946 - git_writestream *stream;
947 - size_t i;
948 -
949 7419 2,4-6 git_vector_foreach(streams, i, stream)
950 2735 3 stream->free(stream);
951 4684 7 git_vector_free(streams);
952 4684 8 }
953 -
954 1573 2 int git_filter_list_stream_file(
955 - git_filter_list *filters,
956 - git_repository *repo,
957 - const char *path,
958 - git_writestream *target)
959 - {
960 - char buf[FILTERIO_BUFSIZE];
961 1573 2 git_buf abspath = GIT_BUF_INIT;
962 1573 2-4 const char *base = repo ? git_repository_workdir(repo) : NULL;
963 1573 5 git_vector filter_streams = GIT_VECTOR_INIT;
964 - git_writestream *stream_start;
965 - ssize_t readlen;
966 1573 5 int fd = -1, error, initialized = 0;
967 -
968 1573 5,6 if ((error = stream_list_init(
969 1573 7,8 &stream_start, &filter_streams, filters, target)) < 0 ||
970 - (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0)
971 - goto done;
972 1573 9 initialized = 1;
973 -
974 1573 9,10 if ((fd = git_futils_open_ro(abspath.ptr)) < 0) {
975 ##### 11 error = fd;
976 ##### 11 goto done;
977 - }
978 -
979 3068 12,16,17 while ((readlen = p_read(fd, buf, sizeof(buf))) > 0) {
980 1495 13,14 if ((error = stream_start->write(stream_start, buf, readlen)) < 0)
981 ##### 15 goto done;
982 - }
983 -
984 1573 18 if (readlen < 0)
985 ##### 19 error = -1;
986 -
987 - done:
988 1573 20 if (initialized)
989 1573 21,22 error |= stream_start->close(stream_start);
990 -
991 1573 23 if (fd >= 0)
992 1573 24 p_close(fd);
993 1573 25 filter_streams_free(&filter_streams);
994 1573 26 git_buf_dispose(&abspath);
995 1573 27 return error;
996 - }
997 -
998 3111 2 int git_filter_list_stream_data(
999 - git_filter_list *filters,
1000 - git_buf *data,
1001 - git_writestream *target)
1002 - {
1003 3111 2 git_vector filter_streams = GIT_VECTOR_INIT;
1004 - git_writestream *stream_start;
1005 3111 2 int error, initialized = 0;
1006 -
1007 3111 2 git_buf_sanitize(data);
1008 -
1009 3111 3,4 if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
1010 1 5 goto out;
1011 3110 6 initialized = 1;
1012 -
1013 3110 6,6,7 if ((error = stream_start->write(
1014 3110 6 stream_start, data->ptr, data->size)) < 0)
1015 ##### 8 goto out;
1016 -
1017 - out:
1018 3111 9 if (initialized)
1019 3110 10,11 error |= stream_start->close(stream_start);
1020 -
1021 3111 12 filter_streams_free(&filter_streams);
1022 3111 13 return error;
1023 - }
1024 -
1025 2796 2 int git_filter_list_stream_blob(
1026 - git_filter_list *filters,
1027 - git_blob *blob,
1028 - git_writestream *target)
1029 - {
1030 2796 2 git_buf in = GIT_BUF_INIT;
1031 -
1032 2796 2,3 if (buf_from_blob(&in, blob) < 0)
1033 ##### 4 return -1;
1034 -
1035 2796 5 if (filters)
1036 879 6,7 git_oid_cpy(&filters->source.oid, git_blob_id(blob));
1037 -
1038 2796 8 return git_filter_list_stream_data(filters, &in, target);
1039 - }
1040 -
1041 1 2 int git_filter_init(git_filter *filter, unsigned int version)
1042 - {
1043 1 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT);
1044 1 5 return 0;
1045 - }