summaryrefslogtreecommitdiff
path: root/setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'setup.c')
-rw-r--r--setup.c161
1 files changed, 122 insertions, 39 deletions
diff --git a/setup.c b/setup.c
index d458edcc02..29f8673921 100644
--- a/setup.c
+++ b/setup.c
@@ -1215,7 +1215,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
}
struct safe_directory_data {
- const char *path;
+ char *path;
int is_safe;
};
@@ -1235,17 +1235,45 @@ static int safe_directory_cb(const char *key, const char *value,
char *allowed = NULL;
if (!git_config_pathname(&allowed, key, value)) {
- const char *check = allowed ? allowed : value;
- if (ends_with(check, "/*")) {
- size_t len = strlen(check);
- if (!fspathncmp(check, data->path, len - 1))
+ char *normalized = NULL;
+
+ /*
+ * Setting safe.directory to a non-absolute path
+ * makes little sense---it won't be relative to
+ * the configuration file the item is defined in.
+ * Except for ".", which means "if we are at the top
+ * level of a repository, then it is OK", which is
+ * slightly tighter than "*" that allows discovery.
+ */
+ if (!is_absolute_path(allowed) && strcmp(allowed, ".")) {
+ warning(_("safe.directory '%s' not absolute"),
+ allowed);
+ goto next;
+ }
+
+ /*
+ * A .gitconfig in $HOME may be shared across
+ * different machines and safe.directory entries
+ * may or may not exist as paths on all of these
+ * machines. In other words, it is not a warning
+ * worthy event when there is no such path on this
+ * machine---the entry may be useful elsewhere.
+ */
+ normalized = real_pathdup(allowed, 0);
+ if (!normalized)
+ goto next;
+
+ if (ends_with(normalized, "/*")) {
+ size_t len = strlen(normalized);
+ if (!fspathncmp(normalized, data->path, len - 1))
data->is_safe = 1;
- } else if (!fspathcmp(data->path, check)) {
+ } else if (!fspathcmp(data->path, normalized)) {
data->is_safe = 1;
}
- }
- if (allowed != value)
+ next:
+ free(normalized);
free(allowed);
+ }
}
return 0;
@@ -1263,9 +1291,7 @@ static int ensure_valid_ownership(const char *gitfile,
const char *worktree, const char *gitdir,
struct strbuf *report)
{
- struct safe_directory_data data = {
- .path = worktree ? worktree : gitdir
- };
+ struct safe_directory_data data = { 0 };
if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
(!gitfile || is_path_owned_by_current_user(gitfile, report)) &&
@@ -1274,12 +1300,22 @@ static int ensure_valid_ownership(const char *gitfile,
return 1;
/*
+ * normalize the data.path for comparison with normalized paths
+ * that come from the configuration file. The path is unsafe
+ * if it cannot be normalized.
+ */
+ data.path = real_pathdup(worktree ? worktree : gitdir, 0);
+ if (!data.path)
+ return 0;
+
+ /*
* data.path is the "path" that identifies the repository and it is
* constant regardless of what failed above. data.is_safe should be
* initialized to false, and might be changed by the callback.
*/
git_protected_config(safe_directory_cb, &data);
+ free(data.path);
return data.is_safe;
}
@@ -1871,7 +1907,7 @@ struct template_dir_cb_data {
};
static int template_dir_cb(const char *key, const char *value,
- const struct config_context *ctx, void *d)
+ const struct config_context *ctx UNUSED, void *d)
{
struct template_dir_cb_data *data = d;
@@ -2284,14 +2320,67 @@ static void separate_git_dir(const char *git_dir, const char *git_link)
write_file(git_link, "gitdir: %s", git_dir);
}
-static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
+struct default_format_config {
+ int hash;
+ enum ref_storage_format ref_format;
+};
+
+static int read_default_format_config(const char *key, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *payload)
+{
+ struct default_format_config *cfg = payload;
+ char *str = NULL;
+ int ret;
+
+ if (!strcmp(key, "init.defaultobjectformat")) {
+ ret = git_config_string(&str, key, value);
+ if (ret)
+ goto out;
+ cfg->hash = hash_algo_by_name(str);
+ if (cfg->hash == GIT_HASH_UNKNOWN)
+ warning(_("unknown hash algorithm '%s'"), str);
+ goto out;
+ }
+
+ if (!strcmp(key, "init.defaultrefformat")) {
+ ret = git_config_string(&str, key, value);
+ if (ret)
+ goto out;
+ cfg->ref_format = ref_storage_format_by_name(str);
+ if (cfg->ref_format == REF_STORAGE_FORMAT_UNKNOWN)
+ warning(_("unknown ref storage format '%s'"), str);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ free(str);
+ return ret;
+}
+
+static void repository_format_configure(struct repository_format *repo_fmt,
+ int hash, enum ref_storage_format ref_format)
{
- const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
+ struct default_format_config cfg = {
+ .hash = GIT_HASH_UNKNOWN,
+ .ref_format = REF_STORAGE_FORMAT_UNKNOWN,
+ };
+ struct config_options opts = {
+ .respect_includes = 1,
+ .ignore_repo = 1,
+ .ignore_worktree = 1,
+ };
+ const char *env;
+
+ config_with_options(read_default_format_config, &cfg, NULL, NULL, &opts);
+
/*
* If we already have an initialized repo, don't allow the user to
* specify a different algorithm, as that could cause corruption.
* Otherwise, if the user has specified one on the command line, use it.
*/
+ env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
die(_("attempt to reinitialize repository with different hash"));
else if (hash != GIT_HASH_UNKNOWN)
@@ -2301,26 +2390,27 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
if (env_algo == GIT_HASH_UNKNOWN)
die(_("unknown hash algorithm '%s'"), env);
repo_fmt->hash_algo = env_algo;
+ } else if (cfg.hash != GIT_HASH_UNKNOWN) {
+ repo_fmt->hash_algo = cfg.hash;
}
-}
-
-static void validate_ref_storage_format(struct repository_format *repo_fmt,
- enum ref_storage_format format)
-{
- const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
+ repo_set_hash_algo(the_repository, repo_fmt->hash_algo);
+ env = getenv("GIT_DEFAULT_REF_FORMAT");
if (repo_fmt->version >= 0 &&
- format != REF_STORAGE_FORMAT_UNKNOWN &&
- format != repo_fmt->ref_storage_format) {
+ ref_format != REF_STORAGE_FORMAT_UNKNOWN &&
+ ref_format != repo_fmt->ref_storage_format) {
die(_("attempt to reinitialize repository with different reference storage format"));
- } else if (format != REF_STORAGE_FORMAT_UNKNOWN) {
- repo_fmt->ref_storage_format = format;
- } else if (name) {
- format = ref_storage_format_by_name(name);
- if (format == REF_STORAGE_FORMAT_UNKNOWN)
- die(_("unknown ref storage format '%s'"), name);
- repo_fmt->ref_storage_format = format;
+ } else if (ref_format != REF_STORAGE_FORMAT_UNKNOWN) {
+ repo_fmt->ref_storage_format = ref_format;
+ } else if (env) {
+ ref_format = ref_storage_format_by_name(env);
+ if (ref_format == REF_STORAGE_FORMAT_UNKNOWN)
+ die(_("unknown ref storage format '%s'"), env);
+ repo_fmt->ref_storage_format = ref_format;
+ } else if (cfg.ref_format != REF_STORAGE_FORMAT_UNKNOWN) {
+ repo_fmt->ref_storage_format = cfg.ref_format;
}
+ repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format);
}
int init_db(const char *git_dir, const char *real_git_dir,
@@ -2353,22 +2443,15 @@ int init_db(const char *git_dir, const char *real_git_dir,
}
startup_info->have_repository = 1;
- /* Check to see if the repository version is right.
+ /*
+ * Check to see if the repository version is right.
* Note that a newly created repository does not have
* config file, so this will not fail. What we are catching
* is an attempt to reinitialize new repository with an old tool.
*/
check_repository_format(&repo_fmt);
- validate_hash_algorithm(&repo_fmt, hash);
- validate_ref_storage_format(&repo_fmt, ref_storage_format);
-
- /*
- * Now that we have set up both the hash algorithm and the ref storage
- * format we can update the repository's settings accordingly.
- */
- repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
- repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
+ repository_format_configure(&repo_fmt, hash, ref_storage_format);
/*
* Ensure `core.hidedotfiles` is processed. This must happen after we