diff options
author | Patrick Steinhardt <ps@pks.im> | 2019-08-01 11:29:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-01 11:29:58 +0200 |
commit | e8f6341105c7221e1358a5f6fda23e57c17eadc5 (patch) | |
tree | 457178e6e6c5e42f3eecfc05cb9e6769afbcf790 | |
parent | f039c836438f96407abc08cbd238b9102318deb2 (diff) | |
parent | 37ebe9ad869b8608055428f081be423ab020be35 (diff) | |
download | libgit2-e8f6341105c7221e1358a5f6fda23e57c17eadc5.tar.gz |
Merge pull request #5186 from pks-t/pks/config-snapshot-separation
config: separate file and snapshot backends
-rw-r--r-- | src/config_backend.h | 12 | ||||
-rw-r--r-- | src/config_entries.c | 47 | ||||
-rw-r--r-- | src/config_entries.h | 1 | ||||
-rw-r--r-- | src/config_file.c | 305 | ||||
-rw-r--r-- | src/config_snapshot.c | 205 |
5 files changed, 337 insertions, 233 deletions
diff --git a/src/config_backend.h b/src/config_backend.h index 6d678ae43..dbb190514 100644 --- a/src/config_backend.h +++ b/src/config_backend.h @@ -26,6 +26,18 @@ extern int git_config_backend_from_file(git_config_backend **out, const char *path); /** + * Create a readonly configuration file backend from another backend + * + * This copies the complete contents of the source backend to the + * new backend. The new backend will be completely read-only and + * cannot be modified. + * + * @param out the new snapshotted backend + * @param source the backend to copy + */ +extern int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source); + +/** * Create an in-memory configuration file backend * * @param out the new backend diff --git a/src/config_entries.c b/src/config_entries.c index 18f8b21ff..2eb9e666c 100644 --- a/src/config_entries.c +++ b/src/config_entries.c @@ -67,6 +67,36 @@ int git_config_entries_new(git_config_entries **out) return error; } +int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry) +{ + git_config_entry *duplicated; + int error; + + duplicated = git__calloc(1, sizeof(git_config_entry)); + GIT_ERROR_CHECK_ALLOC(duplicated); + + duplicated->name = git__strdup(entry->name); + GIT_ERROR_CHECK_ALLOC(duplicated->name); + + if (entry->value) { + duplicated->value = git__strdup(entry->value); + GIT_ERROR_CHECK_ALLOC(duplicated->value); + } + duplicated->level = entry->level; + duplicated->include_depth = entry->include_depth; + + if ((error = git_config_entries_append(entries, duplicated)) < 0) + goto out; + +out: + if (error && duplicated) { + git__free((char *) duplicated->name); + git__free((char *) duplicated->value); + git__free(duplicated); + } + return error; +} + int git_config_entries_dup(git_config_entries **out, git_config_entries *entries) { git_config_entries *result = NULL; @@ -76,22 +106,9 @@ int git_config_entries_dup(git_config_entries **out, git_config_entries *entries if ((error = git_config_entries_new(&result)) < 0) goto out; - for (head = entries->list; head; head = head->next) { - git_config_entry *dup; - - dup = git__calloc(1, sizeof(git_config_entry)); - dup->name = git__strdup(head->entry->name); - GIT_ERROR_CHECK_ALLOC(dup->name); - if (head->entry->value) { - dup->value = git__strdup(head->entry->value); - GIT_ERROR_CHECK_ALLOC(dup->value); - } - dup->level = head->entry->level; - dup->include_depth = head->entry->include_depth; - - if ((error = git_config_entries_append(result, dup)) < 0) + for (head = entries->list; head; head = head->next) + if ((git_config_entries_dup_entry(result, head->entry)) < 0) goto out; - } *out = result; result = NULL; diff --git a/src/config_entries.h b/src/config_entries.h index 6fdbc41ba..832379e74 100644 --- a/src/config_entries.h +++ b/src/config_entries.h @@ -14,6 +14,7 @@ typedef struct git_config_entries git_config_entries; int git_config_entries_new(git_config_entries **out); int git_config_entries_dup(git_config_entries **out, git_config_entries *entries); +int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry); void git_config_entries_incref(git_config_entries *entries); void git_config_entries_free(git_config_entries *entries); /* Add or append the new config option */ diff --git a/src/config_file.c b/src/config_file.c index 88bba90a7..3e8e30cbd 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -7,17 +7,19 @@ #include "config.h" -#include "filebuf.h" -#include "sysdir.h" -#include "buffer.h" -#include "buf_text.h" #include "git2/config.h" #include "git2/sys/config.h" #include "git2/types.h" -#include "strmap.h" + #include "array.h" -#include "config_parse.h" +#include "buf_text.h" +#include "buffer.h" +#include "config_backend.h" #include "config_entries.h" +#include "config_parse.h" +#include "filebuf.h" +#include "strmap.h" +#include "sysdir.h" #include "wildmatch.h" #include <ctype.h> @@ -26,24 +28,19 @@ /* Max depth for [include] directives */ #define MAX_INCLUDE_DEPTH 10 -typedef struct diskfile { +typedef struct config_file { git_futils_filestamp stamp; git_oid checksum; char *path; - git_array_t(struct diskfile) includes; -} diskfile; + git_array_t(struct config_file) includes; +} config_file; typedef struct { git_config_backend parent; - /* mutex to coordinate accessing the values */ git_mutex values_mutex; git_config_entries *entries; const git_repository *repo; git_config_level_t level; -} diskfile_header; - -typedef struct { - diskfile_header header; git_array_t(git_config_parser) readers; @@ -51,61 +48,47 @@ typedef struct { git_filebuf locked_buf; git_buf locked_content; - diskfile file; -} diskfile_backend; - -typedef struct { - diskfile_header header; - - diskfile_backend *snapshot_from; -} diskfile_readonly_backend; + config_file file; +} config_file_backend; typedef struct { const git_repository *repo; - diskfile *file; + config_file *file; git_config_entries *entries; git_config_level_t level; unsigned int depth; -} diskfile_parse_state; +} config_file_parse_data; -static int config_read(git_config_entries *entries, const git_repository *repo, diskfile *file, git_config_level_t level, int depth); -static int config_read_buffer(git_config_entries *entries, const git_repository *repo, diskfile *file, git_config_level_t level, int depth, const char *buf, size_t buflen); -static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char *value); +static int config_read(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth); +static int config_read_buffer(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen); +static int config_write(config_file_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char *value); static char *escape_value(const char *ptr); -static int config_snapshot(git_config_backend **out, git_config_backend *in); - -static int config_error_readonly(void) -{ - git_error_set(GIT_ERROR_CONFIG, "this backend is read-only"); - return -1; -} - /** * Take the current values map from the backend and increase its * refcount. This is its own function to make sure we use the mutex to * avoid the map pointer from changing under us. */ -static git_config_entries *diskfile_entries_take(diskfile_header *h) +static git_config_entries *diskfile_entries_take(config_file_backend *b) { git_config_entries *entries; - if (git_mutex_lock(&h->values_mutex) < 0) { + if (git_mutex_lock(&b->values_mutex) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock config backend"); return NULL; } - entries = h->entries; + entries = b->entries; git_config_entries_incref(entries); - git_mutex_unlock(&h->values_mutex); + git_mutex_unlock(&b->values_mutex); return entries; } -static void config_file_clear(diskfile *file) +static void config_file_clear(config_file *file) { - diskfile *include; + config_file *include; uint32_t i; if (file == NULL) @@ -121,29 +104,29 @@ static void config_file_clear(diskfile *file) static int config_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); int res; - b->header.level = level; - b->header.repo = repo; + b->level = level; + b->repo = repo; - if ((res = git_config_entries_new(&b->header.entries)) < 0) + if ((res = git_config_entries_new(&b->entries)) < 0) return res; if (!git_path_exists(b->file.path)) return 0; - if (res < 0 || (res = config_read(b->header.entries, repo, &b->file, level, 0)) < 0) { - git_config_entries_free(b->header.entries); - b->header.entries = NULL; + if (res < 0 || (res = config_read(b->entries, repo, &b->file, level, 0)) < 0) { + git_config_entries_free(b->entries); + b->entries = NULL; } return res; } -static int config_is_modified(int *modified, diskfile *file) +static int config_is_modified(int *modified, config_file *file) { - diskfile *include; + config_file *include; git_buf buf = GIT_BUF_INIT; git_oid hash; uint32_t i; @@ -179,28 +162,30 @@ out: static int config_set_entries(git_config_backend *cfg, git_config_entries *entries) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *old = NULL; - diskfile *include; + config_file *include; int error; uint32_t i; - if (b->header.parent.readonly) - return config_error_readonly(); + if (b->parent.readonly) { + git_error_set(GIT_ERROR_CONFIG, "this backend is read-only"); + return -1; + } git_array_foreach(b->file.includes, i, include) config_file_clear(include); git_array_clear(b->file.includes); - if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) { + if ((error = git_mutex_lock(&b->values_mutex)) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock config backend"); goto out; } - old = b->header.entries; - b->header.entries = entries; + old = b->entries; + b->entries = entries; - git_mutex_unlock(&b->header.values_mutex); + git_mutex_unlock(&b->values_mutex); out: git_config_entries_free(old); @@ -209,13 +194,13 @@ out: static int config_refresh_from_buffer(git_config_backend *cfg, const char *buf, size_t buflen) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; int error; if ((error = git_config_entries_new(&entries)) < 0 || - (error = config_read_buffer(entries, b->header.repo, &b->file, - b->header.level, 0, buf, buflen)) < 0 || + (error = config_read_buffer(entries, b->repo, &b->file, + b->level, 0, buf, buflen)) < 0 || (error = config_set_entries(cfg, entries)) < 0) goto out; @@ -227,7 +212,7 @@ out: static int config_refresh(git_config_backend *cfg) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; int error, modified; @@ -241,7 +226,7 @@ static int config_refresh(git_config_backend *cfg) return 0; if ((error = git_config_entries_new(&entries)) < 0 || - (error = config_read(entries, b->header.repo, &b->file, b->header.level, 0)) < 0 || + (error = config_read(entries, b->repo, &b->file, b->level, 0)) < 0 || (error = config_set_entries(cfg, entries)) < 0) goto out; @@ -254,14 +239,14 @@ out: static void backend_free(git_config_backend *_backend) { - diskfile_backend *backend = GIT_CONTAINER_OF(_backend, diskfile_backend, header.parent); + config_file_backend *backend = GIT_CONTAINER_OF(_backend, config_file_backend, parent); if (backend == NULL) return; config_file_clear(&backend->file); - git_config_entries_free(backend->header.entries); - git_mutex_free(&backend->header.values_mutex); + git_config_entries_free(backend->entries); + git_mutex_free(&backend->values_mutex); git__free(backend); } @@ -269,12 +254,12 @@ static int config_iterator_new( git_config_iterator **iter, struct git_config_backend *backend) { - diskfile_header *bh = GIT_CONTAINER_OF(backend, diskfile_header, parent); + config_file_backend *b = GIT_CONTAINER_OF(backend, config_file_backend, parent); git_config_entries *entries = NULL; int error; if ((error = config_refresh(backend)) < 0 || - (error = git_config_entries_dup(&entries, bh->entries)) < 0 || + (error = git_config_entries_dup(&entries, b->entries)) < 0 || (error = git_config_entries_iterator_new(iter, entries)) < 0) goto out; @@ -286,7 +271,7 @@ out: static int config_set(git_config_backend *cfg, const char *name, const char *value) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries; git_config_entry *existing; char *key, *esc_value = NULL; @@ -295,7 +280,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val if ((error = git_config__normalize_name(name, &key)) < 0) return error; - if ((entries = diskfile_entries_take(&b->header)) == NULL) + if ((entries = diskfile_entries_take(b)) == NULL) return -1; /* Check whether we'd be modifying an included or multivar key */ @@ -338,7 +323,7 @@ static void free_diskfile_entry(git_config_entry *entry) */ static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out) { - diskfile_header *h = GIT_CONTAINER_OF(cfg, diskfile_header, parent); + config_file_backend *h = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry; int error = 0; @@ -364,7 +349,7 @@ static int config_get(git_config_backend *cfg, const char *key, git_config_entry static int config_set_multivar( git_config_backend *cfg, const char *name, const char *regexp, const char *value) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); char *key; p_regex_t preg; int result; @@ -394,7 +379,7 @@ out: static int config_delete(git_config_backend *cfg, const char *name) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry; char *key = NULL; @@ -403,7 +388,7 @@ static int config_delete(git_config_backend *cfg, const char *name) if ((error = git_config__normalize_name(name, &key)) < 0) goto out; - if ((entries = diskfile_entries_take(&b->header)) == NULL) + if ((entries = diskfile_entries_take(b)) == NULL) goto out; /* Check whether we'd be modifying an included or multivar key */ @@ -424,7 +409,7 @@ out: static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp) { - diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent); + config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry = NULL; p_regex_t preg = { 0 }; @@ -434,7 +419,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con if ((result = git_config__normalize_name(name, &key)) < 0) goto out; - if ((entries = diskfile_entries_take(&b->header)) == NULL) { + if ((entries = diskfile_entries_take(b)) == NULL) { result = -1; goto out; } @@ -463,7 +448,7 @@ out: static int config_lock(git_config_backend *_cfg) { - diskfile_backend *cfg = GIT_CONTAINER_OF(_cfg, diskfile_backend, header.parent); + config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent); int error; if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0) @@ -482,7 +467,7 @@ static int config_lock(git_config_backend *_cfg) static int config_unlock(git_config_backend *_cfg, int success) { - diskfile_backend *cfg = GIT_CONTAINER_OF(_cfg, diskfile_backend, header.parent); + config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent); int error = 0; if (success) { @@ -499,151 +484,35 @@ static int config_unlock(git_config_backend *_cfg, int success) int git_config_backend_from_file(git_config_backend **out, const char *path) { - diskfile_backend *backend; + config_file_backend *backend; - backend = git__calloc(1, sizeof(diskfile_backend)); + backend = git__calloc(1, sizeof(config_file_backend)); GIT_ERROR_CHECK_ALLOC(backend); - backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; - git_mutex_init(&backend->header.values_mutex); + backend->parent.version = GIT_CONFIG_BACKEND_VERSION; + git_mutex_init(&backend->values_mutex); backend->file.path = git__strdup(path); GIT_ERROR_CHECK_ALLOC(backend->file.path); git_array_init(backend->file.includes); - backend->header.parent.open = config_open; - backend->header.parent.get = config_get; - backend->header.parent.set = config_set; - backend->header.parent.set_multivar = config_set_multivar; - backend->header.parent.del = config_delete; - backend->header.parent.del_multivar = config_delete_multivar; - backend->header.parent.iterator = config_iterator_new; - backend->header.parent.snapshot = config_snapshot; - backend->header.parent.lock = config_lock; - backend->header.parent.unlock = config_unlock; - backend->header.parent.free = backend_free; + backend->parent.open = config_open; + backend->parent.get = config_get; + backend->parent.set = config_set; + backend->parent.set_multivar = config_set_multivar; + backend->parent.del = config_delete; + backend->parent.del_multivar = config_delete_multivar; + backend->parent.iterator = config_iterator_new; + backend->parent.snapshot = git_config_backend_snapshot; + backend->parent.lock = config_lock; + backend->parent.unlock = config_unlock; + backend->parent.free = backend_free; *out = (git_config_backend *)backend; return 0; } -static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value) -{ - GIT_UNUSED(cfg); - GIT_UNUSED(name); - GIT_UNUSED(value); - - return config_error_readonly(); -} - -static int config_set_multivar_readonly( - git_config_backend *cfg, const char *name, const char *regexp, const char *value) -{ - GIT_UNUSED(cfg); - GIT_UNUSED(name); - GIT_UNUSED(regexp); - GIT_UNUSED(value); - - return config_error_readonly(); -} - -static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp) -{ - GIT_UNUSED(cfg); - GIT_UNUSED(name); - GIT_UNUSED(regexp); - - return config_error_readonly(); -} - -static int config_delete_readonly(git_config_backend *cfg, const char *name) -{ - GIT_UNUSED(cfg); - GIT_UNUSED(name); - - return config_error_readonly(); -} - -static int config_lock_readonly(git_config_backend *_cfg) -{ - GIT_UNUSED(_cfg); - - return config_error_readonly(); -} - -static int config_unlock_readonly(git_config_backend *_cfg, int success) -{ - GIT_UNUSED(_cfg); - GIT_UNUSED(success); - - return config_error_readonly(); -} - -static void backend_readonly_free(git_config_backend *_backend) -{ - diskfile_backend *backend = GIT_CONTAINER_OF(_backend, diskfile_backend, header.parent); - - if (backend == NULL) - return; - - git_config_entries_free(backend->header.entries); - git_mutex_free(&backend->header.values_mutex); - git__free(backend); -} - -static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo) -{ - diskfile_readonly_backend *b = GIT_CONTAINER_OF(cfg, diskfile_readonly_backend, header.parent); - diskfile_backend *src = b->snapshot_from; - diskfile_header *src_header = &src->header; - git_config_entries *entries; - int error; - - if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0) - return error; - - /* We're just copying data, don't care about the level or repo*/ - GIT_UNUSED(level); - GIT_UNUSED(repo); - - if ((entries = diskfile_entries_take(src_header)) == NULL) - return -1; - b->header.entries = entries; - - return 0; -} - -static int config_snapshot(git_config_backend **out, git_config_backend *in) -{ - diskfile_readonly_backend *backend; - - backend = git__calloc(1, sizeof(diskfile_readonly_backend)); - GIT_ERROR_CHECK_ALLOC(backend); - - backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; - git_mutex_init(&backend->header.values_mutex); - - backend->snapshot_from = GIT_CONTAINER_OF(in, diskfile_backend, header.parent); - - backend->header.parent.readonly = 1; - backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; - backend->header.parent.open = config_readonly_open; - backend->header.parent.get = config_get; - backend->header.parent.set = config_set_readonly; - backend->header.parent.set_multivar = config_set_multivar_readonly; - backend->header.parent.del = config_delete_readonly; - backend->header.parent.del_multivar = config_delete_multivar_readonly; - backend->header.parent.iterator = config_iterator_new; - backend->header.parent.lock = config_lock_readonly; - backend->header.parent.unlock = config_unlock_readonly; - backend->header.parent.free = backend_readonly_free; - - *out = &backend->header.parent; - - return 0; -} - static int included_path(git_buf *out, const char *dir, const char *path) { /* From the user's home */ @@ -685,9 +554,9 @@ static char *escape_value(const char *ptr) return git_buf_detach(&buf); } -static int parse_include(diskfile_parse_state *parse_data, const char *file) +static int parse_include(config_file_parse_data *parse_data, const char *file) { - diskfile *include; + config_file *include; git_buf path = GIT_BUF_INIT; char *dir; int result; @@ -790,7 +659,7 @@ static const struct { { "gitdir/i:", conditional_match_gitdir_i } }; -static int parse_conditional_include(diskfile_parse_state *parse_data, const char *section, const char *file) +static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file) { char *condition; size_t i; @@ -831,7 +700,7 @@ static int read_on_variable( size_t line_len, void *data) { - diskfile_parse_state *parse_data = (diskfile_parse_state *)data; + config_file_parse_data *parse_data = (config_file_parse_data *)data; git_buf buf = GIT_BUF_INIT; git_config_entry *entry; const char *c; @@ -881,13 +750,13 @@ static int read_on_variable( static int config_read_buffer( git_config_entries *entries, const git_repository *repo, - diskfile *file, + config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen) { - diskfile_parse_state parse_data; + config_file_parse_data parse_data; git_config_parser reader; int error; @@ -921,7 +790,7 @@ out: static int config_read( git_config_entries *entries, const git_repository *repo, - diskfile *file, + config_file *file, git_config_level_t level, int depth) { @@ -1173,7 +1042,7 @@ static int write_on_eof( /* * This is pretty much the parsing, except we write out anything we don't have */ -static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char* value) +static int config_write(config_file_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char* value) { char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot; git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT; @@ -1233,7 +1102,7 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char if ((error = git_filebuf_commit(&file)) < 0) goto done; - if ((error = config_refresh_from_buffer(&cfg->header.parent, buf.ptr, buf.size)) < 0) + if ((error = config_refresh_from_buffer(&cfg->parent, buf.ptr, buf.size)) < 0) goto done; } diff --git a/src/config_snapshot.c b/src/config_snapshot.c new file mode 100644 index 000000000..764abe208 --- /dev/null +++ b/src/config_snapshot.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "config.h" + +#include "config_entries.h" + +typedef struct { + git_config_backend parent; + git_mutex values_mutex; + git_config_entries *entries; + git_config_backend *source; +} config_snapshot_backend; + +static int config_error_readonly(void) +{ + git_error_set(GIT_ERROR_CONFIG, "this backend is read-only"); + return -1; +} + +static int config_iterator_new_readonly( + git_config_iterator **iter, + struct git_config_backend *backend) +{ + config_snapshot_backend *b = GIT_CONTAINER_OF(backend, config_snapshot_backend, parent); + git_config_entries *entries = NULL; + int error; + + if ((error = git_config_entries_dup(&entries, b->entries)) < 0 || + (error = git_config_entries_iterator_new(iter, entries)) < 0) + goto out; + +out: + /* Let iterator delete duplicated entries when it's done */ + git_config_entries_free(entries); + return error; +} + +/* release the map containing the entry as an equivalent to freeing it */ +static void free_diskfile_entry(git_config_entry *entry) +{ + git_config_entries *entries = (git_config_entries *) entry->payload; + git_config_entries_free(entries); +} + +static int config_get_readonly(git_config_backend *cfg, const char *key, git_config_entry **out) +{ + config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent); + git_config_entries *entries = NULL; + git_config_entry *entry; + int error = 0; + + if (git_mutex_lock(&b->values_mutex) < 0) { + git_error_set(GIT_ERROR_OS, "failed to lock config backend"); + return -1; + } + + entries = b->entries; + git_config_entries_incref(entries); + git_mutex_unlock(&b->values_mutex); + + if ((error = (git_config_entries_get(&entry, entries, key))) < 0) { + git_config_entries_free(entries); + return error; + } + + entry->free = free_diskfile_entry; + entry->payload = entries; + *out = entry; + + return 0; +} + +static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + GIT_UNUSED(value); + + return config_error_readonly(); +} + +static int config_set_multivar_readonly( + git_config_backend *cfg, const char *name, const char *regexp, const char *value) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + GIT_UNUSED(regexp); + GIT_UNUSED(value); + + return config_error_readonly(); +} + +static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + GIT_UNUSED(regexp); + + return config_error_readonly(); +} + +static int config_delete_readonly(git_config_backend *cfg, const char *name) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + + return config_error_readonly(); +} + +static int config_lock_readonly(git_config_backend *_cfg) +{ + GIT_UNUSED(_cfg); + + return config_error_readonly(); +} + +static int config_unlock_readonly(git_config_backend *_cfg, int success) +{ + GIT_UNUSED(_cfg); + GIT_UNUSED(success); + + return config_error_readonly(); +} + +static void backend_readonly_free(git_config_backend *_backend) +{ + config_snapshot_backend *backend = GIT_CONTAINER_OF(_backend, config_snapshot_backend, parent); + + if (backend == NULL) + return; + + git_config_entries_free(backend->entries); + git_mutex_free(&backend->values_mutex); + git__free(backend); +} + +static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo) +{ + config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent); + git_config_entries *entries = NULL; + git_config_iterator *it = NULL; + git_config_entry *entry; + int error; + + /* We're just copying data, don't care about the level or repo*/ + GIT_UNUSED(level); + GIT_UNUSED(repo); + + if ((error = git_config_entries_new(&entries)) < 0 || + (error = b->source->iterator(&it, b->source)) < 0) + goto out; + + while ((error = git_config_next(&entry, it)) == 0) + if ((error = git_config_entries_dup_entry(entries, entry)) < 0) + goto out; + + if (error < 0) { + if (error != GIT_ITEROVER) + goto out; + error = 0; + } + + b->entries = entries; + +out: + git_config_iterator_free(it); + if (error) + git_config_entries_free(entries); + return error; +} + +int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source) +{ + config_snapshot_backend *backend; + + backend = git__calloc(1, sizeof(config_snapshot_backend)); + GIT_ERROR_CHECK_ALLOC(backend); + + backend->parent.version = GIT_CONFIG_BACKEND_VERSION; + git_mutex_init(&backend->values_mutex); + + backend->source = source; + + backend->parent.readonly = 1; + backend->parent.version = GIT_CONFIG_BACKEND_VERSION; + backend->parent.open = config_readonly_open; + backend->parent.get = config_get_readonly; + backend->parent.set = config_set_readonly; + backend->parent.set_multivar = config_set_multivar_readonly; + backend->parent.del = config_delete_readonly; + backend->parent.del_multivar = config_delete_multivar_readonly; + backend->parent.iterator = config_iterator_new_readonly; + backend->parent.lock = config_lock_readonly; + backend->parent.unlock = config_unlock_readonly; + backend->parent.free = backend_readonly_free; + + *out = &backend->parent; + + return 0; +} |