@@ -1055,14 +1055,32 @@ static int safe_directory_cb(const char *key, const char *value, void *d)
10551055 return 0 ;
10561056}
10571057
1058- static int ensure_valid_ownership (const char * path )
1058+ /*
1059+ * Check if a repository is safe, by verifying the ownership of the
1060+ * worktree (if any), the git directory, and the gitfile (if any).
1061+ *
1062+ * Exemptions for known-safe repositories can be added via `safe.directory`
1063+ * config settings; for non-bare repositories, their worktree needs to be
1064+ * added, for bare ones their git directory.
1065+ */
1066+ static int ensure_valid_ownership (const char * gitfile ,
1067+ const char * worktree , const char * gitdir )
10591068{
1060- struct safe_directory_data data = { .path = path };
1069+ struct safe_directory_data data = {
1070+ .path = worktree ? worktree : gitdir
1071+ };
10611072
10621073 if (!git_env_bool ("GIT_TEST_ASSUME_DIFFERENT_OWNER" , 0 ) &&
1063- is_path_owned_by_current_user (path ))
1074+ (!gitfile || is_path_owned_by_current_user (gitfile )) &&
1075+ (!worktree || is_path_owned_by_current_user (worktree )) &&
1076+ (!gitdir || is_path_owned_by_current_user (gitdir )))
10641077 return 1 ;
10651078
1079+ /*
1080+ * data.path is the "path" that identifies the repository and it is
1081+ * constant regardless of what failed above. data.is_safe should be
1082+ * initialized to false, and might be changed by the callback.
1083+ */
10661084 read_very_early_config (safe_directory_cb , & data );
10671085
10681086 return data .is_safe ;
@@ -1150,6 +1168,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11501168 current_device = get_device_or_die (dir -> buf , NULL , 0 );
11511169 for (;;) {
11521170 int offset = dir -> len , error_code = 0 ;
1171+ char * gitdir_path = NULL ;
1172+ char * gitfile = NULL ;
11531173
11541174 if (offset > min_offset )
11551175 strbuf_addch (dir , '/' );
@@ -1160,21 +1180,50 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11601180 if (die_on_error ||
11611181 error_code == READ_GITFILE_ERR_NOT_A_FILE ) {
11621182 /* NEEDSWORK: fail if .git is not file nor dir */
1163- if (is_git_directory (dir -> buf ))
1183+ if (is_git_directory (dir -> buf )) {
11641184 gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT ;
1185+ gitdir_path = xstrdup (dir -> buf );
1186+ }
11651187 } else if (error_code != READ_GITFILE_ERR_STAT_FAILED )
11661188 return GIT_DIR_INVALID_GITFILE ;
1167- }
1189+ } else
1190+ gitfile = xstrdup (dir -> buf );
1191+ /*
1192+ * Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
1193+ * to check that directory for a repository.
1194+ * Now trim that tentative addition away, because we want to
1195+ * focus on the real directory we are in.
1196+ */
11681197 strbuf_setlen (dir , offset );
11691198 if (gitdirenv ) {
1170- if (!ensure_valid_ownership (dir -> buf ))
1171- return GIT_DIR_INVALID_OWNERSHIP ;
1172- strbuf_addstr (gitdir , gitdirenv );
1173- return GIT_DIR_DISCOVERED ;
1199+ enum discovery_result ret ;
1200+
1201+ if (ensure_valid_ownership (gitfile ,
1202+ dir -> buf ,
1203+ (gitdir_path ? gitdir_path : gitdirenv ))) {
1204+ strbuf_addstr (gitdir , gitdirenv );
1205+ ret = GIT_DIR_DISCOVERED ;
1206+ } else
1207+ ret = GIT_DIR_INVALID_OWNERSHIP ;
1208+
1209+ /*
1210+ * Earlier, during discovery, we might have allocated
1211+ * string copies for gitdir_path or gitfile so make
1212+ * sure we don't leak by freeing them now, before
1213+ * leaving the loop and function.
1214+ *
1215+ * Note: gitdirenv will be non-NULL whenever these are
1216+ * allocated, therefore we need not take care of releasing
1217+ * them outside of this conditional block.
1218+ */
1219+ free (gitdir_path );
1220+ free (gitfile );
1221+
1222+ return ret ;
11741223 }
11751224
11761225 if (is_git_directory (dir -> buf )) {
1177- if (!ensure_valid_ownership (dir -> buf ))
1226+ if (!ensure_valid_ownership (NULL , NULL , dir -> buf ))
11781227 return GIT_DIR_INVALID_OWNERSHIP ;
11791228 strbuf_addstr (gitdir , "." );
11801229 return GIT_DIR_BARE ;
@@ -1312,7 +1361,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
13121361 struct strbuf quoted = STRBUF_INIT ;
13131362
13141363 sq_quote_buf_pretty (& quoted , dir .buf );
1315- die (_ ("unsafe repository ( '%s' is owned by someone else) \n"
1364+ die (_ ("detected dubious ownership in repository at '%s'\n"
13161365 "To add an exception for this directory, call:\n"
13171366 "\n"
13181367 "\tgit config --global --add safe.directory %s" ),
0 commit comments