config: add a notion of "scope"
authorJeff King <peff@peff.net>
Wed, 18 May 2016 22:44:23 +0000 (18:44 -0400)
committerJunio C Hamano <gitster@pobox.com>
Fri, 27 May 2016 17:45:40 +0000 (10:45 -0700)
A config callback passed to git_config() doesn't know very
much about the context in which it sees a variable. It can
ask whether the variable comes from a file, and get the file
name. But without analyzing the filename (which is hard to
do accurately), it cannot tell whether it is in system-level
config, user-level config, or repo-specific config.

Generally this doesn't matter; the point of not passing this
to the callback is that it should treat the config the same
no matter where it comes from. But some programs, like
upload-pack, are a special case: we should be able to run
them in an untrusted repository, which means we cannot use
any "dangerous" config from the repository config file (but
it is OK to use it from system or user config).

This patch teaches the config code to record the "scope" of
each variable, and make it available inside config
callbacks, similar to how we give access to the filename.
The scope is the starting source for a particular parsing
operation, and remains the same even if we include other
files (so a .git/config which includes another file will
remain CONFIG_SCOPE_REPO, as it would be similarly
untrusted).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
cache.h
config.c
t/helper/test-config.c
t/t1308-config-set.sh
diff --git a/cache.h b/cache.h
index 1bce212e88bccaa65b093ccb42aeaeaebcac4ebe..2e3b377ae8ac4b68e740de28c9e339965aa685e4 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1604,6 +1604,16 @@ extern const char *get_log_output_encoding(void);
 extern const char *get_commit_output_encoding(void);
 
 extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+
+enum config_scope {
+       CONFIG_SCOPE_UNKNOWN = 0,
+       CONFIG_SCOPE_SYSTEM,
+       CONFIG_SCOPE_GLOBAL,
+       CONFIG_SCOPE_REPO,
+       CONFIG_SCOPE_CMDLINE,
+};
+
+extern enum config_scope current_config_scope(void);
 extern const char *current_config_origin_type(void);
 extern const char *current_config_name(void);
 
@@ -1697,6 +1707,7 @@ struct key_value_info {
        const char *filename;
        int linenr;
        const char *origin_type;
+       enum config_scope scope;
 };
 
 extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
index d555deeb9a976bbb1640513d4c81e7aacc9cc785..87629164b3623885fa0428790a6e7fa2eb643215 100644 (file)
--- a/config.c
+++ b/config.c
@@ -57,6 +57,15 @@ struct config_source {
 static struct config_source *cf;
 static struct key_value_info *current_config_kvi;
 
+/*
+ * Similar to the variables above, this gives access to the "scope" of the
+ * current value (repo, global, etc). For cached values, it can be found via
+ * the current_config_kvi as above. During parsing, the current value can be
+ * found in this variable. It's not part of "cf" because it transcends a single
+ * file (i.e., a file included from .git/config is still in "repo" scope).
+ */
+static enum config_scope current_parsing_scope;
+
 static int zlib_compression_seen;
 
 /*
@@ -1229,22 +1238,27 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
        char *user_config = expand_user_path("~/.gitconfig");
        char *repo_config = git_pathdup("config");
 
+       current_parsing_scope = CONFIG_SCOPE_SYSTEM;
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
 
+       current_parsing_scope = CONFIG_SCOPE_GLOBAL;
        if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, xdg_config, data);
 
        if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, user_config, data);
 
+       current_parsing_scope = CONFIG_SCOPE_REPO;
        if (repo_config && !access_or_die(repo_config, R_OK, 0))
                ret += git_config_from_file(fn, repo_config, data);
 
+       current_parsing_scope = CONFIG_SCOPE_CMDLINE;
        if (git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
 
+       current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
        free(xdg_config);
        free(user_config);
        free(repo_config);
@@ -1383,6 +1397,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
                kv_info->linenr = -1;
                kv_info->origin_type = NULL;
        }
+       kv_info->scope = current_parsing_scope;
        si->util = kv_info;
 
        return 0;
@@ -2482,3 +2497,11 @@ const char *current_config_name(void)
                die("BUG: current_config_name called outside config callback");
        return name ? name : "";
 }
+
+enum config_scope current_config_scope(void)
+{
+       if (current_config_kvi)
+               return current_config_kvi->scope;
+       else
+               return current_parsing_scope;
+}
index 3605ef8ee69713b030eaffb28ded235ce7ad1b75..509aeef400e6495ce7ca428d7df5cd63f3aa1247 100644 (file)
  *
  */
 
+static const char *scope_name(enum config_scope scope)
+{
+       switch (scope) {
+       case CONFIG_SCOPE_SYSTEM:
+               return "system";
+       case CONFIG_SCOPE_GLOBAL:
+               return "global";
+       case CONFIG_SCOPE_REPO:
+               return "repo";
+       case CONFIG_SCOPE_CMDLINE:
+               return "cmdline";
+       default:
+               return "unknown";
+       }
+}
 static int iterate_cb(const char *var, const char *value, void *data)
 {
        static int nr;
@@ -46,6 +61,7 @@ static int iterate_cb(const char *var, const char *value, void *data)
        printf("value=%s\n", value ? value : "(null)");
        printf("origin=%s\n", current_config_origin_type());
        printf("name=%s\n", current_config_name());
+       printf("scope=%s\n", scope_name(current_config_scope()));
 
        return 0;
 }
index d345a885a35544241550d79c15be7ef098613204..065d5ebb09fa0d3dbc5d2abc3be0cb815cca17f6 100755 (executable)
@@ -238,16 +238,19 @@ test_expect_success 'iteration shows correct origins' '
        value=from-home
        origin=file
        name=$(pwd)/.gitconfig
+       scope=global
 
        key=foo.bar
        value=from-repo
        origin=file
        name=.git/config
+       scope=repo
 
        key=foo.bar
        value=from-cmdline
        origin=command line
        name=
+       scope=cmdline
        EOF
        GIT_CONFIG_PARAMETERS=$cmdline_config test-config iterate >actual &&
        test_cmp expect actual