Subject: [WIP PATCH 04/26] Add GIT_HARDENED_SETUP to detect gitdir/worktree related mis-setup errors

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Tue, 16 Feb 2010 23:04:55 +0700

When GIT_DIR environment variable is not specified, .git will be
searched if a repository is needed. Currently this can be done in two
places: setup_git_directory_gently() and setup_git_env().

The one in setup_git_env() is no longer correct and should IMHO have
been removed since the introduction of setup_git_directory_gently() in
d288a70. Having two ways of auto detection may lead to obscure errors
because .git may be misdetected by setup_git_env(),
automatically called via git_path(), which is all over the place.

This patch makes setup_git_env() die if GIT_DIR is not explictly
set. That's setup_git_directory_gently()'s job. If you ever want to
touch things inside $GIT_DIR, you should have already called
setup_git_directory_gently().

However, doing that will break Git the hard way. So the die()ing
behavior will be only triggered if environment variable
GIT_HARDENED_SETUP is set. Otherwise old behavior remains. Once all
Git commands have been adapted to stay away from the old behavior, the
old code can be removed.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 config.c      |   17 ++++++++++++-----
 environment.c |   42 ++++++++++++++++++++++++++++++++----------
 setup.c       |    4 +++-
 3 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/config.c b/config.c
index 6963fbe..eee12b4 100644
--- a/config.c
+++ b/config.c
@@ -704,6 +704,7 @@ int git_config(config_fn_t fn, void *data)
 	int ret = 0, found = 0;
 	char *repo_config = NULL;
 	const char *home = NULL;
+	const char *hardened_setup = getenv("GIT_HARDENED_SETUP");
 
 	/* Setting $GIT_CONFIG makes git read _only_ the given config file. */
 	if (config_exclusive_filename)
@@ -724,12 +725,18 @@ int git_config(config_fn_t fn, void *data)
 		free(user_config);
 	}
 
-	repo_config = git_pathdup("config");
-	if (!access(repo_config, R_OK)) {
-		ret += git_config_from_file(fn, repo_config, data);
-		found += 1;
+	if (hardened_setup && !*hardened_setup)
+		hardened_setup = NULL;
+	if (hardened_setup && startup_info && !startup_info->have_set_gitdir)
+		die("Try to access a repository before properly setting it up");
+	if (!hardened_setup || !startup_info || startup_info->have_repository) {
+		repo_config = git_pathdup("config");
+		if (!access(repo_config, R_OK)) {
+			ret += git_config_from_file(fn, repo_config, data);
+			found += 1;
+		}
+		free(repo_config);
 	}
-	free(repo_config);
 	if (found == 0)
 		return -1;
 	return ret;
diff --git a/environment.c b/environment.c
index 1ab8815..4795441 100644
--- a/environment.c
+++ b/environment.c
@@ -66,9 +66,19 @@ static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 
 static void setup_git_env(void)
 {
+	const char *harden_setup = getenv("GIT_HARDENED_SETUP");
+
+	if (harden_setup && !*harden_setup)
+		harden_setup = NULL;
+
 	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if (!git_dir)
-		git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	if (!git_dir) {
+		if (harden_setup)
+			die("GIT_DIR not properly set");
+		 git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	}
+	if (harden_setup && startup_info && startup_info->have_set_gitdir)
+			die("internal error: setup_git_env can't be called twice");
 	if (!git_dir)
 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 	git_object_dir = getenv(DB_ENVIRONMENT);
@@ -103,8 +113,11 @@ int have_git_dir(void)
 
 const char *get_git_dir(void)
 {
-	if (!git_dir)
-		setup_git_env();
+	if (!git_dir) {
+		const char *harden_setup = getenv("GIT_HARDENED_SETUP");
+		if (!harden_setup || !*harden_setup)
+			setup_git_env();
+	}
 	return git_dir;
 }
 
@@ -146,22 +159,31 @@ const char *get_git_work_tree(void)
 
 char *get_object_directory(void)
 {
-	if (!git_object_dir)
-		setup_git_env();
+	if (!git_object_dir) {
+		const char *harden_setup = getenv("GIT_HARDENED_SETUP");
+		if (!harden_setup || !*harden_setup)
+			setup_git_env();
+	}
 	return git_object_dir;
 }
 
 char *get_index_file(void)
 {
-	if (!git_index_file)
-		setup_git_env();
+	if (!git_index_file) {
+		const char *harden_setup = getenv("GIT_HARDENED_SETUP");
+		if (!harden_setup || !*harden_setup)
+			setup_git_env();
+	}
 	return git_index_file;
 }
 
 char *get_graft_file(void)
 {
-	if (!git_graft_file)
-		setup_git_env();
+	if (!git_graft_file) {
+		const char *harden_setup = getenv("GIT_HARDENED_SETUP");
+		if (!harden_setup || !*harden_setup)
+			setup_git_env();
+	}
 	return git_graft_file;
 }
 
diff --git a/setup.c b/setup.c
index 0c05d36..5c8777a 100644
--- a/setup.c
+++ b/setup.c
@@ -453,8 +453,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	const char *prefix;
 
 	prefix = setup_git_directory_gently_1(nongit_ok);
-	if (startup_info)
+	if (startup_info) {
 		startup_info->have_set_gitdir = 1;
+		startup_info->have_repository = get_git_dir() != NULL;
+	}
 	if ((!nongit_ok || !*nongit_ok) && check_repository_format_gently(nongit_ok))
 		prefix = NULL;
 	if (startup_info) {
-- 
1.7.0.195.g637a2