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_backend.h"
9 
10 #include "config.h"
11 #include "config_entries.h"
12 
13 typedef struct {
14 	git_config_backend parent;
15 	git_mutex values_mutex;
16 	git_config_entries *entries;
17 	git_config_backend *source;
18 } config_snapshot_backend;
19 
config_error_readonly(void)20 static int config_error_readonly(void)
21 {
22 	git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
23 	return -1;
24 }
25 
config_snapshot_iterator(git_config_iterator ** iter,struct git_config_backend * backend)26 static int config_snapshot_iterator(
27 	git_config_iterator **iter,
28 	struct git_config_backend *backend)
29 {
30 	config_snapshot_backend *b = GIT_CONTAINER_OF(backend, config_snapshot_backend, parent);
31 	git_config_entries *entries = NULL;
32 	int error;
33 
34 	if ((error = git_config_entries_dup(&entries, b->entries)) < 0 ||
35 	    (error = git_config_entries_iterator_new(iter, entries)) < 0)
36 		goto out;
37 
38 out:
39 	/* Let iterator delete duplicated entries when it's done */
40 	git_config_entries_free(entries);
41 	return error;
42 }
43 
44 /* release the map containing the entry as an equivalent to freeing it */
config_snapshot_entry_free(git_config_entry * entry)45 static void config_snapshot_entry_free(git_config_entry *entry)
46 {
47 	git_config_entries *entries = (git_config_entries *) entry->payload;
48 	git_config_entries_free(entries);
49 }
50 
config_snapshot_get(git_config_backend * cfg,const char * key,git_config_entry ** out)51 static int config_snapshot_get(git_config_backend *cfg, const char *key, git_config_entry **out)
52 {
53 	config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
54 	git_config_entries *entries = NULL;
55 	git_config_entry *entry;
56 	int error = 0;
57 
58 	if (git_mutex_lock(&b->values_mutex) < 0) {
59 	    git_error_set(GIT_ERROR_OS, "failed to lock config backend");
60 	    return -1;
61 	}
62 
63 	entries = b->entries;
64 	git_config_entries_incref(entries);
65 	git_mutex_unlock(&b->values_mutex);
66 
67 	if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
68 		git_config_entries_free(entries);
69 		return error;
70 	}
71 
72 	entry->free = config_snapshot_entry_free;
73 	entry->payload = entries;
74 	*out = entry;
75 
76 	return 0;
77 }
78 
config_snapshot_set(git_config_backend * cfg,const char * name,const char * value)79 static int config_snapshot_set(git_config_backend *cfg, const char *name, const char *value)
80 {
81 	GIT_UNUSED(cfg);
82 	GIT_UNUSED(name);
83 	GIT_UNUSED(value);
84 
85 	return config_error_readonly();
86 }
87 
config_snapshot_set_multivar(git_config_backend * cfg,const char * name,const char * regexp,const char * value)88 static int config_snapshot_set_multivar(
89 	git_config_backend *cfg, const char *name, const char *regexp, const char *value)
90 {
91 	GIT_UNUSED(cfg);
92 	GIT_UNUSED(name);
93 	GIT_UNUSED(regexp);
94 	GIT_UNUSED(value);
95 
96 	return config_error_readonly();
97 }
98 
config_snapshot_delete_multivar(git_config_backend * cfg,const char * name,const char * regexp)99 static int config_snapshot_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
100 {
101 	GIT_UNUSED(cfg);
102 	GIT_UNUSED(name);
103 	GIT_UNUSED(regexp);
104 
105 	return config_error_readonly();
106 }
107 
config_snapshot_delete(git_config_backend * cfg,const char * name)108 static int config_snapshot_delete(git_config_backend *cfg, const char *name)
109 {
110 	GIT_UNUSED(cfg);
111 	GIT_UNUSED(name);
112 
113 	return config_error_readonly();
114 }
115 
config_snapshot_lock(git_config_backend * _cfg)116 static int config_snapshot_lock(git_config_backend *_cfg)
117 {
118 	GIT_UNUSED(_cfg);
119 
120 	return config_error_readonly();
121 }
122 
config_snapshot_unlock(git_config_backend * _cfg,int success)123 static int config_snapshot_unlock(git_config_backend *_cfg, int success)
124 {
125 	GIT_UNUSED(_cfg);
126 	GIT_UNUSED(success);
127 
128 	return config_error_readonly();
129 }
130 
config_snapshot_free(git_config_backend * _backend)131 static void config_snapshot_free(git_config_backend *_backend)
132 {
133 	config_snapshot_backend *backend = GIT_CONTAINER_OF(_backend, config_snapshot_backend, parent);
134 
135 	if (backend == NULL)
136 		return;
137 
138 	git_config_entries_free(backend->entries);
139 	git_mutex_free(&backend->values_mutex);
140 	git__free(backend);
141 }
142 
config_snapshot_open(git_config_backend * cfg,git_config_level_t level,const git_repository * repo)143 static int config_snapshot_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
144 {
145 	config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
146 	git_config_entries *entries = NULL;
147 	git_config_iterator *it = NULL;
148 	git_config_entry *entry;
149 	int error;
150 
151 	/* We're just copying data, don't care about the level or repo*/
152 	GIT_UNUSED(level);
153 	GIT_UNUSED(repo);
154 
155 	if ((error = git_config_entries_new(&entries)) < 0 ||
156 	    (error = b->source->iterator(&it, b->source)) < 0)
157 		goto out;
158 
159 	while ((error = git_config_next(&entry, it)) == 0)
160 		if ((error = git_config_entries_dup_entry(entries, entry)) < 0)
161 			goto out;
162 
163 	if (error < 0) {
164 		if (error != GIT_ITEROVER)
165 			goto out;
166 		error = 0;
167 	}
168 
169 	b->entries = entries;
170 
171 out:
172 	git_config_iterator_free(it);
173 	if (error)
174 		git_config_entries_free(entries);
175 	return error;
176 }
177 
git_config_backend_snapshot(git_config_backend ** out,git_config_backend * source)178 int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source)
179 {
180 	config_snapshot_backend *backend;
181 
182 	backend = git__calloc(1, sizeof(config_snapshot_backend));
183 	GIT_ERROR_CHECK_ALLOC(backend);
184 
185 	backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
186 	git_mutex_init(&backend->values_mutex);
187 
188 	backend->source = source;
189 
190 	backend->parent.readonly = 1;
191 	backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
192 	backend->parent.open = config_snapshot_open;
193 	backend->parent.get = config_snapshot_get;
194 	backend->parent.set = config_snapshot_set;
195 	backend->parent.set_multivar = config_snapshot_set_multivar;
196 	backend->parent.snapshot = git_config_backend_snapshot;
197 	backend->parent.del = config_snapshot_delete;
198 	backend->parent.del_multivar = config_snapshot_delete_multivar;
199 	backend->parent.iterator = config_snapshot_iterator;
200 	backend->parent.lock = config_snapshot_lock;
201 	backend->parent.unlock = config_snapshot_unlock;
202 	backend->parent.free = config_snapshot_free;
203 
204 	*out = &backend->parent;
205 
206 	return 0;
207 }
208