From 1c1e4af05262b57f5779e7f825e25945c8c21754 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= <pclouds@gmail.com>
Date: Sun, 21 Mar 2010 17:30:46 +0700
Subject: [PATCH] Guard unallowed access to repository when it's not set up
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Many code path will skip repo access if startup_info->have_repository
is false. This may be a fault if startup_info->have_repository has not
been properly initialized.

So the rule is one of the following commands must be run before any
repo access. And none of them can be called twice.

 - setup_git_directory*
 - enter_repo
 - init_db

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/init-db.c |    1 +
 cache.h           |    1 +
 config.c          |    2 ++
 environment.c     |   13 +++++++++++--
 git.c             |   16 +++++++++++-----
 setup.c           |   13 +++++++++++++
 6 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/builtin/init-db.c b/builtin/init-db.c
index 5b2113b..8242fb9 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -288,6 +288,7 @@ int init_db(const char *git_dir, const char *template_dir, unsigned int flags)
 
 	set_git_dir(make_absolute_path(git_dir));
 	startup_info->have_repository = 1;
+	startup_info->have_run_setup_gitdir = 1;
 
 	safe_create_dir(get_git_dir(), 0);
 
diff --git a/cache.h b/cache.h
index 1b55ad7..c118935 100644
--- a/cache.h
+++ b/cache.h
@@ -1046,6 +1046,7 @@ int split_cmdline(char *cmdline, const char ***argv);
 /* git.c */
 struct startup_info {
 	const char *prefix;
+	int have_run_setup_gitdir;
 	int have_repository;
 	int help;
 };
diff --git a/config.c b/config.c
index 07d854a..9981b09 100644
--- a/config.c
+++ b/config.c
@@ -737,6 +737,8 @@ int git_config(config_fn_t fn, void *data)
 	char *repo_config = NULL;
 	int ret;
 
+	if (startup_info && !startup_info->have_run_setup_gitdir)
+		die("internal error: access to .git/config without repo setup");
 	if (!startup_info || startup_info->have_repository)
 		repo_config = git_pathdup("config");
 	ret = git_config_early(fn, data, repo_config);
diff --git a/environment.c b/environment.c
index ead00a3..ca34a7b 100644
--- a/environment.c
+++ b/environment.c
@@ -81,9 +81,18 @@ void unset_git_env(void)
 
 static void setup_git_env(void)
 {
+	if (startup_info && startup_info->have_run_setup_gitdir)
+		die("internal error: setup_git_env can't be called twice");
 	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if (!git_dir)
-		git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	if (!git_dir) {
+		/*
+		 * Repo detection should be done by setup_git_directory*
+		 * or enter_repo, not by this function
+		 */
+		 if (startup_info)
+			 die("internal error: $GIT_DIR is empty");
+		 git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	}
 	if (!git_dir)
 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 	git_object_dir = getenv(DB_ENVIRONMENT);
diff --git a/git.c b/git.c
index deba49a..21272d8 100644
--- a/git.c
+++ b/git.c
@@ -242,11 +242,14 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 			int nongit_ok;
 			setup_git_directory_gently(&nongit_ok);
 		}
-		else if (startup_info->have_repository) {
-			if (p->option & (RUN_SETUP_GENTLY | RUN_SETUP))
-				; /* done already */
-			else
-				unset_git_directory(startup_info->prefix);
+		else if (startup_info->have_run_setup_gitdir) {
+			if (startup_info->have_repository) {
+				if (p->option & (RUN_SETUP_GENTLY | RUN_SETUP))
+					; /* done already */
+				else
+					unset_git_directory(startup_info->prefix);
+			}
+			startup_info->have_run_setup_gitdir = 0;
 		}
 
 		if (use_pager == -1 && p->option & RUN_SETUP)
@@ -257,6 +260,9 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 			use_pager = 1;
 		}
 	}
+	else
+		/* Stop git_config() from complaining that no repository found. */
+		startup_info->have_run_setup_gitdir = 1;
 	commit_pager_choice();
 
 	if (!startup_info->help && p->option & NEED_WORK_TREE)
diff --git a/setup.c b/setup.c
index 7938481..3bb3a92 100644
--- a/setup.c
+++ b/setup.c
@@ -236,7 +236,17 @@ void setup_work_tree(void)
 		git_dir = make_absolute_path(git_dir);
 	if (!work_tree || chdir(work_tree))
 		die("This operation must be run in a work tree");
+
+	/*
+	 * have_run_setup_gitdir is unset in order to avoid die()ing
+	 * inside set_git_env(). We don't actually initialize
+	 * repo twice, we're just relative-izing gitdir
+	 */
+	if (startup_info)
+		startup_info->have_run_setup_gitdir = 0;
 	set_git_dir(make_relative_path(git_dir, work_tree));
+	if (startup_info)
+		startup_info->have_run_setup_gitdir = 1;
 	initialized = 1;
 }
 
@@ -339,6 +349,7 @@ void unset_git_directory(const char *prefix)
 			unset_git_env();
 		startup_info->prefix = NULL;
 		startup_info->have_repository = 0;
+		startup_info->have_run_setup_gitdir = 0;
 	}
 
 	/* Initialized in setup_git_directory_gently_1() */
@@ -513,6 +524,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	prefix = setup_git_directory_gently_1(nongit_ok);
 	if (startup_info) {
 		startup_info->prefix = prefix;
+		startup_info->have_run_setup_gitdir = 1;
 		startup_info->have_repository = !nongit_ok || !*nongit_ok;
 	}
 	return prefix;
@@ -607,6 +619,7 @@ char *enter_repo(char *path, int strict)
 		set_git_dir(".");
 		if (startup_info) {
 			startup_info->prefix = NULL;
+			startup_info->have_run_setup_gitdir = 1;
 			startup_info->have_repository = 1;
 		}
 		return path;
-- 
1.7.0.rc1.541.g2da82.dirty

