Skip to content

Commit cecd41f

Browse files
authored
Merge pull request libgit2#4217 from pks-t/pks/readonly-cfg-backend
Honor read-only flag when writing to config backends
2 parents 7f75eea + 2a7086f commit cecd41f

File tree

3 files changed

+106
-22
lines changed

3 files changed

+106
-22
lines changed

src/config.c

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -576,22 +576,50 @@ int git_config_foreach_match(
576576
* Setters
577577
**************/
578578

579-
static int config_error_nofiles(const char *name)
579+
typedef enum {
580+
BACKEND_USE_SET,
581+
BACKEND_USE_DELETE
582+
} backend_use;
583+
584+
static const char *uses[] = {
585+
"set",
586+
"delete"
587+
};
588+
589+
static int get_backend_for_use(git_config_backend **out,
590+
git_config *cfg, const char *name, backend_use use)
580591
{
592+
size_t i;
593+
file_internal *f;
594+
595+
*out = NULL;
596+
597+
if (git_vector_length(&cfg->files) == 0) {
598+
giterr_set(GITERR_CONFIG,
599+
"cannot %s value for '%s' when no config files exist",
600+
uses[use], name);
601+
return GIT_ENOTFOUND;
602+
}
603+
604+
git_vector_foreach(&cfg->files, i, f) {
605+
if (!f->file->readonly) {
606+
*out = f->file;
607+
return 0;
608+
}
609+
}
610+
581611
giterr_set(GITERR_CONFIG,
582-
"cannot set value for '%s' when no config files exist", name);
612+
"cannot %s value for '%s' when all config files are readonly",
613+
uses[use], name);
583614
return GIT_ENOTFOUND;
584615
}
585616

586617
int git_config_delete_entry(git_config *cfg, const char *name)
587618
{
588619
git_config_backend *file;
589-
file_internal *internal;
590620

591-
internal = git_vector_get(&cfg->files, 0);
592-
if (!internal || !internal->file)
593-
return config_error_nofiles(name);
594-
file = internal->file;
621+
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
622+
return GIT_ENOTFOUND;
595623

596624
return file->del(file, name);
597625
}
@@ -617,17 +645,14 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
617645
{
618646
int error;
619647
git_config_backend *file;
620-
file_internal *internal;
621648

622649
if (!value) {
623650
giterr_set(GITERR_CONFIG, "the value to set cannot be NULL");
624651
return -1;
625652
}
626653

627-
internal = git_vector_get(&cfg->files, 0);
628-
if (!internal || !internal->file)
629-
return config_error_nofiles(name);
630-
file = internal->file;
654+
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_SET) < 0)
655+
return GIT_ENOTFOUND;
631656

632657
error = file->set(file, name, value);
633658

@@ -1032,25 +1057,19 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config
10321057
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
10331058
{
10341059
git_config_backend *file;
1035-
file_internal *internal;
10361060

1037-
internal = git_vector_get(&cfg->files, 0);
1038-
if (!internal || !internal->file)
1039-
return config_error_nofiles(name);
1040-
file = internal->file;
1061+
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
1062+
return GIT_ENOTFOUND;
10411063

10421064
return file->set_multivar(file, name, regexp, value);
10431065
}
10441066

10451067
int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
10461068
{
10471069
git_config_backend *file;
1048-
file_internal *internal;
10491070

1050-
internal = git_vector_get(&cfg->files, 0);
1051-
if (!internal || !internal->file)
1052-
return config_error_nofiles(name);
1053-
file = internal->file;
1071+
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
1072+
return GIT_ENOTFOUND;
10541073

10551074
return file->del_multivar(file, name, regexp);
10561075
}

src/config_file.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#ifndef INCLUDE_config_file_h__
88
#define INCLUDE_config_file_h__
99

10+
#include "git2/sys/config.h"
1011
#include "git2/config.h"
1112

1213
GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)

tests/config/readonly.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include "clar_libgit2.h"
2+
#include "config_file.h"
3+
#include "config.h"
4+
5+
static git_config *cfg;
6+
7+
void test_config_readonly__initialize(void)
8+
{
9+
cl_git_pass(git_config_new(&cfg));
10+
}
11+
12+
void test_config_readonly__cleanup(void)
13+
{
14+
git_config_free(cfg);
15+
cfg = NULL;
16+
}
17+
18+
void test_config_readonly__writing_to_readonly_fails(void)
19+
{
20+
git_config_backend *backend;
21+
22+
cl_git_pass(git_config_file__ondisk(&backend, "global"));
23+
backend->readonly = 1;
24+
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
25+
26+
cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
27+
cl_assert(!git_path_exists("global"));
28+
}
29+
30+
void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
31+
{
32+
git_config_backend *backend;
33+
34+
cl_git_pass(git_config_file__ondisk(&backend, "global"));
35+
backend->readonly = 1;
36+
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
37+
38+
cl_git_pass(git_config_file__ondisk(&backend, "local"));
39+
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
40+
41+
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
42+
43+
cl_assert(git_path_exists("local"));
44+
cl_assert(!git_path_exists("global"));
45+
cl_git_pass(p_unlink("local"));
46+
}
47+
48+
void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
49+
{
50+
git_config_backend *backend;
51+
52+
cl_git_pass(git_config_file__ondisk(&backend, "local"));
53+
backend->readonly = 1;
54+
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
55+
56+
cl_git_pass(git_config_file__ondisk(&backend, "global"));
57+
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
58+
59+
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
60+
61+
cl_assert(!git_path_exists("local"));
62+
cl_assert(git_path_exists("global"));
63+
cl_git_pass(p_unlink("global"));
64+
}

0 commit comments

Comments
 (0)