]> git.proxmox.com Git - libgit2.git/blob - src/config_file.c
0c05fb515003fcef4af513b32ca1e882226b4031
[libgit2.git] / src / config_file.c
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 "config_file.h"
9
10 #include "config.h"
11 #include "filebuf.h"
12 #include "sysdir.h"
13 #include "buffer.h"
14 #include "buf_text.h"
15 #include "git2/config.h"
16 #include "git2/sys/config.h"
17 #include "git2/types.h"
18 #include "strmap.h"
19 #include "array.h"
20 #include "config_parse.h"
21
22 #include <ctype.h>
23 #include <sys/types.h>
24 #include <regex.h>
25
26 typedef struct cvar_t {
27 struct cvar_t *next;
28 git_config_entry *entry;
29 bool included; /* whether this is part of [include] */
30 } cvar_t;
31
32 typedef struct git_config_file_iter {
33 git_config_iterator parent;
34 git_strmap_iter iter;
35 cvar_t* next_var;
36 } git_config_file_iter;
37
38 /* Max depth for [include] directives */
39 #define MAX_INCLUDE_DEPTH 10
40
41 #define CVAR_LIST_HEAD(list) ((list)->head)
42
43 #define CVAR_LIST_TAIL(list) ((list)->tail)
44
45 #define CVAR_LIST_NEXT(var) ((var)->next)
46
47 #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
48
49 #define CVAR_LIST_APPEND(list, var) do {\
50 if (CVAR_LIST_EMPTY(list)) {\
51 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
52 } else {\
53 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
54 CVAR_LIST_TAIL(list) = var;\
55 }\
56 } while(0)
57
58 #define CVAR_LIST_REMOVE_HEAD(list) do {\
59 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
60 } while(0)
61
62 #define CVAR_LIST_REMOVE_AFTER(var) do {\
63 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
64 } while(0)
65
66 #define CVAR_LIST_FOREACH(list, iter)\
67 for ((iter) = CVAR_LIST_HEAD(list);\
68 (iter) != NULL;\
69 (iter) = CVAR_LIST_NEXT(iter))
70
71 /*
72 * Inspired by the FreeBSD functions
73 */
74 #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
75 for ((iter) = CVAR_LIST_HEAD(vars);\
76 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
77 (iter) = (tmp))
78
79 typedef struct {
80 git_atomic refcount;
81 git_strmap *values;
82 } refcounted_strmap;
83
84 typedef struct {
85 git_config_backend parent;
86 /* mutex to coordinate accessing the values */
87 git_mutex values_mutex;
88 refcounted_strmap *values;
89 const git_repository *repo;
90 git_config_level_t level;
91 } diskfile_header;
92
93 typedef struct {
94 diskfile_header header;
95
96 git_array_t(git_config_parser) readers;
97
98 bool locked;
99 git_filebuf locked_buf;
100 git_buf locked_content;
101
102 struct config_file file;
103 } diskfile_backend;
104
105 typedef struct {
106 diskfile_header header;
107
108 diskfile_backend *snapshot_from;
109 } diskfile_readonly_backend;
110
111 static int config_read(git_strmap *values, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
112 static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
113 static char *escape_value(const char *ptr);
114
115 int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
116 static int config_snapshot(git_config_backend **out, git_config_backend *in);
117
118 static int config_error_readonly(void)
119 {
120 giterr_set(GITERR_CONFIG, "this backend is read-only");
121 return -1;
122 }
123
124 static void cvar_free(cvar_t *var)
125 {
126 if (var == NULL)
127 return;
128
129 git__free((char*)var->entry->name);
130 git__free((char *)var->entry->value);
131 git__free(var->entry);
132 git__free(var);
133 }
134
135 int git_config_file_normalize_section(char *start, char *end)
136 {
137 char *scan;
138
139 if (start == end)
140 return GIT_EINVALIDSPEC;
141
142 /* Validate and downcase range */
143 for (scan = start; *scan; ++scan) {
144 if (end && scan >= end)
145 break;
146 if (isalnum(*scan))
147 *scan = (char)git__tolower(*scan);
148 else if (*scan != '-' || scan == start)
149 return GIT_EINVALIDSPEC;
150 }
151
152 if (scan == start)
153 return GIT_EINVALIDSPEC;
154
155 return 0;
156 }
157
158 /* Add or append the new config option */
159 static int append_entry(git_strmap *values, cvar_t *var)
160 {
161 git_strmap_iter pos;
162 cvar_t *existing;
163 int error = 0;
164
165 pos = git_strmap_lookup_index(values, var->entry->name);
166 if (!git_strmap_valid_index(values, pos)) {
167 git_strmap_insert(values, var->entry->name, var, &error);
168 } else {
169 existing = git_strmap_value_at(values, pos);
170 while (existing->next != NULL) {
171 existing = existing->next;
172 }
173 existing->next = var;
174 }
175
176 if (error > 0)
177 error = 0;
178
179 return error;
180 }
181
182 static void free_vars(git_strmap *values)
183 {
184 cvar_t *var = NULL;
185
186 if (values == NULL)
187 return;
188
189 git_strmap_foreach_value(values, var,
190 while (var != NULL) {
191 cvar_t *next = CVAR_LIST_NEXT(var);
192 cvar_free(var);
193 var = next;
194 });
195
196 git_strmap_free(values);
197 }
198
199 static void refcounted_strmap_free(refcounted_strmap *map)
200 {
201 if (!map)
202 return;
203
204 if (git_atomic_dec(&map->refcount) != 0)
205 return;
206
207 free_vars(map->values);
208 git__free(map);
209 }
210
211 /**
212 * Take the current values map from the backend and increase its
213 * refcount. This is its own function to make sure we use the mutex to
214 * avoid the map pointer from changing under us.
215 */
216 static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
217 {
218 refcounted_strmap *map;
219
220 if (git_mutex_lock(&h->values_mutex) < 0) {
221 giterr_set(GITERR_OS, "failed to lock config backend");
222 return NULL;
223 }
224
225 map = h->values;
226 git_atomic_inc(&map->refcount);
227
228 git_mutex_unlock(&h->values_mutex);
229
230 return map;
231 }
232
233 static int refcounted_strmap_alloc(refcounted_strmap **out)
234 {
235 refcounted_strmap *map;
236 int error;
237
238 map = git__calloc(1, sizeof(refcounted_strmap));
239 GITERR_CHECK_ALLOC(map);
240
241 git_atomic_set(&map->refcount, 1);
242
243 if ((error = git_strmap_alloc(&map->values)) < 0)
244 git__free(map);
245 else
246 *out = map;
247
248 return error;
249 }
250
251 static void config_file_clear(struct config_file *file)
252 {
253 struct config_file *include;
254 uint32_t i;
255
256 if (file == NULL)
257 return;
258
259 git_array_foreach(file->includes, i, include) {
260 config_file_clear(include);
261 }
262 git_array_clear(file->includes);
263
264 git__free(file->path);
265 }
266
267 static int config_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
268 {
269 int res;
270 diskfile_backend *b = (diskfile_backend *)cfg;
271
272 b->header.level = level;
273 b->header.repo = repo;
274
275 if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
276 return res;
277
278 if (!git_path_exists(b->file.path))
279 return 0;
280
281 if (res < 0 || (res = config_read(b->header.values->values, repo, &b->file, level, 0)) < 0) {
282 refcounted_strmap_free(b->header.values);
283 b->header.values = NULL;
284 }
285
286 return res;
287 }
288
289 static int config_is_modified(int *modified, struct config_file *file)
290 {
291 git_config_file *include;
292 git_buf buf = GIT_BUF_INIT;
293 git_oid hash;
294 uint32_t i;
295 int error = 0;
296
297 *modified = 0;
298
299 if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
300 goto out;
301
302 if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0)
303 goto out;
304
305 if (!git_oid_equal(&hash, &file->checksum)) {
306 *modified = 1;
307 goto out;
308 }
309
310 git_array_foreach(file->includes, i, include) {
311 if ((error = config_is_modified(modified, include)) < 0 || *modified)
312 goto out;
313 }
314
315 out:
316 git_buf_free(&buf);
317
318 return error;
319 }
320
321 static int config_refresh(git_config_backend *cfg)
322 {
323 diskfile_backend *b = (diskfile_backend *)cfg;
324 refcounted_strmap *values = NULL, *tmp;
325 git_config_file *include;
326 int error, modified;
327 uint32_t i;
328
329 if (b->header.parent.readonly)
330 return config_error_readonly();
331
332 error = config_is_modified(&modified, &b->file);
333 if (error < 0 && error != GIT_ENOTFOUND)
334 goto out;
335
336 if (!modified)
337 return 0;
338
339 if ((error = refcounted_strmap_alloc(&values)) < 0)
340 goto out;
341
342 /* Reparse the current configuration */
343 git_array_foreach(b->file.includes, i, include) {
344 config_file_clear(include);
345 }
346 git_array_clear(b->file.includes);
347
348 if ((error = config_read(values->values, b->header.repo, &b->file, b->header.level, 0)) < 0)
349 goto out;
350
351 if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
352 giterr_set(GITERR_OS, "failed to lock config backend");
353 goto out;
354 }
355
356 tmp = b->header.values;
357 b->header.values = values;
358 values = tmp;
359
360 git_mutex_unlock(&b->header.values_mutex);
361
362 out:
363 refcounted_strmap_free(values);
364
365 return (error == GIT_ENOTFOUND) ? 0 : error;
366 }
367
368 static void backend_free(git_config_backend *_backend)
369 {
370 diskfile_backend *backend = (diskfile_backend *)_backend;
371
372 if (backend == NULL)
373 return;
374
375 config_file_clear(&backend->file);
376 refcounted_strmap_free(backend->header.values);
377 git_mutex_free(&backend->header.values_mutex);
378 git__free(backend);
379 }
380
381 static void config_iterator_free(
382 git_config_iterator* iter)
383 {
384 iter->backend->free(iter->backend);
385 git__free(iter);
386 }
387
388 static int config_iterator_next(
389 git_config_entry **entry,
390 git_config_iterator *iter)
391 {
392 git_config_file_iter *it = (git_config_file_iter *) iter;
393 diskfile_header *h = (diskfile_header *) it->parent.backend;
394 git_strmap *values = h->values->values;
395 int err = 0;
396 cvar_t * var;
397
398 if (it->next_var == NULL) {
399 err = git_strmap_next((void**) &var, &(it->iter), values);
400 } else {
401 var = it->next_var;
402 }
403
404 if (err < 0) {
405 it->next_var = NULL;
406 return err;
407 }
408
409 *entry = var->entry;
410 it->next_var = CVAR_LIST_NEXT(var);
411
412 return 0;
413 }
414
415 static int config_iterator_new(
416 git_config_iterator **iter,
417 struct git_config_backend* backend)
418 {
419 diskfile_header *h;
420 git_config_file_iter *it;
421 git_config_backend *snapshot;
422 diskfile_header *bh = (diskfile_header *) backend;
423 int error;
424
425 if ((error = config_snapshot(&snapshot, backend)) < 0)
426 return error;
427
428 if ((error = snapshot->open(snapshot, bh->level, bh->repo)) < 0)
429 return error;
430
431 it = git__calloc(1, sizeof(git_config_file_iter));
432 GITERR_CHECK_ALLOC(it);
433
434 h = (diskfile_header *)snapshot;
435
436 /* strmap_begin() is currently a macro returning 0 */
437 GIT_UNUSED(h);
438
439 it->parent.backend = snapshot;
440 it->iter = git_strmap_begin(h->values);
441 it->next_var = NULL;
442
443 it->parent.next = config_iterator_next;
444 it->parent.free = config_iterator_free;
445 *iter = (git_config_iterator *) it;
446
447 return 0;
448 }
449
450 static int config_set(git_config_backend *cfg, const char *name, const char *value)
451 {
452 diskfile_backend *b = (diskfile_backend *)cfg;
453 refcounted_strmap *map;
454 git_strmap *values;
455 char *key, *esc_value = NULL;
456 khiter_t pos;
457 int rval, ret;
458
459 if ((rval = git_config__normalize_name(name, &key)) < 0)
460 return rval;
461
462 if ((map = refcounted_strmap_take(&b->header)) == NULL)
463 return -1;
464 values = map->values;
465
466 /*
467 * Try to find it in the existing values and update it if it
468 * only has one value.
469 */
470 pos = git_strmap_lookup_index(values, key);
471 if (git_strmap_valid_index(values, pos)) {
472 cvar_t *existing = git_strmap_value_at(values, pos);
473
474 if (existing->next != NULL) {
475 giterr_set(GITERR_CONFIG, "multivar incompatible with simple set");
476 ret = -1;
477 goto out;
478 }
479
480 if (existing->included) {
481 giterr_set(GITERR_CONFIG, "modifying included variable is not supported");
482 ret = -1;
483 goto out;
484 }
485
486 /* don't update if old and new values already match */
487 if ((!existing->entry->value && !value) ||
488 (existing->entry->value && value &&
489 !strcmp(existing->entry->value, value))) {
490 ret = 0;
491 goto out;
492 }
493 }
494
495 /* No early returns due to sanity checks, let's write it out and refresh */
496
497 if (value) {
498 esc_value = escape_value(value);
499 GITERR_CHECK_ALLOC(esc_value);
500 }
501
502 if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
503 goto out;
504
505 ret = config_refresh(cfg);
506
507 out:
508 refcounted_strmap_free(map);
509 git__free(esc_value);
510 git__free(key);
511 return ret;
512 }
513
514 /* release the map containing the entry as an equivalent to freeing it */
515 static void release_map(git_config_entry *entry)
516 {
517 refcounted_strmap *map = (refcounted_strmap *) entry->payload;
518 refcounted_strmap_free(map);
519 }
520
521 /*
522 * Internal function that actually gets the value in string form
523 */
524 static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
525 {
526 diskfile_header *h = (diskfile_header *)cfg;
527 refcounted_strmap *map;
528 git_strmap *values;
529 khiter_t pos;
530 cvar_t *var;
531 int error = 0;
532
533 if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
534 return error;
535
536 if ((map = refcounted_strmap_take(h)) == NULL)
537 return -1;
538 values = map->values;
539
540 pos = git_strmap_lookup_index(values, key);
541
542 /* no error message; the config system will write one */
543 if (!git_strmap_valid_index(values, pos)) {
544 refcounted_strmap_free(map);
545 return GIT_ENOTFOUND;
546 }
547
548 var = git_strmap_value_at(values, pos);
549 while (var->next)
550 var = var->next;
551
552 *out = var->entry;
553 (*out)->free = release_map;
554 (*out)->payload = map;
555
556 return error;
557 }
558
559 static int config_set_multivar(
560 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
561 {
562 diskfile_backend *b = (diskfile_backend *)cfg;
563 char *key;
564 regex_t preg;
565 int result;
566
567 assert(regexp);
568
569 if ((result = git_config__normalize_name(name, &key)) < 0)
570 return result;
571
572 result = p_regcomp(&preg, regexp, REG_EXTENDED);
573 if (result != 0) {
574 giterr_set_regex(&preg, result);
575 result = -1;
576 goto out;
577 }
578
579 /* If we do have it, set call config_write() and reload */
580 if ((result = config_write(b, name, key, &preg, value)) < 0)
581 goto out;
582
583 result = config_refresh(cfg);
584
585 out:
586 git__free(key);
587 regfree(&preg);
588
589 return result;
590 }
591
592 static int config_delete(git_config_backend *cfg, const char *name)
593 {
594 cvar_t *var;
595 diskfile_backend *b = (diskfile_backend *)cfg;
596 refcounted_strmap *map; git_strmap *values;
597 char *key;
598 int result;
599 khiter_t pos;
600
601 if ((result = git_config__normalize_name(name, &key)) < 0)
602 return result;
603
604 if ((map = refcounted_strmap_take(&b->header)) == NULL)
605 return -1;
606 values = b->header.values->values;
607
608 pos = git_strmap_lookup_index(values, key);
609 git__free(key);
610
611 if (!git_strmap_valid_index(values, pos)) {
612 refcounted_strmap_free(map);
613 giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
614 return GIT_ENOTFOUND;
615 }
616
617 var = git_strmap_value_at(values, pos);
618 refcounted_strmap_free(map);
619
620 if (var->included) {
621 giterr_set(GITERR_CONFIG, "cannot delete included variable");
622 return -1;
623 }
624
625 if (var->next != NULL) {
626 giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete");
627 return -1;
628 }
629
630 if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
631 return result;
632
633 return config_refresh(cfg);
634 }
635
636 static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
637 {
638 diskfile_backend *b = (diskfile_backend *)cfg;
639 refcounted_strmap *map;
640 git_strmap *values;
641 char *key;
642 regex_t preg;
643 int result;
644 khiter_t pos;
645
646 if ((result = git_config__normalize_name(name, &key)) < 0)
647 return result;
648
649 if ((map = refcounted_strmap_take(&b->header)) == NULL)
650 return -1;
651 values = b->header.values->values;
652
653 pos = git_strmap_lookup_index(values, key);
654
655 if (!git_strmap_valid_index(values, pos)) {
656 refcounted_strmap_free(map);
657 git__free(key);
658 giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
659 return GIT_ENOTFOUND;
660 }
661
662 refcounted_strmap_free(map);
663
664 result = p_regcomp(&preg, regexp, REG_EXTENDED);
665 if (result != 0) {
666 giterr_set_regex(&preg, result);
667 result = -1;
668 goto out;
669 }
670
671 if ((result = config_write(b, name, key, &preg, NULL)) < 0)
672 goto out;
673
674 result = config_refresh(cfg);
675
676 out:
677 git__free(key);
678 regfree(&preg);
679 return result;
680 }
681
682 static int config_snapshot(git_config_backend **out, git_config_backend *in)
683 {
684 diskfile_backend *b = (diskfile_backend *) in;
685
686 return git_config_file__snapshot(out, b);
687 }
688
689 static int config_lock(git_config_backend *_cfg)
690 {
691 diskfile_backend *cfg = (diskfile_backend *) _cfg;
692 int error;
693
694 if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0)
695 return error;
696
697 error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path);
698 if (error < 0 && error != GIT_ENOTFOUND) {
699 git_filebuf_cleanup(&cfg->locked_buf);
700 return error;
701 }
702
703 cfg->locked = true;
704 return 0;
705
706 }
707
708 static int config_unlock(git_config_backend *_cfg, int success)
709 {
710 diskfile_backend *cfg = (diskfile_backend *) _cfg;
711 int error = 0;
712
713 if (success) {
714 git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
715 error = git_filebuf_commit(&cfg->locked_buf);
716 }
717
718 git_filebuf_cleanup(&cfg->locked_buf);
719 git_buf_free(&cfg->locked_content);
720 cfg->locked = false;
721
722 return error;
723 }
724
725 int git_config_file__ondisk(git_config_backend **out, const char *path)
726 {
727 diskfile_backend *backend;
728
729 backend = git__calloc(1, sizeof(diskfile_backend));
730 GITERR_CHECK_ALLOC(backend);
731
732 backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
733 git_mutex_init(&backend->header.values_mutex);
734
735 backend->file.path = git__strdup(path);
736 GITERR_CHECK_ALLOC(backend->file.path);
737 git_array_init(backend->file.includes);
738
739 backend->header.parent.open = config_open;
740 backend->header.parent.get = config_get;
741 backend->header.parent.set = config_set;
742 backend->header.parent.set_multivar = config_set_multivar;
743 backend->header.parent.del = config_delete;
744 backend->header.parent.del_multivar = config_delete_multivar;
745 backend->header.parent.iterator = config_iterator_new;
746 backend->header.parent.snapshot = config_snapshot;
747 backend->header.parent.lock = config_lock;
748 backend->header.parent.unlock = config_unlock;
749 backend->header.parent.free = backend_free;
750
751 *out = (git_config_backend *)backend;
752
753 return 0;
754 }
755
756 static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value)
757 {
758 GIT_UNUSED(cfg);
759 GIT_UNUSED(name);
760 GIT_UNUSED(value);
761
762 return config_error_readonly();
763 }
764
765 static int config_set_multivar_readonly(
766 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
767 {
768 GIT_UNUSED(cfg);
769 GIT_UNUSED(name);
770 GIT_UNUSED(regexp);
771 GIT_UNUSED(value);
772
773 return config_error_readonly();
774 }
775
776 static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp)
777 {
778 GIT_UNUSED(cfg);
779 GIT_UNUSED(name);
780 GIT_UNUSED(regexp);
781
782 return config_error_readonly();
783 }
784
785 static int config_delete_readonly(git_config_backend *cfg, const char *name)
786 {
787 GIT_UNUSED(cfg);
788 GIT_UNUSED(name);
789
790 return config_error_readonly();
791 }
792
793 static int config_lock_readonly(git_config_backend *_cfg)
794 {
795 GIT_UNUSED(_cfg);
796
797 return config_error_readonly();
798 }
799
800 static int config_unlock_readonly(git_config_backend *_cfg, int success)
801 {
802 GIT_UNUSED(_cfg);
803 GIT_UNUSED(success);
804
805 return config_error_readonly();
806 }
807
808 static void backend_readonly_free(git_config_backend *_backend)
809 {
810 diskfile_backend *backend = (diskfile_backend *)_backend;
811
812 if (backend == NULL)
813 return;
814
815 refcounted_strmap_free(backend->header.values);
816 git_mutex_free(&backend->header.values_mutex);
817 git__free(backend);
818 }
819
820 static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
821 {
822 diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
823 diskfile_backend *src = b->snapshot_from;
824 diskfile_header *src_header = &src->header;
825 refcounted_strmap *src_map;
826 int error;
827
828 if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
829 return error;
830
831 /* We're just copying data, don't care about the level or repo*/
832 GIT_UNUSED(level);
833 GIT_UNUSED(repo);
834
835 if ((src_map = refcounted_strmap_take(src_header)) == NULL)
836 return -1;
837 b->header.values = src_map;
838
839 return 0;
840 }
841
842 int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
843 {
844 diskfile_readonly_backend *backend;
845
846 backend = git__calloc(1, sizeof(diskfile_readonly_backend));
847 GITERR_CHECK_ALLOC(backend);
848
849 backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
850 git_mutex_init(&backend->header.values_mutex);
851
852 backend->snapshot_from = in;
853
854 backend->header.parent.readonly = 1;
855 backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
856 backend->header.parent.open = config_readonly_open;
857 backend->header.parent.get = config_get;
858 backend->header.parent.set = config_set_readonly;
859 backend->header.parent.set_multivar = config_set_multivar_readonly;
860 backend->header.parent.del = config_delete_readonly;
861 backend->header.parent.del_multivar = config_delete_multivar_readonly;
862 backend->header.parent.iterator = config_iterator_new;
863 backend->header.parent.lock = config_lock_readonly;
864 backend->header.parent.unlock = config_unlock_readonly;
865 backend->header.parent.free = backend_readonly_free;
866
867 *out = (git_config_backend *)backend;
868
869 return 0;
870 }
871
872 static int included_path(git_buf *out, const char *dir, const char *path)
873 {
874 /* From the user's home */
875 if (path[0] == '~' && path[1] == '/')
876 return git_sysdir_expand_global_file(out, &path[1]);
877
878 return git_path_join_unrooted(out, path, dir, NULL);
879 }
880
881 /* Escape the values to write them to the file */
882 static char *escape_value(const char *ptr)
883 {
884 git_buf buf;
885 size_t len;
886 const char *esc;
887
888 assert(ptr);
889
890 len = strlen(ptr);
891 if (!len)
892 return git__calloc(1, sizeof(char));
893
894 if (git_buf_init(&buf, len) < 0)
895 return NULL;
896
897 while (*ptr != '\0') {
898 if ((esc = strchr(git_config_escaped, *ptr)) != NULL) {
899 git_buf_putc(&buf, '\\');
900 git_buf_putc(&buf, git_config_escapes[esc - git_config_escaped]);
901 } else {
902 git_buf_putc(&buf, *ptr);
903 }
904 ptr++;
905 }
906
907 if (git_buf_oom(&buf)) {
908 git_buf_free(&buf);
909 return NULL;
910 }
911
912 return git_buf_detach(&buf);
913 }
914
915 struct parse_data {
916 const git_repository *repo;
917 const char *file_path;
918 git_strmap *values;
919 git_config_level_t level;
920 int depth;
921 };
922
923 static int parse_include(git_config_parser *reader,
924 struct parse_data *parse_data, const char *file)
925 {
926 struct config_file *include;
927 git_buf path = GIT_BUF_INIT;
928 char *dir;
929 int result;
930
931 if (!file)
932 return 0;
933
934 if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
935 return result;
936
937 dir = git_buf_detach(&path);
938 result = included_path(&path, dir, file);
939 git__free(dir);
940
941 if (result < 0)
942 return result;
943
944 include = git_array_alloc(reader->file->includes);
945 memset(include, 0, sizeof(*include));
946 git_array_init(include->includes);
947 include->path = git_buf_detach(&path);
948
949 result = config_read(parse_data->values, parse_data->repo,
950 include, parse_data->level, parse_data->depth+1);
951
952 if (result == GIT_ENOTFOUND) {
953 giterr_clear();
954 result = 0;
955 }
956
957 return result;
958 }
959
960 static int do_match_gitdir(
961 int *matches,
962 const git_repository *repo,
963 const char *cfg_file,
964 const char *value,
965 bool case_insensitive)
966 {
967 git_buf path = GIT_BUF_INIT;
968 int error, fnmatch_flags;
969
970 if (value[0] == '.' && git_path_is_dirsep(value[1])) {
971 git_path_dirname_r(&path, cfg_file);
972 git_buf_joinpath(&path, path.ptr, value + 2);
973 } else if (value[0] == '~' && git_path_is_dirsep(value[1]))
974 git_sysdir_expand_global_file(&path, value + 1);
975 else if (!git_path_is_absolute(value))
976 git_buf_joinpath(&path, "**", value);
977 else
978 git_buf_sets(&path, value);
979
980 if (git_buf_oom(&path)) {
981 error = -1;
982 goto out;
983 }
984
985 if (git_path_is_dirsep(value[strlen(value) - 1]))
986 git_buf_puts(&path, "**");
987
988 fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
989 if (case_insensitive)
990 fnmatch_flags |= FNM_IGNORECASE;
991
992 if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
993 goto out;
994
995 *matches = (error == 0);
996
997 out:
998 git_buf_free(&path);
999 return error;
1000 }
1001
1002 static int conditional_match_gitdir(
1003 int *matches,
1004 const git_repository *repo,
1005 const char *cfg_file,
1006 const char *value)
1007 {
1008 return do_match_gitdir(matches, repo, cfg_file, value, false);
1009 }
1010
1011 static int conditional_match_gitdir_i(
1012 int *matches,
1013 const git_repository *repo,
1014 const char *cfg_file,
1015 const char *value)
1016 {
1017 return do_match_gitdir(matches, repo, cfg_file, value, true);
1018 }
1019
1020 static const struct {
1021 const char *prefix;
1022 int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
1023 } conditions[] = {
1024 { "gitdir:", conditional_match_gitdir },
1025 { "gitdir/i:", conditional_match_gitdir_i }
1026 };
1027
1028 static int parse_conditional_include(git_config_parser *reader,
1029 struct parse_data *parse_data, const char *section, const char *file)
1030 {
1031 char *condition;
1032 size_t i;
1033 int error = 0, matches;
1034
1035 if (!parse_data->repo || !file)
1036 return 0;
1037
1038 condition = git__substrdup(section + strlen("includeIf."),
1039 strlen(section) - strlen("includeIf.") - strlen(".path"));
1040
1041 for (i = 0; i < ARRAY_SIZE(conditions); i++) {
1042 if (git__prefixcmp(condition, conditions[i].prefix))
1043 continue;
1044
1045 if ((error = conditions[i].matches(&matches,
1046 parse_data->repo,
1047 parse_data->file_path,
1048 condition + strlen(conditions[i].prefix))) < 0)
1049 break;
1050
1051 if (matches)
1052 error = parse_include(reader, parse_data, file);
1053
1054 break;
1055 }
1056
1057 git__free(condition);
1058 return error;
1059 }
1060
1061 static int read_on_variable(
1062 git_config_parser *reader,
1063 const char *current_section,
1064 char *var_name,
1065 char *var_value,
1066 const char *line,
1067 size_t line_len,
1068 void *data)
1069 {
1070 struct parse_data *parse_data = (struct parse_data *)data;
1071 git_buf buf = GIT_BUF_INIT;
1072 cvar_t *var;
1073 int result = 0;
1074
1075 GIT_UNUSED(line);
1076 GIT_UNUSED(line_len);
1077
1078 if (current_section) {
1079 /* TODO: Once warnings lang, we should likely warn
1080 * here. Git appears to warn in most cases if it sees
1081 * un-namespaced config options.
1082 */
1083 git_buf_puts(&buf, current_section);
1084 git_buf_putc(&buf, '.');
1085 }
1086 git__strtolower(var_name);
1087 git_buf_puts(&buf, var_name);
1088 git__free(var_name);
1089
1090 if (git_buf_oom(&buf)) {
1091 git__free(var_value);
1092 return -1;
1093 }
1094
1095 var = git__calloc(1, sizeof(cvar_t));
1096 GITERR_CHECK_ALLOC(var);
1097 var->entry = git__calloc(1, sizeof(git_config_entry));
1098 GITERR_CHECK_ALLOC(var->entry);
1099
1100 var->entry->name = git_buf_detach(&buf);
1101 var->entry->value = var_value;
1102 var->entry->level = parse_data->level;
1103 var->included = !!parse_data->depth;
1104
1105 if ((result = append_entry(parse_data->values, var)) < 0)
1106 return result;
1107
1108 result = 0;
1109
1110 /* Add or append the new config option */
1111 if (!git__strcmp(var->entry->name, "include.path"))
1112 result = parse_include(reader, parse_data, var->entry->value);
1113 else if (!git__prefixcmp(var->entry->name, "includeif.") &&
1114 !git__suffixcmp(var->entry->name, ".path"))
1115 result = parse_conditional_include(reader, parse_data,
1116 var->entry->name, var->entry->value);
1117
1118
1119 return result;
1120 }
1121
1122 static int config_read(
1123 git_strmap *values,
1124 const git_repository *repo,
1125 git_config_file *file,
1126 git_config_level_t level,
1127 int depth)
1128 {
1129 struct parse_data parse_data;
1130 git_config_parser reader;
1131 git_buf contents = GIT_BUF_INIT;
1132 int error;
1133
1134 if (depth >= MAX_INCLUDE_DEPTH) {
1135 giterr_set(GITERR_CONFIG, "maximum config include depth reached");
1136 return -1;
1137 }
1138
1139 if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
1140 goto out;
1141
1142 git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
1143
1144 if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
1145 goto out;
1146
1147 /* Initialize the reading position */
1148 reader.file = file;
1149 git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
1150
1151 /* If the file is empty, there's nothing for us to do */
1152 if (!reader.ctx.content || *reader.ctx.content == '\0')
1153 goto out;
1154
1155 parse_data.repo = repo;
1156 parse_data.file_path = file->path;
1157 parse_data.values = values;
1158 parse_data.level = level;
1159 parse_data.depth = depth;
1160
1161 error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
1162
1163 out:
1164 git_buf_free(&contents);
1165 return error;
1166 }
1167
1168 static int write_section(git_buf *fbuf, const char *key)
1169 {
1170 int result;
1171 const char *dot;
1172 git_buf buf = GIT_BUF_INIT;
1173
1174 /* All of this just for [section "subsection"] */
1175 dot = strchr(key, '.');
1176 git_buf_putc(&buf, '[');
1177 if (dot == NULL) {
1178 git_buf_puts(&buf, key);
1179 } else {
1180 char *escaped;
1181 git_buf_put(&buf, key, dot - key);
1182 escaped = escape_value(dot + 1);
1183 GITERR_CHECK_ALLOC(escaped);
1184 git_buf_printf(&buf, " \"%s\"", escaped);
1185 git__free(escaped);
1186 }
1187 git_buf_puts(&buf, "]\n");
1188
1189 if (git_buf_oom(&buf))
1190 return -1;
1191
1192 result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size);
1193 git_buf_free(&buf);
1194
1195 return result;
1196 }
1197
1198 static const char *quotes_for_value(const char *value)
1199 {
1200 const char *ptr;
1201
1202 if (value[0] == ' ' || value[0] == '\0')
1203 return "\"";
1204
1205 for (ptr = value; *ptr; ++ptr) {
1206 if (*ptr == ';' || *ptr == '#')
1207 return "\"";
1208 }
1209
1210 if (ptr[-1] == ' ')
1211 return "\"";
1212
1213 return "";
1214 }
1215
1216 struct write_data {
1217 git_buf *buf;
1218 git_buf buffered_comment;
1219 unsigned int in_section : 1,
1220 preg_replaced : 1;
1221 const char *orig_section;
1222 const char *section;
1223 const char *orig_name;
1224 const char *name;
1225 const regex_t *preg;
1226 const char *value;
1227 };
1228
1229 static int write_line_to(git_buf *buf, const char *line, size_t line_len)
1230 {
1231 int result = git_buf_put(buf, line, line_len);
1232
1233 if (!result && line_len && line[line_len-1] != '\n')
1234 result = git_buf_printf(buf, "\n");
1235
1236 return result;
1237 }
1238
1239 static int write_line(struct write_data *write_data, const char *line, size_t line_len)
1240 {
1241 return write_line_to(write_data->buf, line, line_len);
1242 }
1243
1244 static int write_value(struct write_data *write_data)
1245 {
1246 const char *q;
1247 int result;
1248
1249 q = quotes_for_value(write_data->value);
1250 result = git_buf_printf(write_data->buf,
1251 "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
1252
1253 /* If we are updating a single name/value, we're done. Setting `value`
1254 * to `NULL` will prevent us from trying to write it again later (in
1255 * `write_on_section`) if we see the same section repeated.
1256 */
1257 if (!write_data->preg)
1258 write_data->value = NULL;
1259
1260 return result;
1261 }
1262
1263 static int write_on_section(
1264 git_config_parser *reader,
1265 const char *current_section,
1266 const char *line,
1267 size_t line_len,
1268 void *data)
1269 {
1270 struct write_data *write_data = (struct write_data *)data;
1271 int result = 0;
1272
1273 GIT_UNUSED(reader);
1274
1275 /* If we were previously in the correct section (but aren't anymore)
1276 * and haven't written our value (for a simple name/value set, not
1277 * a multivar), then append it to the end of the section before writing
1278 * the new one.
1279 */
1280 if (write_data->in_section && !write_data->preg && write_data->value)
1281 result = write_value(write_data);
1282
1283 write_data->in_section = strcmp(current_section, write_data->section) == 0;
1284
1285 /*
1286 * If there were comments just before this section, dump them as well.
1287 */
1288 if (!result) {
1289 result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
1290 git_buf_clear(&write_data->buffered_comment);
1291 }
1292
1293 if (!result)
1294 result = write_line(write_data, line, line_len);
1295
1296 return result;
1297 }
1298
1299 static int write_on_variable(
1300 git_config_parser *reader,
1301 const char *current_section,
1302 char *var_name,
1303 char *var_value,
1304 const char *line,
1305 size_t line_len,
1306 void *data)
1307 {
1308 struct write_data *write_data = (struct write_data *)data;
1309 bool has_matched = false;
1310 int error;
1311
1312 GIT_UNUSED(reader);
1313 GIT_UNUSED(current_section);
1314
1315 /*
1316 * If there were comments just before this variable, let's dump them as well.
1317 */
1318 if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
1319 return error;
1320
1321 git_buf_clear(&write_data->buffered_comment);
1322
1323 /* See if we are to update this name/value pair; first examine name */
1324 if (write_data->in_section &&
1325 strcasecmp(write_data->name, var_name) == 0)
1326 has_matched = true;
1327
1328 /* If we have a regex to match the value, see if it matches */
1329 if (has_matched && write_data->preg != NULL)
1330 has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
1331
1332 git__free(var_name);
1333 git__free(var_value);
1334
1335 /* If this isn't the name/value we're looking for, simply dump the
1336 * existing data back out and continue on.
1337 */
1338 if (!has_matched)
1339 return write_line(write_data, line, line_len);
1340
1341 write_data->preg_replaced = 1;
1342
1343 /* If value is NULL, we are deleting this value; write nothing. */
1344 if (!write_data->value)
1345 return 0;
1346
1347 return write_value(write_data);
1348 }
1349
1350 static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data)
1351 {
1352 struct write_data *write_data;
1353
1354 GIT_UNUSED(reader);
1355
1356 write_data = (struct write_data *)data;
1357 return write_line_to(&write_data->buffered_comment, line, line_len);
1358 }
1359
1360 static int write_on_eof(
1361 git_config_parser *reader, const char *current_section, void *data)
1362 {
1363 struct write_data *write_data = (struct write_data *)data;
1364 int result = 0;
1365
1366 GIT_UNUSED(reader);
1367
1368 /*
1369 * If we've buffered comments when reaching EOF, make sure to dump them.
1370 */
1371 if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
1372 return result;
1373
1374 /* If we are at the EOF and have not written our value (again, for a
1375 * simple name/value set, not a multivar) then we have never seen the
1376 * section in question and should create a new section and write the
1377 * value.
1378 */
1379 if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
1380 /* write the section header unless we're already in it */
1381 if (!current_section || strcmp(current_section, write_data->section))
1382 result = write_section(write_data->buf, write_data->orig_section);
1383
1384 if (!result)
1385 result = write_value(write_data);
1386 }
1387
1388 return result;
1389 }
1390
1391 /*
1392 * This is pretty much the parsing, except we write out anything we don't have
1393 */
1394 static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
1395 {
1396 int result;
1397 char *orig_section, *section, *orig_name, *name, *ldot;
1398 git_filebuf file = GIT_FILEBUF_INIT;
1399 git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
1400 git_config_parser reader;
1401 struct write_data write_data;
1402
1403 memset(&reader, 0, sizeof(reader));
1404 reader.file = &cfg->file;
1405
1406 if (cfg->locked) {
1407 result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content));
1408 } else {
1409 /* Lock the file */
1410 if ((result = git_filebuf_open(
1411 &file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
1412 git_buf_free(&contents);
1413 return result;
1414 }
1415
1416 /* We need to read in our own config file */
1417 result = git_futils_readbuffer(&contents, cfg->file.path);
1418 }
1419
1420 /* Initialise the reading position */
1421 if (result == 0 || result == GIT_ENOTFOUND) {
1422 git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
1423 } else {
1424 git_filebuf_cleanup(&file);
1425 return -1; /* OS error when reading the file */
1426 }
1427
1428 ldot = strrchr(key, '.');
1429 name = ldot + 1;
1430 section = git__strndup(key, ldot - key);
1431 GITERR_CHECK_ALLOC(section);
1432
1433 ldot = strrchr(orig_key, '.');
1434 orig_name = ldot + 1;
1435 orig_section = git__strndup(orig_key, ldot - orig_key);
1436 GITERR_CHECK_ALLOC(orig_section);
1437
1438 write_data.buf = &buf;
1439 git_buf_init(&write_data.buffered_comment, 0);
1440 write_data.orig_section = orig_section;
1441 write_data.section = section;
1442 write_data.in_section = 0;
1443 write_data.preg_replaced = 0;
1444 write_data.orig_name = orig_name;
1445 write_data.name = name;
1446 write_data.preg = preg;
1447 write_data.value = value;
1448
1449 result = git_config_parse(&reader,
1450 write_on_section,
1451 write_on_variable,
1452 write_on_comment,
1453 write_on_eof,
1454 &write_data);
1455 git__free(section);
1456 git__free(orig_section);
1457 git_buf_free(&write_data.buffered_comment);
1458
1459 if (result < 0) {
1460 git_filebuf_cleanup(&file);
1461 goto done;
1462 }
1463
1464 if (cfg->locked) {
1465 size_t len = buf.asize;
1466 /* Update our copy with the modified contents */
1467 git_buf_free(&cfg->locked_content);
1468 git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
1469 } else {
1470 git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
1471 result = git_filebuf_commit(&file);
1472 }
1473
1474 done:
1475 git_buf_free(&buf);
1476 git_buf_free(&contents);
1477 git_parse_ctx_clear(&reader.ctx);
1478 return result;
1479 }